@poupe/theme-builder
Version:
Design token management and theme generation system for Poupe UI framework
953 lines (952 loc) • 32.6 kB
JavaScript
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