diff --git a/config.json b/config.json index 9e3526d..ab8eff9 100644 --- a/config.json +++ b/config.json @@ -23,15 +23,14 @@ "mono": "Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace" }, "theme": { - "light": "#f8f9fa", - "dark": "#343a40", - "accent": "#007aff", - "primary": "#007aff" + "light": { + "accent": "#0075db", + "error": "#d60606", + "back": "#fefefe", + "fore": "#222222" + } }, "color": { - "error": "#ff3b2f", - "warn": "#ffcc02", - "success": "#35c759" }, "properties": { }, diff --git a/theme-color.mjs b/theme-color.mjs index 69872c2..4cd6fc8 100644 --- a/theme-color.mjs +++ b/theme-color.mjs @@ -2,14 +2,47 @@ import hextohsl from './hex-to-hsl.mjs' export default function themeColor({ config }) { const { color={}, theme={} } = config - const defaultLight = '#f8f9fa' - const defaultDark = '#343a40' - const lightParts = hextohsl(theme['light'] || defaultLight); - const defaultAccent = '#007aff' - const defaultAccentContrast = defaultDark - theme['accent'] = theme['accent'] || defaultAccent - theme['accent-contrast'] = theme['accent-contrast'] || defaultAccentContrast - + const defaultAccent = '#0075db' + const defaultError = '#d60606' + const defaultLight = '#fefefe' + const defaultDark = '#222222' + const light = color.light || defaultLight + const dark = color.dark || defaultDark + const lightParts = hextohsl(light); + const lightTheme = theme?.light || {} + const darkTheme = theme?.dark || {} + const lightAccent = lightTheme?.accent || defaultAccent + const lightError = lightTheme?.error || defaultError + const accentParts = hextohsl(lightAccent) + const errorParts = hextohsl(lightError) + const darkThemeColors = Object.keys(darkTheme).map(name => { + if ( name === 'accent' || + name === 'back' || + name === 'fore') { + return + } + return `--${name}: ${darkTheme[name]}`; + }).join('\n') + const lightThemeColors = Object.keys(lightTheme).map(name => { + if ( name === 'accent' || + name === 'error' || + name === 'back' || + name === 'fore') { + return + } + return `--${name}: ${lightTheme[name]}`; + }).join('\n') + const themeColors = Object.keys(theme).map(name => { + if ((name === 'light' && typeof theme[name] === 'object') || + (name === 'dark' && typeof theme[name] === 'object')) { + return + } + else { + return colorSteps(hextohsl(theme[name]), name) + } + }).join('\n') + const colors = Object.keys(color).map(name => ` --${name}: ${color[name]};`).join('\n') + const grayScale = colorSteps({ h: lightParts.h, s: 0, l: 50}, 'gray') function colorSteps(color, name) { const hue = color.h @@ -33,25 +66,24 @@ export default function themeColor({ config }) { return /*css*/` /*** Theme Colors ***/ :root { -${Object.keys(theme).map(name => { - if (name === 'light' || - name === 'dark' || - name === 'accent' || - name === 'accent-contrast') { - return ` --${name}: ${theme[name]};` -} -else { - return colorSteps(hextohsl(theme[name]), name) -} -}).join('\n')} - -${Object.keys(color).map(name => ` --${name}: ${color[name]};`).join('\n')} - --back: var(--light, ${defaultLight}); - --fore: var(--dark, ${defaultDark}); - ${colorSteps({ h: lightParts.h, s: 0, l: 50}, 'grey')} - --focus-size: 1px; - --focus-offset: 1px; - accent-color: var(--accent); + --accent-h: ${accentParts.h}; + --accent-s: ${accentParts.s}%; + --accent-l: ${accentParts.l}%; + --accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); + --light: ${light}; + --dark: ${dark}; + --fore: var(--dark, currentColor); + --back: var(--light); + --error-h: ${errorParts.h}; + --error-s: ${errorParts.s}%; + --error-l: ${errorParts.l}%; + --error: hsl(var(--error-h), var(--error-s), var(--error-l)); +${lightThemeColors} +${themeColors} +${colors} +${grayScale} + --focus-l: 30%; + accent-color: var(--accent, royalblue); color-scheme: light dark; } @@ -60,24 +92,25 @@ ${Object.keys(color).map(name => ` --${name}: ${color[name]};`).join('\n')} } :is(a, button, input, textarea, summary):focus-visible { - outline: max(var(--focus-size), 1px) solid var(--accent); - outline-offset: var(--focus-offset); -} - -:is(a, button, input, textarea, summary):not(:focus):not(:placeholder-shown):invalid { - outline: max(var(--focus-size), 1px) solid var(--error); - outline-offset: var(--focus-offset); + outline: max(var(--focus-size, 1px), 1px) solid var(--accent, royalblue); + outline-offset: var(--focus-offset, 0); + box-shadow: 0 0 0 max(var(--focus-size, 3px), 3px) hsl(var(--accent-h), var(--accent-s), calc(var(--accent-l) + var(--focus-l))) + ; } :is(a, button, input, textarea, summary):not(:focus):not(:placeholder-shown):invalid { - outline: max(var(--focus-size), 1px) solid var(--error); - outline-offset: var(--focus-offset); + outline: max(var(--focus-size, 1px), 1px) solid var(--error, crimson); + outline-offset: var(--focus-offset, 0); + box-shadow: 0 0 0 3px hsl(var(--error-h), var(--error-s), calc(var(--error-l) + var(--focus-l))); } @media (prefers-color-scheme: dark) { :root { - --back: var(--dark, ${defaultDark}); - --fore: var(--light, ${defaultLight}); + --accent-l: 62%; + --focus-l: -30%; + --fore: var(--light); + --back: var(--dark); + ${darkThemeColors} } } `