@poupe/theme-builder
Version:
Design token management and theme generation system for Poupe UI framework
1 lines • 17.3 kB
Source Map (JSON)
{"version":3,"file":"core.mjs","sources":["../src/core/types.ts","../src/core/mix.ts","../src/core/palettes.ts","../src/core/states.ts","../src/core/index.ts"],"sourcesContent":["import {\n Hct,\n TonalPalette,\n} from '@poupe/material-color-utilities';\n\nimport {\n type AnyColor,\n type RgbColor,\n Colord,\n} from 'colord';\n\nexport {\n DynamicScheme,\n Hct,\n TonalPalette,\n Variant,\n} from '@poupe/material-color-utilities';\n\nexport {\n type AnyColor,\n type RgbColor,\n type HslColor,\n type HslaColor,\n Colord,\n} from 'colord';\n\nexport type ColorMap<K extends string> = Record<K, Hct>;\n\nexport type HexColor = `#${string}`;\n\n/** {@link RgbColor} variant with optional alpha value */\nexport type RgbaColor = RgbColor & { a?: number };\n\n/** destructured {@link Hct} color with optional alpha value */\nexport interface HctColor {\n h: number\n c: number\n t: number\n a?: number\n}\n\n/** ObjectColor represents a destructured Color object */\nexport type ObjectColor = HctColor | Exclude<AnyColor, string>;\n\n/** Color is any accepted color representation */\nexport type Color = Hct | Colord | ObjectColor | number | string;\n\n/**\n * Core Material Design 3 palettes for DynamicSchemeOptions.\n * Contains tonal palettes for the standard Material Design color roles.\n */\nexport type CorePalettes = {\n primary: TonalPalette\n secondary?: TonalPalette\n tertiary?: TonalPalette\n neutral?: TonalPalette\n neutralVariant?: TonalPalette\n error?: TonalPalette\n};\n\n/**\n * Type representing the valid keys for core palettes in the Material Design 3 color system.\n * Derived from the keys of the {@link CorePalettes} type, representing standard color roles.\n */\nexport type CorePaletteKey = keyof CorePalettes;\n\n/**\n * Readonly array of core palette keys used in Material Design 3 color system.\n * Represents the standard color roles that can be defined in a dynamic color scheme.\n */\nexport const corePaletteKeys: Readonly<CorePaletteKey[]> = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'neutral',\n 'neutralVariant',\n 'error',\n];\n\n/**\n * Represents a custom color definition with tones and color groups for light and dark schemes\n * @param name - Optional name for the custom color\n * @param tones - Readonly tonal palette defining color variations\n * @param light - Color group representing the light scheme variant\n * @param dark - Color group representing the dark scheme variant\n */\nexport type CustomColor = {\n name?: string\n tones: TonalPalette\n light: Readonly<ColorGroup>\n dark: Readonly<ColorGroup>\n};\n\n/**\n * Represents a color group with specific color roles in a color scheme.\n * Defines the color and its on-color variants for different contexts and containers.\n * @param color - The custom color\n * @param onColor - The color used for content on top of the custom color\n * @param colorContainer - The container variant of the custom color\n * @param onColorContainer - The color used for content on the color container\n */\nexport type ColorGroup = {\n color: Hct\n onColor: Hct\n colorContainer: Hct\n onColorContainer: Hct\n};\n","import {\n unsafeKeys,\n} from '@poupe/css';\n\nimport {\n type Color,\n Hct,\n} from './types';\n\nimport {\n colord,\n hctFromColord,\n} from './colors';\n\n/** @returns the result of mixing two colors in given ratios */\nexport function makeColorMix(base: Color, other: Color, ratios: number): Hct;\nexport function makeColorMix(base: Color, other: Color, ratios: Array<number>): Hct[];\nexport function makeColorMix<K extends string>(base: Color, other: Color, ratios: Record<K, number>): Record<K, Hct>;\nexport function makeColorMix<K extends string>(base: Color, other: Color, ratios: number | Array<number> | Record<K, number>): Hct | Hct[] | Record<K, Hct> {\n const c0 = colord(base);\n const c1 = colord(other);\n\n if (typeof ratios === 'number') {\n // single value\n const c = c0.mix(c1, ratios);\n return hctFromColord(c);\n }\n\n if (Array.isArray(ratios)) {\n // array\n const out: Hct[] = [];\n for (const r of ratios) {\n const c = c0.mix(c1, r);\n out.push(hctFromColord(c));\n }\n return out;\n }\n\n // named\n const out = {} as Record<K, Hct>;\n for (const k of unsafeKeys(ratios)) {\n const c = c0.mix(c1, ratios[k]);\n out[k] = hctFromColord(c);\n }\n\n return out;\n}\n","import {\n Blend,\n} from '@poupe/material-color-utilities';\n\nimport {\n type Color,\n type CustomColor,\n Hct,\n TonalPalette,\n} from './types';\n\nimport {\n hct,\n} from './colors';\n\n/**\n * Creates a tonal palette from a color with optional harmonization\n *\n * @param color - The base color to create the palette from\n * @param harmonizeTo - Optional target color to harmonize towards.\n * Harmonization shifts the hue of the base color\n * towards the target color while preserving tone.\n * @param isKeyColor - Whether to preserve the exact tone from the input\n * color (true) as key color or derive a tone\n * algorithmically (false).\n * @defaultValue `true`\n * @returns A TonalPalette instance handling tones from 0-100\n */\nexport function makeTonalPalette(color: Color, harmonizeTo?: Hct, isKeyColor: boolean = true): TonalPalette {\n let c = hct(color);\n if (harmonizeTo) {\n const c1 = Blend.harmonize(c.toInt(), harmonizeTo.toInt());\n c = hct(c1);\n }\n\n if (isKeyColor)\n return TonalPalette.fromHct(c);\n\n return TonalPalette.fromHueAndChroma(c.hue, c.chroma);\n}\n\n/**\n * Generates a custom color optionally harmonized to a target color\n *\n * @param color - The base color to transform into a custom color\n * @param harmonizeTo - Optional target color to harmonize towards.\n * Harmonization blends the hue of the base color\n * with the target while maintaining lightness.\n * @param name - Optional name identifier for the custom color\n * @param isKeyColor - Whether to preserve exact tone from input color\n * as key color. @defaultValue `true`.\n * @returns A CustomColor object with light and dark theme variations,\n * including primary color, on-color, container, and\n * on-container variants for each theme\n */\nexport function makeCustomColor(color: Color, harmonizeTo?: Hct, name?: string, isKeyColor: boolean = true): CustomColor {\n const tones = makeTonalPalette(color, harmonizeTo, isKeyColor);\n\n return makeCustomColorFromPalette(tones, name);\n}\n\n/**\n * Generates a custom color from a given tonal palette with predefined\n * light and dark color variations.\n *\n * Light theme uses tones: 40 (color), 100 (onColor), 90 (container),\n * 10 (onContainer). Dark theme uses tones: 80 (color), 20 (onColor),\n * 30 (container), 90 (onContainer). These follow Material Design 3\n * color system guidelines for optimal contrast and accessibility.\n *\n * @param tones - The tonal palette (0-100 tone range) used to derive\n * color variations\n * @param name - Optional name identifier for the custom color\n * @returns A CustomColor object with light and dark color configurations\n * containing color, onColor, colorContainer, and onColorContainer\n * properties for each theme mode\n */\nexport function makeCustomColorFromPalette(tones: TonalPalette, name?: string): CustomColor {\n return {\n name,\n tones,\n light: {\n color: tones.getHct(40),\n onColor: tones.getHct(100),\n colorContainer: tones.getHct(90),\n onColorContainer: tones.getHct(10),\n },\n dark: {\n color: tones.getHct(80),\n onColor: tones.getHct(20),\n colorContainer: tones.getHct(30),\n onColorContainer: tones.getHct(90),\n },\n };\n}\n","import type { Color, Hct } from './types';\nimport { makeColorMix } from './mix';\nimport { hct } from './colors';\n\n/**\n * Material Design 3 state layer opacity values\n * @see https://m3.material.io/foundations/interaction/states/state-layers\n */\nexport const stateLayerOpacities = {\n hover: 0.08,\n focus: 0.12,\n pressed: 0.12,\n dragged: 0.16,\n disabled: 0.12,\n onDisabled: 0.38,\n} as const;\n\nexport type StateLayerOpacity = typeof stateLayerOpacities;\nexport type InteractionState = keyof Omit<StateLayerOpacity, 'disabled' | 'onDisabled'>;\nexport type StateVariants<T extends string> = {\n [K in T as `${K}-hover` | `${K}-focus` | `${K}-pressed` | `${K}-dragged` | `${K}-disabled`]: Hct;\n} & {\n [K in T as `on-${K}-disabled`]: Hct;\n};\n\n/**\n * State color mix parameters for CSS color-mix() function\n */\nexport interface StateColorMixParams {\n /** The state type */\n state: keyof typeof stateLayerOpacities\n /** The base color CSS variable name */\n baseColor: string\n /** The on-color CSS variable name */\n onColor: string\n /** The opacity percentage for mixing (0-100) */\n opacityPercent: number\n}\n\n/**\n * Get CSS color-mix parameters for creating state colors\n * @param colorName - The base color name (e.g., 'primary', 'secondary')\n * @param state - The state type\n * @param prefix - Optional CSS variable prefix (defaults to empty string)\n * @returns Parameters for creating CSS color-mix\n */\nexport function getStateColorMixParams(\n colorName: string,\n state: keyof Omit<StateLayerOpacity, 'onDisabled'>,\n prefix = '',\n): StateColorMixParams {\n const opacity = stateLayerOpacities[state];\n const isOnColor = colorName.startsWith('on-');\n\n // For disabled state on \"on-colors\", we use different opacity\n const actualOpacity = state === 'disabled' && isOnColor ? stateLayerOpacities.onDisabled : opacity;\n\n // Determine base and on-color names\n let baseColor: string;\n let onColor: string;\n\n if (isOnColor) {\n // For on-colors (e.g., 'on-primary'), the base is the color without 'on-' prefix\n baseColor = colorName.replace('on-', '');\n onColor = colorName;\n } else {\n // For base colors (e.g., 'primary'), we mix with the on-color\n baseColor = colorName;\n onColor = `on-${colorName}`;\n }\n\n return {\n state,\n baseColor: prefix ? `${prefix}${baseColor}` : baseColor,\n onColor: prefix ? `${prefix}${onColor}` : onColor,\n opacityPercent: Math.round(actualOpacity * 100),\n };\n}\n\n/**\n * Creates state layer colors by mixing the on-color with the base color at specified opacities\n * Following Material Design 3 state layer principles\n *\n * @param baseColor - The base/background color\n * @param onColor - The on-color (content color that goes on top of the base)\n * @returns Object with state layer colors for each interaction state\n */\nexport function makeStateLayerColors(baseColor: Color, onColor: Color) {\n const base = hct(baseColor);\n const on = hct(onColor);\n\n return makeColorMix(base, on, {\n hover: stateLayerOpacities.hover,\n focus: stateLayerOpacities.focus,\n pressed: stateLayerOpacities.pressed,\n dragged: stateLayerOpacities.dragged,\n disabled: stateLayerOpacities.disabled,\n onDisabled: stateLayerOpacities.onDisabled,\n });\n}\n\n/**\n * Generates state variants for a set of color pairs\n * Each color should have a corresponding on-color\n *\n * @param colors - Object with base colors and their on-colors\n * @returns Object with state variants for each color\n */\nexport function makeStateVariants<K extends string>(\n colors: Record<K, Color>,\n onColors: Record<`on-${K}`, Color>,\n): StateVariants<K> {\n const result: Record<string, Hct> = {};\n\n for (const colorName in colors) {\n const baseColor = colors[colorName];\n const onColorKey = `on-${colorName}` as `on-${K}`;\n const onColor = onColors[onColorKey];\n\n if (!onColor) {\n throw new Error(`Missing on-color for ${colorName}. Expected key: ${onColorKey}`);\n }\n\n const states = makeStateLayerColors(baseColor, onColor);\n\n result[`${colorName}-hover`] = states.hover;\n result[`${colorName}-focus`] = states.focus;\n result[`${colorName}-pressed`] = states.pressed;\n result[`${colorName}-dragged`] = states.dragged;\n result[`${colorName}-disabled`] = states.disabled;\n result[`on-${colorName}-disabled`] = states.onDisabled;\n }\n\n return result as StateVariants<K>;\n}\n","// re-export\n//\nexport {\n formatCSSRules,\n formatCSSRulesArray,\n generateCSSRules,\n generateCSSRulesArray,\n} from '@poupe/css';\n\nexport * from './colors';\nexport * from './default-colors';\nexport * from './formatter';\nexport * from './mix';\nexport * from './palettes';\nexport * from './states';\nexport * from './types';\n\n// tools\n//\nexport const hexColorPattern = /^#([\\da-f]{3}|[\\da-f]{6}|[\\da-f]{8})$/i;\nexport const isHexColor = (s: string = '') => !!hexColorPattern.test(s || '');\n"],"names":["out"],"mappings":"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"}