UNPKG

@poupe/theme-builder

Version:

Design token management and theme generation system for Poupe UI framework

953 lines (952 loc) 32.6 kB
import { camelCase, formatCSSRules, formatCSSRulesArray, generateCSSRules, generateCSSRulesArray, kebabCase, pairs, pairs as pairs$1, processCSSSelectors, setDeepRule, unsafeKeys, unsafeKeys as unsafeKeys$1 } from "@poupe/css"; import { Blend, DynamicScheme, Hct, Hct as Hct$1, MaterialDynamicColors, TonalPalette, Variant, sourceColorFromImage } from "@poupe/material-color-utilities"; import { Colord, colord as colord$1, extend } from "colord"; import mixPlugin from "colord/plugins/mix"; import { defu } from "defu"; const uint32 = (n) => n >>> 0; const uint8 = (n) => n >>> 0 & 255; const alphaFromArgb = (argb) => uint8(uint32(argb) >> 24); const redFromArgb = (argb) => uint8(uint32(argb) >> 16); const greenFromArgb = (argb) => uint8(uint32(argb) >> 8); const blueFromArgb = (argb) => uint8(uint32(argb)); const defaultColors = { indianred: "#cd5c5c", lightcoral: "#f08080", salmon: "#fa8072", darksalmon: "#e9967a", crimson: "#dc143c", red: "#ff0000", firebrick: "#b22222", darkred: "#8b0000", pink: "#ffc0cb", lightpink: "#ffb6c1", hotpink: "#ff69b4", deeppink: "#ff1493", mediumvioletred: "#c71585", palevioletred: "#db7093", lightsalmon: "#ffa07a", coral: "#ff7f50", tomato: "#ff6347", orangered: "#ff4500", darkorange: "#ff8c00", orange: "#ffa500", gold: "#ffd700", yellow: "#ffff00", lightyellow: "#ffffe0", lemonchiffon: "#fffacd", lightgoldenrodyellow: "#fafad2", papayawhip: "#ffefd5", moccasin: "#ffe4b5", peachpuff: "#ffdab9", palegoldenrod: "#eee8aa", khaki: "#f0e68c", darkkhaki: "#bdb76b", lavender: "#e6e6fa", thistle: "#d8bfd8", plum: "#dda0dd", violet: "#ee82ee", orchid: "#da70d6", fuchsia: "#ff00ff", magenta: "#ff00ff", mediumorchid: "#ba55d3", mediumpurple: "#9370db", rebeccapurple: "#663399", blueviolet: "#8a2be2", darkviolet: "#9400d3", darkorchid: "#9932cc", darkmagenta: "#8b008b", purple: "#800080", indigo: "#4b0082", slateblue: "#6a5acd", darkslateblue: "#483d8b", mediumslateblue: "#7b68ee", greenyellow: "#adff2f", chartreuse: "#7fff00", lawngreen: "#7cfc00", lime: "#00ff00", limegreen: "#32cd32", palegreen: "#98fb98", lightgreen: "#90ee90", mediumspringgreen: "#00fa9a", springgreen: "#00ff7f", mediumseagreen: "#3cb371", seagreen: "#2e8b57", forestgreen: "#228b22", green: "#008000", darkgreen: "#006400", yellowgreen: "#9acd32", olivedrab: "#6b8e23", olive: "#808000", darkolivegreen: "#556b2f", mediumaquamarine: "#66cdaa", darkseagreen: "#8fbc8f", lightseagreen: "#20b2aa", darkcyan: "#008b8b", teal: "#008080", aqua: "#00ffff", cyan: "#00ffff", lightcyan: "#e0ffff", paleturquoise: "#afeeee", aquamarine: "#7fffd4", turquoise: "#40e0d0", mediumturquoise: "#48d1cc", darkturquoise: "#00ced1", cadetblue: "#5f9ea0", steelblue: "#4682b4", lightsteelblue: "#b0c4de", powderblue: "#b0e0e6", lightblue: "#add8e6", skyblue: "#87ceeb", lightskyblue: "#87cefa", deepskyblue: "#00bfff", dodgerblue: "#1e90ff", cornflowerblue: "#6495ed", royalblue: "#4169e1", blue: "#0000ff", mediumblue: "#0000cd", darkblue: "#00008b", navy: "#000080", midnightblue: "#191970", cornsilk: "#fff8dc", blanchedalmond: "#ffebcd", bisque: "#ffe4c4", navajowhite: "#ffdead", wheat: "#f5deb3", burlywood: "#deb887", tan: "#d2b48c", rosybrown: "#bc8f8f", sandybrown: "#f4a460", goldenrod: "#daa520", darkgoldenrod: "#b8860b", peru: "#cd853f", chocolate: "#d2691e", saddlebrown: "#8b4513", sienna: "#a0522d", brown: "#a52a2a", maroon: "#800000", white: "#ffffff", snow: "#fffafa", honeydew: "#f0fff0", mintcream: "#f5fffa", azure: "#f0ffff", aliceblue: "#f0f8ff", ghostwhite: "#f8f8ff", whitesmoke: "#f5f5f5", seashell: "#fff5ee", beige: "#f5f5dc", oldlace: "#fdf5e6", floralwhite: "#fffaf0", ivory: "#fffff0", antiquewhite: "#faebd7", linen: "#faf0e6", lavenderblush: "#fff0f5", mistyrose: "#ffe4e1", gainsboro: "#dcdcdc", lightgray: "#d3d3d3", lightgrey: "#d3d3d3", silver: "#c0c0c0", darkgray: "#a9a9a9", darkgrey: "#a9a9a9", gray: "#808080", grey: "#808080", dimgray: "#696969", dimgrey: "#696969", lightslategray: "#778899", lightslategrey: "#778899", slategray: "#708090", slategrey: "#708090", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", black: "#000000", transparent: "#00000000" }; function withKnownColor(c) { if (typeof c !== "string" || !reOnlyLetters.test(c)) return c; const name = c.toLowerCase(); if (name in defaultColors) return defaultColors[name]; return c; } const reOnlyLetters = /^[a-zA-Z]+$/; const corePaletteKeys = [ "primary", "secondary", "tertiary", "neutral", "neutralVariant", "error" ]; extend([mixPlugin]); const isObjectColor = (c) => { if (c === null || typeof c !== "object") return false; else if (c instanceof Hct || c instanceof Colord) return false; else if ("r" in c && "g" in c && "b" in c || "h" in c && "c" in c && "t" in c || "h" in c && "s" in c && "l" in c || "h" in c && "s" in c && "v" in c || "h" in c && "w" in c && "b" in c || "x" in c && "y" in c && "z" in c || "l" in c && "a" in c && "b" in c || "l" in c && "c" in c && "h" in c || "c" in c && "m" in c && "y" in c && "k" in c) return true; else return false; }; function normalizeAlpha(a) { if (a === void 0) return; const n = a > 1 ? a / 255 : a; return Math.min(Math.max(n, 0), 1); } function withNormalizedAlpha(c) { if ("a" in c && c.a !== void 0) { const a = normalizeAlpha(c.a); return { ...c, a }; } return c; } const rgbFromRgbaColor = (c) => { const r255 = uint8(c.r); const g255 = uint8(c.g); const b255 = uint8(c.b); return uint32(r255 << 16 | g255 << 8 | b255); }; const rgbaFromHctColor = (c) => splitArgb(argbFromHctColor(c)); const rgba = (c) => splitArgb(argb(c)); const argbFromHct = (c) => c.toInt(); const argbFromRgbaColor = (c) => { const a = normalizeAlpha(c.a) ?? 1; return uint32(uint8(Math.round(a * 255)) << 24 | rgbFromRgbaColor(c)); }; const argbFromHctColor = (c) => { const argb = argbFromHct(Hct.from(c.h, c.c, c.t)); if (c.a === void 0) return argb; const a = normalizeAlpha(c.a); return uint32(uint8(Math.round(a * 255)) << 24 | argb & 16777215); }; const argbFromColord = (c) => { if (!c.isValid()) throw new Error("Invalid color"); return argbFromRgbaColor(c.rgba); }; const argbFromString = (s) => argbFromColord(colordFromString(s)); const splitArgb = (argb) => { const a255 = alphaFromArgb(argb); return { r: redFromArgb(argb), g: greenFromArgb(argb), b: blueFromArgb(argb), ...a255 > 0 ? { a: a255 / 255 } : {} }; }; const argb = (c) => { if (c instanceof Hct) return argbFromHct(c); else if (c instanceof Colord) return argbFromRgbaColor(c.rgba); else if (typeof c === "number") return c; else if (typeof c === "string") return argbFromString(c); else if ("h" in c && "c" in c && "t" in c) return argbFromHctColor(c); else return argbFromColord(colord(c)); }; const colordFromArgb = (argb) => colord$1(splitArgb(argb)); const colordFromHct = (c) => colordFromArgb(argbFromHct(c)); const colordFromHctColor = (c) => colordFromArgb(argbFromHctColor(c)); const colordFromString = (c) => { const c1 = colord$1(withKnownColor(c)); if (!c1.isValid()) throw new Error(`Invalid color '${c}'`); return c1; }; const colord = (c) => { if (c instanceof Colord) return c; else if (c instanceof Hct) return colordFromHct(c); else if (typeof c === "number") return colordFromArgb(c); else if (typeof c === "string") return colordFromString(c); else if ("h" in c && "c" in c && "t" in c) return colordFromHctColor(c); else if (!isObjectColor(c)) throw new Error("Invalid color"); else if ("a" in c && c.a !== void 0) { const a = normalizeAlpha(c.a); return colord$1({ ...c, a }); } else return colord$1(c); }; const hctFromArgb = (argb) => Hct.fromInt(argb); const hctFromRgbaColor = (c) => Hct.fromInt(argbFromRgbaColor(c)); const hctFromColord = (c) => { if (!c.isValid()) throw new Error("Invalid color"); return hctFromRgbaColor(c.rgba); }; const hctFromString = (s) => hctFromColord(colordFromString(s)); const splitHct = (c) => { return { h: c.hue, c: c.chroma, t: c.tone }; }; const hct = (c) => { if (c instanceof Hct) return c; else if (c instanceof Colord) return hctFromColord(c); else if (typeof c === "number") return hctFromArgb(c); else if (typeof c === "string") return hctFromString(c); else if ("h" in c && "c" in c && "t" in c) return Hct.from(c.h, c.c, c.t); else return hctFromColord(colord(c)); }; const hslFromColord = (c) => { if (!c.isValid()) throw new Error("Invalid color"); return c.toHsl(); }; const hslFromArgb = (argb) => hslFromColord(colord(splitArgb(argb))); const hslFromHct = (c) => hslFromArgb(argbFromHct(c)); const hslFromHctColor = (c) => hslFromColord(colord(rgbaFromHctColor(c))); const hslFromString = (s) => hslFromColord(colordFromString(s)); const hsl = (c) => { if (c instanceof Hct) return hslFromHct(c); else if (c instanceof Colord) return hslFromColord(c); else if (typeof c === "number") return hslFromArgb(c); else if (typeof c === "string") return hslFromString(c); else if ("h" in c && "c" in c && "t" in c) return hslFromHctColor(c); else return hslFromColord(colord(c)); }; const hexFromColord = (c) => { if (!c.isValid()) throw new Error("Invalid color"); return c.toHex(); }; const hexFromArgb = (argb) => hexFromColord(colord(splitArgb(argb))); const hexFromHct = (c) => hexFromArgb(argbFromHct(c)); const hexFromHctColor = (c) => hexFromColord(colord$1(rgbaFromHctColor(c))); function colorFormatter(v = "rgb") { if (typeof v === "function") return v; if (v === "numbers") return (c) => { const { r, g, b } = rgba(c); return `${r} ${g} ${b}`; }; if (v === "hsl") return hslString; if (v === "hex") return hexString; return rgbaString; } const hexString = (c) => { if (c instanceof Hct) return hexFromHct(c); else if (typeof c === "number") return hexFromArgb(c); const c1 = colord(c); if (!c1.isValid) throw new Error("Invalid color"); return hexFromColord(c1); }; function hslString(c, alpha = true) { const c1 = colord(c); if (!c1.isValid) throw new Error("Invalid color"); const { h, s, l, a: a0 } = c1.toHsl(); const a = alpha === false ? 1 : a0; if (a < 1) return `hsla(${h}, ${s}%, ${l}%, ${a})`; return `hsl(${h}, ${s}%, ${l}%)`; } const rgbaString = (c, alpha = true) => { const { r, g, b, a: a0 = 1 } = rgba(c); const a = alpha === false ? 1 : a0; if (a < 1) return `rgb(${r} ${g} ${b} / ${a.toFixed(2)})`; return `rgb(${r} ${g} ${b})`; }; function makeColorMix(base, other, ratios) { const c0 = colord(base); const c1 = colord(other); if (typeof ratios === "number") return hctFromColord(c0.mix(c1, ratios)); if (Array.isArray(ratios)) { const out = []; for (const r of ratios) { const c = c0.mix(c1, r); out.push(hctFromColord(c)); } return out; } const out = {}; for (const k of unsafeKeys(ratios)) out[k] = hctFromColord(c0.mix(c1, ratios[k])); return out; } function makeTonalPalette(color, harmonizeTo, isKeyColor = true) { let c = hct(color); if (harmonizeTo) c = hct(Blend.harmonize(c.toInt(), harmonizeTo.toInt())); if (isKeyColor) return TonalPalette.fromHct(c); return TonalPalette.fromHueAndChroma(c.hue, c.chroma); } function makeCustomColor(color, harmonizeTo, name, isKeyColor = true) { return makeCustomColorFromPalette(makeTonalPalette(color, harmonizeTo, isKeyColor), name); } function makeCustomColorFromPalette(tones, name) { return { name, tones, light: { color: tones.getHct(40), onColor: tones.getHct(100), colorContainer: tones.getHct(90), onColorContainer: tones.getHct(10) }, dark: { color: tones.getHct(80), onColor: tones.getHct(20), colorContainer: tones.getHct(30), onColorContainer: tones.getHct(90) } }; } const stateLayerOpacities = { hover: .08, focus: .12, pressed: .12, dragged: .16, disabled: .12, onDisabled: .38 }; function getStateColorMixParams(colorName, state, prefix = "") { const opacity = stateLayerOpacities[state]; const isOnColor = colorName.startsWith("on-"); const actualOpacity = state === "disabled" && isOnColor ? stateLayerOpacities.onDisabled : opacity; let baseColor; let onColor; if (isOnColor) { baseColor = colorName.replace("on-", ""); onColor = colorName; } else { baseColor = colorName; onColor = `on-${colorName}`; } return { state, baseColor: prefix ? `${prefix}${baseColor}` : baseColor, onColor: prefix ? `${prefix}${onColor}` : onColor, opacityPercent: Math.round(actualOpacity * 100) }; } function makeStateLayerColors(baseColor, onColor) { return makeColorMix(hct(baseColor), hct(onColor), { hover: stateLayerOpacities.hover, focus: stateLayerOpacities.focus, pressed: stateLayerOpacities.pressed, dragged: stateLayerOpacities.dragged, disabled: stateLayerOpacities.disabled, onDisabled: stateLayerOpacities.onDisabled }); } function makeStateVariants(colors, onColors) { const result = {}; for (const colorName in colors) { const baseColor = colors[colorName]; const onColorKey = `on-${colorName}`; const onColor = onColors[onColorKey]; if (!onColor) throw new Error(`Missing on-color for ${colorName}. Expected key: ${onColorKey}`); const states = makeStateLayerColors(baseColor, onColor); result[`${colorName}-hover`] = states.hover; result[`${colorName}-focus`] = states.focus; result[`${colorName}-pressed`] = states.pressed; result[`${colorName}-dragged`] = states.dragged; result[`${colorName}-disabled`] = states.disabled; result[`on-${colorName}-disabled`] = states.onDisabled; } return result; } const hexColorPattern = /^#([\da-f]{3}|[\da-f]{6}|[\da-f]{8})$/i; const isHexColor = (s = "") => !!hexColorPattern.test(s || ""); function isNonEmpty(object) { return !!object && Object.keys(object).length > 0; } const customDynamicColors = { "{}": (cc) => cc.color, "{}-container": (cc) => cc.colorContainer, "on-{}": (cc) => cc.onColor, "on-{}-container": (cc) => cc.onColorContainer }; const standardDynamicColors = { "surface": (ds) => ds.surface, "surface-dim": (ds) => ds.surfaceDim, "surface-bright": (ds) => ds.surfaceBright, "surface-variant": (ds) => ds.surfaceVariant, "surface-container-lowest": (ds) => ds.surfaceContainerLowest, "surface-container-low": (ds) => ds.surfaceContainerLow, "surface-container": (ds) => ds.surfaceContainer, "surface-container-high": (ds) => ds.surfaceContainerHigh, "surface-container-highest": (ds) => ds.surfaceContainerHighest, "inverse-surface": (ds) => ds.inverseSurface, "on-surface": (ds) => ds.onSurface, "on-surface-dim": (ds) => ds.onSurface, "on-surface-bright": (ds) => ds.onSurface, "on-surface-variant": (ds) => ds.onSurfaceVariant, "on-surface-container-lowest": (ds) => ds.onSurface, "on-surface-container-low": (ds) => ds.onSurface, "on-surface-container": (ds) => ds.onSurface, "on-surface-container-high": (ds) => ds.onSurface, "on-surface-container-highest": (ds) => ds.onSurface, "on-inverse-surface": (ds) => ds.inverseOnSurface, "primary": (ds) => ds.primary, "primary-dim": (ds) => ds.primaryDim, "primary-container": (ds) => ds.primaryContainer, "primary-fixed": (ds) => ds.primaryFixed, "primary-fixed-dim": (ds) => ds.primaryFixedDim, "inverse-primary": (ds) => ds.inversePrimary, "on-primary": (ds) => ds.onPrimary, "on-primary-container": (ds) => ds.onPrimaryContainer, "on-primary-fixed": (ds) => ds.onPrimaryFixed, "on-primary-fixed-variant": (ds) => ds.onPrimaryFixedVariant, "secondary": (ds) => ds.secondary, "secondary-dim": (ds) => ds.secondaryDim, "secondary-container": (ds) => ds.secondaryContainer, "secondary-fixed": (ds) => ds.secondaryFixed, "secondary-fixed-dim": (ds) => ds.secondaryFixedDim, "on-secondary": (ds) => ds.onSecondary, "on-secondary-container": (ds) => ds.onSecondaryContainer, "on-secondary-fixed": (ds) => ds.onSecondaryFixed, "on-secondary-fixed-variant": (ds) => ds.onSecondaryFixedVariant, "tertiary": (ds) => ds.tertiary, "tertiary-dim": (ds) => ds.tertiaryDim, "tertiary-container": (ds) => ds.tertiaryContainer, "tertiary-fixed": (ds) => ds.tertiaryFixed, "tertiary-fixed-dim": (ds) => ds.tertiaryFixedDim, "on-tertiary": (ds) => ds.onTertiary, "on-tertiary-container": (ds) => ds.onTertiaryContainer, "on-tertiary-fixed": (ds) => ds.onTertiaryFixed, "on-tertiary-fixed-variant": (ds) => ds.onTertiaryFixedVariant, "error": (ds) => ds.error, "error-container": (ds) => ds.errorContainer, "on-error": (ds) => ds.onError, "on-error-container": (ds) => ds.onErrorContainer, "outline": (ds) => ds.outline, "outline-variant": (ds) => ds.outlineVariant, "shadow": (ds) => ds.shadow, "scrim": (ds) => ds.scrim }; const contentAccentToneDelta = MaterialDynamicColors.contentAccentToneDelta; const standardDynamicColorKeys = Object.keys(standardDynamicColors); const standardPalettes = { primary: (ds) => ds.primaryPalette, secondary: (ds) => ds.secondaryPalette, tertiary: (ds) => ds.tertiaryPalette, neutral: (ds) => ds.neutralPalette, neutralVariant: (ds) => ds.neutralVariantPalette }; const standardPaletteKeys = Object.keys(standardPalettes); const standardDynamicSchemes = { content: Variant.CONTENT, expressive: Variant.EXPRESSIVE, fidelity: Variant.FIDELITY, monochrome: Variant.MONOCHROME, neutral: Variant.NEUTRAL, tonalSpot: Variant.TONAL_SPOT, vibrant: Variant.VIBRANT, rainbow: Variant.RAINBOW, fruitSalad: Variant.FRUIT_SALAD }; function makeStandardColorsFromScheme(scheme) { const out = {}; for (const [name, fn] of pairs(standardDynamicColors)) out[name] = Hct.fromInt(fn(scheme)); return out; } function makeStandardPaletteKeyColorsFromScheme(scheme) { const out = {}; for (const [kebabName, palette] of pairs(makeStandardPaletteFromScheme(scheme))) out[kebabName] = palette.keyColor; return out; } function makeStandardPaletteFromScheme(scheme) { const out = {}; for (const [name, fn] of pairs(standardPalettes)) { const kebabName = kebabCase(name); out[kebabName] = fn(scheme); } return out; } function makeCustomColors(source, colors) { const $source = hct(source); const colorOptions = {}; const palettes = {}; const darkColors = {}; const lightColors = {}; for (const [color, options] of pairs(colors)) { const kebabName = kebabCase(color); const { tones, dark, light } = makeCustomColor(hct(options.value), options.harmonize ?? true ? $source : void 0, kebabName); colorOptions[kebabName] = options; palettes[kebabName] = tones; for (const [pattern, fn] of Object.entries(customDynamicColors)) { const name = pattern.replace("{}", kebabName); darkColors[name] = fn(dark); lightColors[name] = fn(light); } } return { source, colors: unsafeKeys(colorOptions), colorOptions, palettes, dark: darkColors, light: lightColors }; } function makeCustomColorsFromPalettes(colors = {}) { const palettes = {}; const darkColors = {}; const lightColors = {}; for (const [color, tones] of pairs(colors)) { const kebabName = kebabCase(color); const { dark, light } = makeCustomColorFromPalette(tones, kebabName); palettes[kebabName] = tones; for (const [pattern, fn] of pairs(customDynamicColors)) { const name = pattern.replace("{}", kebabName); darkColors[name] = fn(dark); lightColors[name] = fn(light); } } return { colors: unsafeKeys(palettes), palettes, dark: darkColors, light: lightColors }; } function makeDynamicScheme(source, variant, contrastLevel, isDark, palettes = {}) { return new DynamicScheme({ sourceColorHct: source, variant, contrastLevel, isDark, primaryPalette: palettes.primary, secondaryPalette: palettes.secondary, tertiaryPalette: palettes.tertiary, neutralPalette: palettes.neutral, neutralVariantPalette: palettes.neutralVariant, errorPalette: palettes.error, specVersion: "2025", platform: "phone" }); } function flattenPartialColorOptions(c) { if (c === void 0 || c === null) return {}; else if (c instanceof Hct || c instanceof Colord) return { value: c }; else if (typeof c !== "object") return { value: c }; else if ("value" in c) return c; else if (isObjectColor(c)) return { value: c }; else return c; } const flattenColorOptions = (c) => { const c2 = flattenPartialColorOptions(c); if (c2.value === void 0) throw new Error("invalid color"); else if (c2.value instanceof Hct) return c2; else return { ...c2, value: hct(c2.value) }; }; const makeThemePalettes = (colors) => { const { colors: $colors } = cookThemeColors(colors); const { primary, ...rest } = $colors; const { value: primaryValue } = primary; const source = hct(primaryValue); const corePalettes = { primary: makeTonalPalette(source) }; const extraPalettes = {}; for (const [name, options] of pairs$1(rest)) { const { value, harmonize = true } = options; const tones = makeTonalPalette(value, harmonize ? source : void 0); if (corePaletteKeys.includes(name)) corePalettes[name] = tones; else extraPalettes[name] = tones; } return { source, corePalettes, extraPalettes, palettes: { ...corePalettes, ...extraPalettes }, colors: $colors }; }; function cookThemeColors(colors) { const { primary, ...rest } = colors; const out = { primary: flattenColorOptions(primary) }; for (const key of unsafeKeys$1(rest)) { const $options = flattenColorOptions(rest[key]); const { name = key } = $options; out[camelCase(name)] = $options; } return { keys: Object.keys(out), colors: out }; } function makeStandardStateVariants(colors) { const variants = {}; for (const [baseKey, onKey] of [ ["primary", "on-primary"], ["secondary", "on-secondary"], ["tertiary", "on-tertiary"], ["error", "on-error"], ["surface", "on-surface"], ["surface-variant", "on-surface-variant"], ["primary-container", "on-primary-container"], ["secondary-container", "on-secondary-container"], ["tertiary-container", "on-tertiary-container"], ["error-container", "on-error-container"] ]) { const baseColor = colors[baseKey]; const onColor = colors[onKey]; if (baseColor && onColor) { const states = makeStateLayerColors(baseColor, onColor); variants[`${baseKey}-hover`] = states.hover; variants[`${baseKey}-focus`] = states.focus; variants[`${baseKey}-pressed`] = states.pressed; variants[`${baseKey}-dragged`] = states.dragged; variants[`${baseKey}-disabled`] = states.disabled; } } return variants; } function makeCustomStateVariants(customColors) { const variants = {}; const colorNames = /* @__PURE__ */ new Set(); for (const key in customColors) { const match = key.match(/^(?:on-)?([^-]+(?:-[^-]+)*)(?:-container)?$/); if (match) colorNames.add(match[1]); } for (const colorName of colorNames) { const baseKey = colorName; const onKey = `on-${colorName}`; const containerKey = `${colorName}-container`; const onContainerKey = `on-${colorName}-container`; if (customColors[baseKey] && customColors[onKey]) { const states = makeStateLayerColors(customColors[baseKey], customColors[onKey]); variants[`${colorName}-hover`] = states.hover; variants[`${colorName}-focus`] = states.focus; variants[`${colorName}-pressed`] = states.pressed; variants[`${colorName}-dragged`] = states.dragged; variants[`${colorName}-disabled`] = states.disabled; } if (customColors[containerKey] && customColors[onContainerKey]) { const states = makeStateLayerColors(customColors[containerKey], customColors[onContainerKey]); variants[`${colorName}-container-hover`] = states.hover; variants[`${colorName}-container-focus`] = states.focus; variants[`${colorName}-container-pressed`] = states.pressed; variants[`${colorName}-container-dragged`] = states.dragged; variants[`${colorName}-container-disabled`] = states.disabled; } } return variants; } function makeThemeKeys(colors) { const colorOptions = {}; const kebabStandardPaletteKeys = standardPaletteKeys.map((s) => kebabCase(s)); const paletteKeys = [...kebabStandardPaletteKeys]; const keys = [...standardDynamicColorKeys]; for (const name of unsafeKeys(colors)) { const kebabName = kebabCase(name); colorOptions[kebabName] = flattenPartialColorOptions(colors[name]); if (!keys.includes(kebabName)) { paletteKeys.push(kebabName); for (const pattern in customDynamicColors) keys.push(pattern.replace("{}", kebabName)); } } for (const kebabName of kebabStandardPaletteKeys) { if (!keys.includes(kebabName)) keys.push(kebabName); if (!(kebabName in colorOptions)) colorOptions[kebabName] = {}; } return { keys, paletteKeys, colorOptions }; } function makeTheme(colors, scheme = "content", contrastLevel = 0, extraOptions) { return makeThemeWithOptions(colors, { scheme, contrastLevel, useColorMix: false, ...extraOptions }); } function makeThemeWithOptions(colors, options) { const { source, corePalettes, extraPalettes, colors: colorOptions } = makeThemePalettes(colors); const { contrastLevel = 0, scheme = "content" } = options; const variant = standardDynamicSchemes[scheme] ?? standardDynamicSchemes.content; const darkScheme = makeDynamicScheme(source, variant, contrastLevel, true, corePalettes); const lightScheme = makeDynamicScheme(source, variant, contrastLevel, false, corePalettes); const baseResult = { source, colorOptions, darkScheme, lightScheme, extraPalettes, ...makeThemeFromSchemes(darkScheme, lightScheme, extraPalettes) }; if (options.useColorMix) return baseResult; const { dark, light } = baseResult; const darkStateColors = makeStandardStateVariants(dark); const lightStateColors = makeStandardStateVariants(light); const darkCustomStateColors = isNonEmpty(extraPalettes) ? makeCustomStateVariants(baseResult.dark) : {}; const lightCustomStateColors = isNonEmpty(extraPalettes) ? makeCustomStateVariants(baseResult.light) : {}; return { ...baseResult, dark: { ...dark, ...darkStateColors, ...darkCustomStateColors }, light: { ...light, ...lightStateColors, ...lightCustomStateColors } }; } function makeThemeFromSchemes(darkScheme, lightScheme, extraPalettes) { const { palettes: darkPalettes, keyColors: darkKeyColors, colors: darkStandardColors } = cookThemeScheme(darkScheme); const { palettes: lightPalettes, keyColors: lightKeyColors, colors: lightStandardColors } = cookThemeScheme(lightScheme); const { dark: darkCustomColors, light: lightCustomColors, keyColors: extraKeyColors } = cookThemeCustomColors(extraPalettes); const dark = { ...darkKeyColors, ...darkStandardColors, ...darkCustomColors }; const light = { ...lightKeyColors, ...lightStandardColors, ...lightCustomColors }; return { darkKeyColors: { ...darkKeyColors, ...extraKeyColors }, lightKeyColors: { ...lightKeyColors, ...extraKeyColors }, darkPalettes: { ...darkPalettes, ...extraPalettes }, lightPalettes: { ...lightPalettes, ...extraPalettes }, dark, light }; } function cookThemeScheme(scheme) { return { palettes: makeStandardPaletteFromScheme(scheme), keyColors: makeStandardPaletteKeyColorsFromScheme(scheme), colors: makeStandardColorsFromScheme(scheme) }; } function cookThemeCustomColors(palettes) { const { dark, light, palettes: tones } = makeCustomColorsFromPalettes(palettes); const keyColors = {}; for (const [name, palette] of pairs(tones)) keyColors[name] = palette.keyColor; return { dark, light, keyColors }; } function defaultCSSThemeOptions(options = {}) { return defu(options, { darkMode: ".dark", lightMode: ".light", prefix: "md-", darkSuffix: "-dark", lightSuffix: "-light", stringify: rgbaString }); } function defaultDarkSelector(options) { const { addStarVariantsToDark = true } = options; let { darkMode = true } = options; if (darkMode === true) darkMode = ".dark"; else if (darkMode === false || darkMode === "") darkMode = "media"; return processCSSSelectors(darkMode, { addStarVariants: addStarVariantsToDark }) ?? [".dark, .dark *"]; } function defaultLightSelector(options) { const { addStarVariantsToLight = true } = options; let { lightMode = true } = options; if (lightMode === true) lightMode = ".light"; else if (lightMode === false || lightMode === "") return void 0; return processCSSSelectors(lightMode, { addStarVariants: addStarVariantsToLight }) ?? [".light, .light *"]; } function defaultRootLightSelector(options) { const rootSelector = [":root"]; const lightSelector = defaultLightSelector(options); if (lightSelector) { const combined = processCSSSelectors([...rootSelector, ...lightSelector], { addStarVariants: false }); if (combined) return combined; } return rootSelector; } function assembleCSSRules(root, light, dark, options) { const rootSelector = ":root"; const darkSelector = defaultDarkSelector(options); const rootLightSelector = defaultRootLightSelector(options); const styles = []; if (root) styles.push({ [rootSelector]: root }); const combinedRules = {}; setDeepRule(combinedRules, rootLightSelector, light); setDeepRule(combinedRules, darkSelector, dark); styles.push(combinedRules); return styles; } function generateCSSColorVariables(dark, light, options) { const { prefix, darkSuffix, lightSuffix, stringify } = options; let darkVars = {}; let lightVars = {}; const darkValues = {}; const lightValues = {}; const vars = {}; for (const k of unsafeKeys(dark)) { const k0 = `--${prefix}${k}`; const k1 = `${k0}${darkSuffix}`; const k2 = `${k0}${lightSuffix}`; const v1 = stringify(dark[k]); const v2 = stringify(light[k]); darkValues[k1] = v1; lightValues[k2] = v2; vars[k] = k0; if (k1 !== k0) darkVars[k0] = `var(${k1})`; if (k2 !== k0) lightVars[k0] = `var(${k2})`; } if (darkSuffix === lightSuffix) { for (const key of Object.keys(darkValues)) if (key in lightValues && darkValues[key] === lightValues[key]) delete darkValues[key]; for (const key of Object.keys(darkVars)) if (key in lightVars && darkVars[key] === lightVars[key]) delete darkVars[key]; } if (unsafeKeys(darkVars).length === 0) darkVars = void 0; if (unsafeKeys(lightVars).length === 0) lightVars = void 0; return { vars, darkValues, lightValues, darkVars, lightVars }; } function assembleCSSColors(dark, light, options = {}) { const $options = defaultCSSThemeOptions(options); const { vars, darkValues, lightValues, darkVars, lightVars } = generateCSSColorVariables(dark, light, $options); let rootStyles; let lightStyles; let darkStyles; if (darkVars) { rootStyles = { ...darkValues }; darkStyles = darkVars; } else darkStyles = darkValues; if (lightVars) { rootStyles = { ...rootStyles, ...lightValues }; lightStyles = lightVars; } else lightStyles = lightValues; return { vars, darkValues, lightValues, darkVars, lightVars, styles: assembleCSSRules(rootStyles, lightStyles, darkStyles, $options), options: $options }; } function makeCSSTheme(colors, options = {}) { const { dark, light } = makeTheme(colors, options.scheme, options.contrastLevel); return assembleCSSColors(dark, light, options); } async function fromImageElement(image) { const color = await sourceColorFromImage(image); return Hct$1.fromInt(color); } export { Colord, DynamicScheme, Hct, TonalPalette, Variant, argb, argbFromColord, argbFromHct, argbFromHctColor, argbFromRgbaColor, argbFromString, assembleCSSColors, colorFormatter, colord, colordFromArgb, colordFromHct, colordFromHctColor, colordFromString, contentAccentToneDelta, corePaletteKeys, customDynamicColors, defaultCSSThemeOptions, defaultColors, defaultDarkSelector, defaultLightSelector, flattenColorOptions, flattenPartialColorOptions, formatCSSRules, formatCSSRulesArray, fromImageElement, generateCSSRules, generateCSSRulesArray, getStateColorMixParams, hct, hctFromArgb, hctFromColord, hctFromRgbaColor, hctFromString, hexColorPattern, hexFromArgb, hexFromColord, hexFromHct, hexFromHctColor, hexString, hsl, hslFromArgb, hslFromColord, hslFromHct, hslFromHctColor, hslFromString, hslString, isHexColor, isObjectColor, makeCSSTheme, makeColorMix, makeCustomColor, makeCustomColorFromPalette, makeCustomColors, makeCustomColorsFromPalettes, makeCustomStateVariants, makeDynamicScheme, makeStandardColorsFromScheme, makeStandardPaletteFromScheme, makeStandardPaletteKeyColorsFromScheme, makeStandardStateVariants, makeStateLayerColors, makeStateVariants, makeTheme, makeThemeFromSchemes, makeThemeKeys, makeThemePalettes, makeTonalPalette, normalizeAlpha, rgbFromRgbaColor, rgba, rgbaFromHctColor, rgbaString, splitArgb, splitHct, standardDynamicColorKeys, standardDynamicColors, standardDynamicSchemes, standardPaletteKeys, standardPalettes, stateLayerOpacities, withKnownColor, withNormalizedAlpha }; //# sourceMappingURL=index.mjs.map