UNPKG

@enhance/styles

Version:
145 lines (123 loc) 4.77 kB
import hextohsl from './hex-to-hsl.mjs' export default function themeColor({ config }) { const { color = {}, theme = {} } = config if (theme === false) { return '' } const defaultAccent = '#0075db' const defaultError = '#d60606' const defaultLight = '#fefefe' const defaultDark = '#222222' const light = color.light || theme.back || defaultLight const dark = color.dark || theme.fore || defaultDark const lightParts = hextohsl(light) const darkTheme = theme?.dark || {} const lightAccent = theme?.accent || defaultAccent const lightAccentParts = hextohsl(lightAccent) const darkAccent = theme?.dark?.accent || theme?.accent || defaultAccent const darkAccentParts = hextohsl(darkAccent) // If no custom dark accent colour provided, modify default accent's lightness for dark mode if (darkAccent === lightAccent || darkAccent === defaultAccent) { darkAccentParts.l = 62 } const lightError = theme?.error || defaultError const lightErrorParts = hextohsl(lightError) const darkError = theme?.dark?.error || theme?.error || defaultError const darkErrorParts = hextohsl(darkError) // If no custom dark error colour provided, modify default error's lightness for dark mode if (darkError === lightError || darkError === defaultError) { darkErrorParts.l = 62 } const darkThemeColors = Object.keys(darkTheme).map(name => { return `--${name}: ${darkTheme[name]};` }).join('\n') const themeColors = Object.keys(theme).map(name => { if (name === 'accent' || name === 'error' || name === 'back' || name === 'fore' || (name === 'dark' && typeof theme[name] === 'object')) { return } else { return colorSteps(hextohsl(theme[name]), name) } }).join('\n') const colors = Object.keys(color).length ? 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 const saturation = color.s const luminance = color.l return ` --${name}-100: hsl(${hue}, ${saturation}%, ${Math.floor(luminance + 40)}%); --${name}-200: hsl(${hue}, ${saturation}%, ${Math.floor(luminance + 30)}%); --${name}-300: hsl(${hue}, ${saturation}%, ${Math.floor(luminance + 20)}%); --${name}-400: hsl(${hue}, ${saturation}%, ${Math.floor(luminance + 10)}%); --${name}-500: hsl(${hue}, ${saturation}%, ${luminance}%); --${name}-600: hsl(${hue}, ${saturation}%, ${Math.floor(luminance - 10)}%); --${name}-700: hsl(${hue}, ${saturation}%, ${Math.floor(luminance - 20)}%); --${name}-800: hsl(${hue}, ${saturation}%, ${Math.floor(luminance - 30)}%); --${name}-900: hsl(${hue}, ${saturation}%, ${Math.floor(luminance - 40)}%);` } const themeStyles = ` /*** Theme Colors ***/ :root { --accent-h: ${lightAccentParts.h}; --accent-s: ${lightAccentParts.s}%; --accent-l: ${lightAccentParts.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: ${lightErrorParts.h}; --error-s: ${lightErrorParts.s}%; --error-l: ${lightErrorParts.l}%; --error: hsl(var(--error-h), var(--error-s), var(--error-l)); ${themeColors} ${grayScale} --focus-l: 30%; accent-color: var(--accent, royalblue); color-scheme: light dark; } :is(a, button, input, textarea, summary):focus:not(:focus-visible) { outline: none; } :is(a, button, input, textarea, summary):focus-visible { 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), 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 { --accent-h: ${darkAccentParts.h}; --accent-s: ${darkAccentParts.s}%; --accent-l: ${darkAccentParts.l}%; --error-h: ${darkErrorParts.h}; --error-s: ${darkErrorParts.s}%; --error-l: ${darkErrorParts.l}%; --focus-l: -30%; --fore: var(--light); --back: var(--dark); ${darkThemeColors} } } `.replace(/^\s*\n/gm, '') // remove empty newlines const colorStyles = ` /*** Spot Colors ***/ :root { ${colors} } ` let result = `` if (theme !== false) result += themeStyles if (Object.keys(color).length) result += colorStyles return result }