@epilot/spark-ui 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion.cjs +2 -2
- package/dist/components/accordion.d.ts +1 -1
- package/dist/components/accordion.js +1 -1
- package/dist/components/alert-dialog.cjs +1 -1
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/attachment.d.cts +2 -2
- package/dist/components/attachment.d.ts +2 -2
- package/dist/components/autocomplete.cjs +1 -1
- package/dist/components/autocomplete.js +1 -1
- package/dist/components/avatar.cjs +1 -1
- package/dist/components/avatar.cjs.map +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.cjs +1 -1
- package/dist/components/badge.cjs.map +1 -1
- package/dist/components/badge.d.cts +2 -1
- package/dist/components/badge.d.ts +2 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/{energy-bar-chart.cjs → bar-chart-with-total.cjs} +4 -4
- package/dist/components/bar-chart-with-total.cjs.map +1 -0
- package/dist/components/bar-chart-with-total.d.cts +19 -0
- package/dist/components/bar-chart-with-total.d.ts +19 -0
- package/dist/components/{energy-bar-chart.js → bar-chart-with-total.js} +4 -4
- package/dist/components/bar-chart-with-total.js.map +1 -0
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button-group.cjs +2 -2
- package/dist/components/button-group.cjs.map +1 -1
- package/dist/components/button-group.d.cts +1 -1
- package/dist/components/button-group.d.ts +1 -1
- package/dist/components/button-group.js +2 -2
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/button.cjs +6 -4
- package/dist/components/button.cjs.map +1 -1
- package/dist/components/button.d.cts +6 -5
- package/dist/components/button.d.ts +6 -5
- package/dist/components/button.js +6 -4
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar.cjs +3 -3
- package/dist/components/calendar.cjs.map +1 -1
- package/dist/components/calendar.js +3 -3
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/callout.cjs +11 -11
- package/dist/components/callout.cjs.map +1 -1
- package/dist/components/callout.d.ts +1 -1
- package/dist/components/callout.js +11 -11
- package/dist/components/callout.js.map +1 -1
- package/dist/components/checkbox.cjs +1 -1
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/circular-gauge.cjs +7 -7
- package/dist/components/circular-gauge.cjs.map +1 -1
- package/dist/components/circular-gauge.js +7 -7
- package/dist/components/circular-gauge.js.map +1 -1
- package/dist/components/day-of-week-picker.cjs +21 -41
- package/dist/components/day-of-week-picker.cjs.map +1 -1
- package/dist/components/day-of-week-picker.d.cts +19 -23
- package/dist/components/day-of-week-picker.d.ts +19 -23
- package/dist/components/day-of-week-picker.js +21 -41
- package/dist/components/day-of-week-picker.js.map +1 -1
- package/dist/components/dialog.cjs +1 -1
- package/dist/components/dialog.js +1 -1
- package/dist/components/dropdown-menu.cjs +1 -1
- package/dist/components/dropdown-menu.js +1 -1
- package/dist/components/empty-state.d.ts +1 -1
- package/dist/components/{device-card.cjs → entity-card.cjs} +31 -31
- package/dist/components/entity-card.cjs.map +1 -0
- package/dist/components/{device-card.d.cts → entity-card.d.cts} +15 -15
- package/dist/components/{device-card.d.ts → entity-card.d.ts} +16 -16
- package/dist/components/{device-card.js → entity-card.js} +20 -20
- package/dist/components/entity-card.js.map +1 -0
- package/dist/components/{feed-in-hero.cjs → hero-metric.cjs} +10 -15
- package/dist/components/hero-metric.cjs.map +1 -0
- package/dist/components/hero-metric.d.cts +27 -0
- package/dist/components/hero-metric.d.ts +27 -0
- package/dist/components/{feed-in-hero.js → hero-metric.js} +7 -12
- package/dist/components/hero-metric.js.map +1 -0
- package/dist/components/info-block.d.cts +1 -1
- package/dist/components/info-block.d.ts +1 -1
- package/dist/components/input-date.cjs +1 -1
- package/dist/components/input-date.js +1 -1
- package/dist/components/input-otp.cjs +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/input.cjs +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/line-chart.cjs +1 -1
- package/dist/components/line-chart.js +1 -1
- package/dist/components/marker.d.cts +1 -1
- package/dist/components/marker.d.ts +1 -1
- package/dist/components/message-scroller.cjs +3 -3
- package/dist/components/message-scroller.js +3 -3
- package/dist/components/number-flow.d.cts +12 -2
- package/dist/components/number-flow.d.ts +12 -2
- package/dist/components/page-header.cjs +1 -1
- package/dist/components/page-header.js +1 -1
- package/dist/components/period-tabs.cjs +2 -22
- package/dist/components/period-tabs.cjs.map +1 -1
- package/dist/components/period-tabs.d.cts +5 -22
- package/dist/components/period-tabs.d.ts +5 -22
- package/dist/components/period-tabs.js +2 -22
- package/dist/components/period-tabs.js.map +1 -1
- package/dist/components/pill.cjs +3 -3
- package/dist/components/pill.cjs.map +1 -1
- package/dist/components/pill.js +3 -3
- package/dist/components/pill.js.map +1 -1
- package/dist/components/progress.cjs +1 -1
- package/dist/components/progress.cjs.map +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio.cjs +1 -1
- package/dist/components/radio.cjs.map +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/radio.js.map +1 -1
- package/dist/components/sankey-chart.cjs +1 -11
- package/dist/components/sankey-chart.cjs.map +1 -1
- package/dist/components/sankey-chart.d.cts +2 -3
- package/dist/components/sankey-chart.d.ts +2 -3
- package/dist/components/sankey-chart.js +2 -11
- package/dist/components/sankey-chart.js.map +1 -1
- package/dist/components/schedule-list.cjs +1 -1
- package/dist/components/schedule-list.js +1 -1
- package/dist/components/segmented-control.cjs +2 -2
- package/dist/components/segmented-control.cjs.map +1 -1
- package/dist/components/segmented-control.js +2 -2
- package/dist/components/segmented-control.js.map +1 -1
- package/dist/components/select.cjs +33 -3
- package/dist/components/select.cjs.map +1 -1
- package/dist/components/select.d.cts +11 -1
- package/dist/components/select.d.ts +11 -1
- package/dist/components/select.js +32 -4
- package/dist/components/select.js.map +1 -1
- package/dist/components/sheet.cjs +1 -1
- package/dist/components/sheet.js +1 -1
- package/dist/components/stepper.cjs +2 -2
- package/dist/components/stepper.cjs.map +1 -1
- package/dist/components/stepper.d.cts +4 -4
- package/dist/components/stepper.d.ts +4 -4
- package/dist/components/stepper.js +2 -2
- package/dist/components/stepper.js.map +1 -1
- package/dist/components/table.cjs +1 -1
- package/dist/components/table.js +1 -1
- package/dist/components/tabs.cjs +1 -1
- package/dist/components/tabs.js +1 -1
- package/dist/components/textarea.cjs +1 -1
- package/dist/components/textarea.js +1 -1
- package/dist/components/toast.cjs +1 -1
- package/dist/components/toast.js +1 -1
- package/dist/components/tooltip.cjs +2 -2
- package/dist/components/tooltip.cjs.map +1 -1
- package/dist/components/tooltip.js +2 -2
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/typography.cjs +1 -1
- package/dist/components/typography.cjs.map +1 -1
- package/dist/components/typography.d.cts +4 -1
- package/dist/components/typography.d.ts +4 -1
- package/dist/components/typography.js +1 -1
- package/dist/components/typography.js.map +1 -1
- package/dist/components/wizard-layout.cjs +57 -0
- package/dist/components/wizard-layout.cjs.map +1 -0
- package/dist/components/wizard-layout.d.cts +34 -0
- package/dist/components/wizard-layout.d.ts +34 -0
- package/dist/components/wizard-layout.js +50 -0
- package/dist/components/wizard-layout.js.map +1 -0
- package/dist/lib/accent-colors.cjs +4 -1
- package/dist/lib/accent-colors.cjs.map +1 -1
- package/dist/lib/accent-colors.d.cts +1 -1
- package/dist/lib/accent-colors.d.ts +1 -1
- package/dist/lib/accent-colors.js +4 -1
- package/dist/lib/accent-colors.js.map +1 -1
- package/dist/lib/accent-theme.cjs +2 -2
- package/dist/lib/accent-theme.cjs.map +1 -1
- package/dist/lib/accent-theme.js +2 -2
- package/dist/lib/accent-theme.js.map +1 -1
- package/dist/styles/index.css +1 -1
- package/dist/styles/spark-default.css +49 -314
- package/dist/styles/theme.css +52 -6
- package/dist/utils/format.cjs +0 -13
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.d.cts +1 -7
- package/dist/utils/format.d.ts +1 -7
- package/dist/utils/format.js +1 -12
- package/dist/utils/format.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/battery-progress-row.cjs +0 -135
- package/dist/components/battery-progress-row.cjs.map +0 -1
- package/dist/components/battery-progress-row.d.cts +0 -69
- package/dist/components/battery-progress-row.d.ts +0 -69
- package/dist/components/battery-progress-row.js +0 -125
- package/dist/components/battery-progress-row.js.map +0 -1
- package/dist/components/device-card.cjs.map +0 -1
- package/dist/components/device-card.js.map +0 -1
- package/dist/components/energy-bar-chart.cjs.map +0 -1
- package/dist/components/energy-bar-chart.d.cts +0 -43
- package/dist/components/energy-bar-chart.d.ts +0 -43
- package/dist/components/energy-bar-chart.js.map +0 -1
- package/dist/components/energy-breakdown.cjs +0 -80
- package/dist/components/energy-breakdown.cjs.map +0 -1
- package/dist/components/energy-breakdown.d.cts +0 -24
- package/dist/components/energy-breakdown.d.ts +0 -24
- package/dist/components/energy-breakdown.js +0 -79
- package/dist/components/energy-breakdown.js.map +0 -1
- package/dist/components/energy-history-chart.cjs +0 -63
- package/dist/components/energy-history-chart.cjs.map +0 -1
- package/dist/components/energy-history-chart.d.cts +0 -64
- package/dist/components/energy-history-chart.d.ts +0 -64
- package/dist/components/energy-history-chart.js +0 -62
- package/dist/components/energy-history-chart.js.map +0 -1
- package/dist/components/feed-in-hero.cjs.map +0 -1
- package/dist/components/feed-in-hero.d.cts +0 -32
- package/dist/components/feed-in-hero.d.ts +0 -32
- package/dist/components/feed-in-hero.js.map +0 -1
- package/dist/components/metering-row.cjs +0 -59
- package/dist/components/metering-row.cjs.map +0 -1
- package/dist/components/metering-row.d.cts +0 -31
- package/dist/components/metering-row.d.ts +0 -31
- package/dist/components/metering-row.js +0 -55
- package/dist/components/metering-row.js.map +0 -1
- package/dist/components/money-saved-block.cjs +0 -83
- package/dist/components/money-saved-block.cjs.map +0 -1
- package/dist/components/money-saved-block.d.cts +0 -55
- package/dist/components/money-saved-block.d.ts +0 -55
- package/dist/components/money-saved-block.js +0 -76
- package/dist/components/money-saved-block.js.map +0 -1
- package/dist/components/on-off-button.cjs +0 -32
- package/dist/components/on-off-button.cjs.map +0 -1
- package/dist/components/on-off-button.d.cts +0 -18
- package/dist/components/on-off-button.d.ts +0 -18
- package/dist/components/on-off-button.js +0 -31
- package/dist/components/on-off-button.js.map +0 -1
- package/dist/components/onboarding-layout.cjs +0 -67
- package/dist/components/onboarding-layout.cjs.map +0 -1
- package/dist/components/onboarding-layout.d.cts +0 -44
- package/dist/components/onboarding-layout.d.ts +0 -44
- package/dist/components/onboarding-layout.js +0 -60
- package/dist/components/onboarding-layout.js.map +0 -1
- package/dist/components/power-history-chart.cjs +0 -498
- package/dist/components/power-history-chart.cjs.map +0 -1
- package/dist/components/power-history-chart.d.cts +0 -71
- package/dist/components/power-history-chart.d.ts +0 -71
- package/dist/components/power-history-chart.js +0 -496
- package/dist/components/power-history-chart.js.map +0 -1
- package/dist/components/power-history.cjs +0 -114
- package/dist/components/power-history.cjs.map +0 -1
- package/dist/components/power-history.d.cts +0 -40
- package/dist/components/power-history.d.ts +0 -41
- package/dist/components/power-history.js +0 -114
- package/dist/components/power-history.js.map +0 -1
- package/dist/components/power-network.cjs +0 -559
- package/dist/components/power-network.cjs.map +0 -1
- package/dist/components/power-network.d.cts +0 -80
- package/dist/components/power-network.d.ts +0 -80
- package/dist/components/power-network.js +0 -553
- package/dist/components/power-network.js.map +0 -1
- package/dist/components/smart-toggle-button.cjs +0 -24
- package/dist/components/smart-toggle-button.cjs.map +0 -1
- package/dist/components/smart-toggle-button.d.cts +0 -16
- package/dist/components/smart-toggle-button.d.ts +0 -16
- package/dist/components/smart-toggle-button.js +0 -23
- package/dist/components/smart-toggle-button.js.map +0 -1
- package/dist/components/solar-forecast-chart.cjs +0 -108
- package/dist/components/solar-forecast-chart.cjs.map +0 -1
- package/dist/components/solar-forecast-chart.d.cts +0 -77
- package/dist/components/solar-forecast-chart.d.ts +0 -77
- package/dist/components/solar-forecast-chart.js +0 -103
- package/dist/components/solar-forecast-chart.js.map +0 -1
- package/dist/number-flow-Bch81ixZ.d.cts +0 -14
- package/dist/number-flow-Bch81ixZ.d.ts +0 -14
- package/dist/utils/build-sankey-nodes.cjs +0 -72
- package/dist/utils/build-sankey-nodes.cjs.map +0 -1
- package/dist/utils/build-sankey-nodes.d.cts +0 -27
- package/dist/utils/build-sankey-nodes.d.ts +0 -27
- package/dist/utils/build-sankey-nodes.js +0 -71
- package/dist/utils/build-sankey-nodes.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accent-theme.cjs","names":["RadixColors","Color","getAlphaColorSrgb","getAlphaColorP3","generateRadixColors","isSparkNeutralFamily"],"sources":["../../src/lib/accent-theme.ts"],"sourcesContent":["// Accent is the one runtime knob (colour math, not a CSS-var swap): generate the\n// `--spark-*` scale with the same Radix algorithm live portals use, apply as inline vars.\nimport * as RadixColors from '@radix-ui/colors'\nimport Color from 'colorjs.io'\nimport {\n generateRadixColors,\n getAlphaColorP3,\n getAlphaColorSrgb,\n} from './generate-radix-colors'\nimport {\n isSparkNeutralFamily,\n type SparkNeutral,\n type SparkNeutralFamily,\n} from './theme-types'\n\n/** Shape returned by the vendored Radix generator (it doesn't export a named type). */\nexport type GeneratedColors = ReturnType<typeof generateRadixColors>\n\n/**\n * generateAccentScale output: the raw scale, but with two readable foregrounds on the\n * solid (step-9) fill, each meeting a different WCAG bar:\n * - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.\n * - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).\n * A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white\n * where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark\n * accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for\n * the glyph over-darkened it (a black tick on a red/neutral checkbox).\n */\nexport type SparkAccentScale = GeneratedColors & { accentOnSolid: string }\n\nexport type Appearance = 'light' | 'dark'\n\n/** The epilot brand: the no-attribute default `--spark-*` in spark-default.css. */\nexport const DEFAULT_ACCENT = '#005EB4'\n\n/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */\nexport const CURATED_ACCENTS: Record<string, string> = {\n blue: RadixColors.blue.blue9,\n indigo: RadixColors.indigo.indigo9,\n violet: RadixColors.violet.violet9,\n purple: RadixColors.purple.purple9,\n cyan: RadixColors.cyan.cyan9,\n teal: RadixColors.teal.teal9,\n green: RadixColors.green.green9,\n grass: RadixColors.grass.grass9,\n orange: RadixColors.orange.orange9,\n red: RadixColors.red.red9,\n pink: RadixColors.pink.pink9,\n gray: RadixColors.gray.gray9,\n}\n\n/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */\nconst DEFAULTS: Record<Appearance, { gray: string; background: string }> = {\n light: { gray: '#333333', background: '#ffffff' },\n // Matches ECP DesignUtils' DEFAULT_DARK_BACKGROUND_COLOR -- the alpha ramp\n // is composited against this, so it must match what's actually painted.\n dark: { gray: '#8b8d98', background: '#000000' },\n}\n\n/** Radix step-9 per neutral family — the generator's `gray` seed, so a regenerated\n neutral lands on the family `[data-neutral-color]` would have selected statically. */\nconst NEUTRAL_BASE: Record<SparkNeutralFamily, string> = {\n slate: RadixColors.slate.slate9,\n mauve: RadixColors.mauve.mauve9,\n gray: RadixColors.gray.gray9,\n sage: RadixColors.sage.sage9,\n olive: RadixColors.olive.olive9,\n sand: RadixColors.sand.sand9,\n}\n\nexport interface GenerateScaleOptions {\n /**\n * Page canvas the scale is composited against. Drives the lightness curve of all 12\n * steps AND every alpha step (each is solved as `target = bg·(1-α) + fg·α`), so it\n * must match what's actually painted or the alpha ramp renders wrong.\n */\n background?: string\n /**\n * Neutral family, or an arbitrary color to seed the neutral scale (regenerated against\n * `background`, same as a named family). Pass a resolved value, not `'auto'` — this\n * option isn't itself typed to exclude it (an open string union can't forbid one\n * literal), but nothing in `<Theme>` ever forwards `'auto'` this far.\n */\n neutral?: SparkNeutral\n}\n\n/** Which appearance a canvas belongs to, by OKLCH lightness. */\nexport function appearanceForBackground(background: string): Appearance {\n const [lightness] = new Color(background).to('oklch').coords\n return (lightness ?? 1) >= 0.5 ? 'light' : 'dark'\n}\n\n/** deltaE2000 ≈ 2 is the just-noticeable bar — a good solve lands far inside it. */\nconst ALPHA_SOLVE_TOLERANCE = 2\n\n/**\n * OKLCH chroma below which a color reads as neutral rather than tinted — shared by every\n * \"is this canvas/accent actually chromatic\" check (getMatchingNeutral, resolveAutoNeutral,\n * accentScaleForCanvas).\n */\nconst CHROMATIC_THRESHOLD = 0.04\n\n/**\n * An alpha step past this needs to cover the canvas almost completely to reproduce its\n * target — at that point it isn't a tint anymore, and the opaque solid is both cheaper and\n * more predictable. Only applied to the accent ramp's canvas-tinted low steps (gap #12a,\n * ask 2 — specifically the steps `tintLowStepsFromCanvas` retargets; steps 6-12 are by-design\n * high-alpha ink/solid tokens even on plain white — capping *those* would be a regression, not\n * a fix). Same shape as the deltaE check below, widened from \"could not solve\" to \"solved, but\n * not usefully\".\n */\nconst ALPHA_CAP = 0.5\n\n/**\n * Replace alpha steps the solver could not actually solve — or, within `capAlphaSteps` leading\n * steps, solved only at a near-opaque alpha — with their opaque solid.\n *\n * Each `aN` is solved so that compositing it over the background reproduces step N\n * (`target = bg·(1-α) + fg·α`). From a saturated canvas a desaturated ink target is\n * unreachable within alpha bounds, and Radix's solver **degrades silently** rather than\n * failing — on `#FFD400` it returns `#0000ff39` for slate a11, which composites to\n * 1.66:1 body text where the solid step is a correct 9.03:1. Separately, even a solve that\n * *succeeds* can need alpha past `ALPHA_CAP` to get there — technically correct only over\n * that exact background, and no longer a usable \"tint\" anywhere else (gap #12a, ask 2).\n *\n * `space` must match whatever space `alpha` was actually solved in — `getAlphaColorP3` solves\n * per-channel in P3, and verifying that against an sRGB composite rejects perfectly good\n * solves (they were never trying to reproduce the sRGB composite in the first place).\n *\n * So verify the solve and fall back to the opaque value where either check fails. On\n * white/black every step solves exactly at low alpha, so the default output — and portal\n * parity — is untouched either way.\n */\nfunction opaqueWhereUnsolvable(\n background: string,\n solid: string[],\n alpha: string[],\n capAlphaSteps = 0,\n space: 'srgb' | 'p3' = 'srgb',\n): string[] {\n let bg: number[]\n try {\n bg = new Color(background).to(space).coords\n } catch {\n return alpha\n }\n return alpha.map((step, i) => {\n const target = solid[i]\n if (!target) return step\n try {\n const ink = new Color(step)\n const fg = ink.to(space).coords\n const a = ink.alpha as number\n if (i < capAlphaSteps && a > ALPHA_CAP) return target\n const composited = new Color(\n space,\n [0, 1, 2].map((k) => bg[k]! * (1 - a) + fg[k]! * a) as [\n number,\n number,\n number,\n ],\n )\n return composited.deltaE(new Color(target), '2000') <=\n ALPHA_SOLVE_TOLERANCE\n ? step\n : target\n } catch {\n return target\n }\n })\n}\n\n/**\n * Weight (0–1) each of the accent scale's first 5 steps is pulled toward the canvas's own\n * hue/chroma; step 6 and up (borders, solid, text) stay the true accent untouched.\n */\nconst CANVAS_TINT_WEIGHTS = [1, 0.8, 0.6, 0.4, 0.2]\n\n/**\n * Pull the accent scale's low steps toward the canvas's own color (gap #12a, ask 1): Radix's\n * generator sets every step's hue/chroma from the accent itself and only re-curves lightness\n * against the background, so on a chromatic canvas the low steps (app/component background\n * territory) stay a flat wash of the accent instead of a tint of the page — right where the\n * now-canvas-tinted neutral scale (gap #12) visibly disagrees with it. Step 9 (the solid,\n * brand-defining step) and everything above step 5 is left exactly as Radix generated it.\n *\n * Mixed in OKLab (rectangular a/b), not OKLCH (polar hue + chroma): a circular hue\n * interpolation between two far-apart hues (a blue accent on a yellow-green canvas, say) has\n * to sweep through whatever unrelated hues sit on the \"shorter\" arc between them — a vivid\n * green/teal/cyan that belongs to neither endpoint. Linearly mixing a/b instead moves along\n * the straight line between the two points, which naturally desaturates toward neutral as the\n * two colors pull in more different directions, rather than swinging through a third, unrelated\n * hue. Lightness (shared between OKLab and OKLCH) is untouched either way — Radix's own\n * background-aware curve already owns it.\n *\n * Takes/returns `Color` objects rather than strings so callers can reuse the same tinted\n * coordinates for both the sRGB and wide-gamut representations without a lossy round trip.\n */\nfunction tintLowStepsFromCanvas(\n solidScale: readonly Color[],\n bg: Color,\n): Color[] {\n const [, bgA, bgB] = bg.to('oklab').coords\n return solidScale.map((step, i) => {\n const weight = CANVAS_TINT_WEIGHTS[i]\n if (!weight) return step\n const [lightness, a, b] = step.to('oklab').coords\n return new Color('oklab', [\n lightness ?? 0,\n (a ?? 0) * (1 - weight) + (bgA ?? 0) * weight,\n (b ?? 0) * (1 - weight) + (bgB ?? 0) * weight,\n ])\n })\n}\n\n/** Mirrors generate-radix-colors.tsx's private toOklchString (not exported by the vendored file). */\nfunction toOklchWideGamutString(color: Color): string {\n const L = +((color.coords[0] ?? 0) * 100).toFixed(1)\n return color\n .to('oklch')\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${L}%$2`)\n}\n\n/** Same fixed alpha the vendored generator uses for `accentSurface` — a stylistic choice, not a solve. */\nfunction surfaceAlphaFor(appearance: Appearance): number {\n return appearance === 'light' ? 0.8 : 0.5\n}\n\n/**\n * Canvas-tinted low steps for the accent scale, or the untouched scale (and `tinted: false`)\n * when the canvas isn't chromatic enough to bother (gap #12a, ask 1). Only indices\n * `0..CANVAS_TINT_WEIGHTS.length-1` are ever replaced — steps 6-12 come back byte-identical to\n * what Radix generated, in both representations (no lossy sRGB round trip for the untouched\n * wide-gamut steps).\n *\n * `tinted` tells the caller whether to scope `opaqueWhereUnsolvable`'s alpha cap in at all —\n * the cap only makes sense for steps this function actually retargeted; a caller that applied\n * it unconditionally on any custom `background` would wrongly force perfectly fine low-alpha\n * steps opaque on an *achromatic* custom background (an explicit `#000000`/`#f5f5f5`, say),\n * where nothing here ever ran.\n *\n * Re-solves alpha for the tinted indices too: the generator's own `accentScaleAlpha` was\n * solved against the *pre-tint* solid, so once that solid moves the old alpha value no longer\n * reproduces it — left alone, every tinted step would fail `opaqueWhereUnsolvable`'s deltaE\n * check and go opaque, defeating the point (the `soft`-card fill this exists for would never\n * actually be translucent on a tinted canvas). Reuses Radix's own alpha solver so the result\n * matches what the generator itself would have produced had it been given this target directly.\n *\n * `accentSurface` (the `surface` card variant's fill) gets the same treatment: the generator\n * computes it as a fixed-alpha composite of step 2 — index 1, always tinted — over the\n * background, so recomputing it from the *tinted* step 2 at that same fixed alpha (0.8 light,\n * 0.5 dark; not a solve) keeps it in sync with `soft`'s fill instead of staying the old,\n * untinted wash (review-findings.md #13).\n */\nfunction accentScaleForCanvas(\n scale: GeneratedColors,\n background: string,\n appearance: Appearance,\n): Pick<\n GeneratedColors,\n | 'accentScale'\n | 'accentScaleWideGamut'\n | 'accentScaleAlpha'\n | 'accentScaleAlphaWideGamut'\n | 'accentSurface'\n | 'accentSurfaceWideGamut'\n> & { tinted: boolean } {\n try {\n const bg = new Color(background).to('oklch')\n const [, bgChroma] = bg.coords\n if (!bgChroma || bgChroma < CHROMATIC_THRESHOLD)\n return { ...scale, tinted: false }\n const tinted = tintLowStepsFromCanvas(\n scale.accentScale.map((hex) => new Color(hex).to('oklch')),\n bg,\n )\n const accentScale = scale.accentScale.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? tinted[i]!.to('srgb').toString({ format: 'hex' })\n : original,\n ) as typeof scale.accentScale\n const accentScaleWideGamut = scale.accentScaleWideGamut.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i] ? toOklchWideGamutString(tinted[i]!) : original,\n ) as typeof scale.accentScaleWideGamut\n const accentScaleAlpha = scale.accentScaleAlpha.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorSrgb(accentScale[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlpha\n const accentScaleAlphaWideGamut = scale.accentScaleAlphaWideGamut.map(\n (original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorP3(accentScaleWideGamut[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlphaWideGamut\n const surfaceAlpha = surfaceAlphaFor(appearance)\n const accentSurface = getAlphaColorSrgb(\n accentScale[1]!,\n scale.background,\n surfaceAlpha,\n )\n const accentSurfaceWideGamut = getAlphaColorP3(\n accentScaleWideGamut[1]!,\n scale.background,\n surfaceAlpha,\n )\n return {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha,\n accentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted: true,\n }\n } catch {\n return { ...scale, tinted: false }\n }\n}\n\n/** Build the Radix-style scale for one appearance from a single accent color. */\nfunction buildScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): GeneratedColors {\n const background = opts?.background ?? DEFAULTS[appearance].background\n const scale = generateRadixColors({\n appearance,\n accent,\n gray: opts?.neutral\n ? isSparkNeutralFamily(opts.neutral)\n ? NEUTRAL_BASE[opts.neutral]\n : opts.neutral\n : DEFAULTS[appearance].gray,\n background,\n })\n const {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha: canvasAccentScaleAlpha,\n accentScaleAlphaWideGamut: canvasAccentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted,\n } = opts?.background\n ? accentScaleForCanvas(scale, background, appearance)\n : { ...scale, tinted: false }\n const capAlphaSteps = tinted ? CANVAS_TINT_WEIGHTS.length : 0\n const solved = opts?.background\n ? {\n ...scale,\n accentScale,\n accentScaleWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n accentScaleAlpha: opaqueWhereUnsolvable(\n background,\n accentScale,\n canvasAccentScaleAlpha,\n capAlphaSteps,\n ) as typeof scale.accentScaleAlpha,\n accentScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n accentScaleWideGamut,\n canvasAccentScaleAlphaWideGamut,\n capAlphaSteps,\n 'p3',\n ) as typeof scale.accentScaleAlphaWideGamut,\n grayScaleAlpha: opaqueWhereUnsolvable(\n background,\n scale.grayScale,\n scale.grayScaleAlpha,\n ) as typeof scale.grayScaleAlpha,\n grayScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n 0,\n 'p3',\n ) as typeof scale.grayScaleAlphaWideGamut,\n }\n : scale\n return solved\n}\n\n/**\n * Generate the Radix-style accent scale for one appearance from a single accent color.\n *\n * Solid-fill inks come from `readableContrast`, which clears WCAG but can only reach an\n * unreadable APCA on a light, saturated step 9 (a near-black tint at Lc 33-44). Where it\n * does, the fill is darkened until plain white clears 4.5:1 and the scale is re-seeded\n * from that fill — see `darkenForWhiteInk`.\n */\nexport function generateAccentScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): SparkAccentScale {\n const solved = withNativeInks(buildScale(accent, appearance, opts))\n const fill = solved.accentScale[8]!\n if (apcaOn(fill, solved.accentContrast) >= MIN_INK_APCA)\n return withReadableTintInk(solved)\n\n // Re-seed rather than patch step 9: steps 10-12 are derived from it by a fixed\n // lightness offset, so moving 9 alone leaves hover lighter than the fill.\n const reseeded = buildScale(darkenForWhiteInk(fill), appearance, opts)\n const reseededFill = reseeded.accentScale[8]!\n if (contrastOn(reseededFill, WHITE) < MIN_TEXT_CONTRAST)\n return withReadableTintInk(solved)\n return withReadableTintInk({\n ...reseeded,\n accentContrast: WHITE,\n accentOnSolid: WHITE,\n })\n}\n\n/**\n * Step 11 is the ink of every non-solid style, read on the a3 fill and the a4 hover. Both\n * are translucent, so on a soft card (neutral-a3 ≈ neutral-3 over the canvas) they\n * composite darker than on the canvas and Radix's step 11 drops under 4.5:1 (red 3.72:1).\n * Move step 11 away from the fill, holding chroma and hue, until a11 on a4 clears 4.5:1\n * over every neutral knob's step 3, and re-solve a11 from it. Unchanged when it clears.\n */\nexport function withReadableTintInk<T extends GeneratedColors>(scale: T): T {\n const canvas = scale.background\n const dark = appearanceForBackground(canvas) === 'dark'\n const neutral3s = [\n scale.grayScale[2]!,\n ...Object.keys(NEUTRAL_BASE).map((family) => {\n const steps = RadixColors[\n (dark ? `${family}Dark` : family) as keyof typeof RadixColors\n ] as Record<string, string>\n return steps[`${family}3`]!\n }),\n ]\n const hoverFills = neutral3s.map((page) =>\n composite(scale.accentScaleAlpha[3]!, page),\n )\n const inkOn = (step11: string) => {\n const ink = getAlphaColorSrgb(step11, canvas)\n return Math.min(\n ...hoverFills.map((fill) => contrastOn(fill, composite(ink, fill))),\n )\n }\n const original = scale.accentScale[10]!\n if (inkOn(original) >= MIN_TEXT_CONTRAST) return scale\n\n const [lightness, chroma, hue] = new Color(original).to('oklch').coords\n const [, wideChroma, wideHue] = new Color(scale.accentScaleWideGamut[10]!).to(\n 'oklch',\n ).coords\n const toward = contrastOn(canvas, '#000') > contrastOn(canvas, '#fff') ? 0 : 1\n const srgbAt = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (inkOn(srgbAt(mid)) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n const step11 = srgbAt(passes)\n const wideStep11 = toOklchWideGamutString(\n new Color('oklch', [passes, wideChroma ?? 0, wideHue ?? 0]).toGamut({\n space: 'p3',\n }),\n )\n const withStep = <A extends readonly string[]>(steps: A, value: string) =>\n steps.map((v, i) => (i === 10 ? value : v)) as unknown as A\n return {\n ...scale,\n accentScale: withStep(scale.accentScale, step11),\n accentScaleWideGamut: withStep(scale.accentScaleWideGamut, wideStep11),\n accentScaleAlpha: withStep(\n scale.accentScaleAlpha,\n getAlphaColorSrgb(step11, canvas),\n ),\n accentScaleAlphaWideGamut: withStep(\n scale.accentScaleAlphaWideGamut,\n getAlphaColorP3(wideStep11, canvas),\n ),\n }\n}\n\n// Rounds each 8-bit channel term the way browsers blend (see blendAlpha in the vendored\n// generator); the solve lands on the 4.5:1 boundary, where unrounded math is off by ~0.01.\nconst composite = (top: string, bottom: string) => {\n const fg = new Color(top).to('srgb')\n const bg = new Color(bottom).to('srgb').coords\n const a = fg.alpha as number\n const channel = (k: number) =>\n (Math.round(bg[k]! * 255 * (1 - a)) + Math.round(fg.coords[k]! * 255 * a)) /\n 255\n return new Color('srgb', [channel(0), channel(1), channel(2)]).toString({\n format: 'hex',\n })\n}\n\n// accentContrast → 4.5:1 (button text); accentOnSolid → 3:1 (graphical glyph,\n// see SparkAccentScale). Same picker, different WCAG threshold.\nfunction withNativeInks(scale: GeneratedColors): SparkAccentScale {\n const fill = scale.accentScale[8]!\n return {\n ...scale,\n accentContrast: readableContrast(\n fill,\n scale.accentContrast,\n MIN_TEXT_CONTRAST,\n ),\n accentOnSolid: readableContrast(\n fill,\n scale.accentContrast,\n MIN_GRAPHIC_CONTRAST,\n ),\n }\n}\n\n/**\n * Darkest the fill needs to be for white text to clear 4.5:1, holding chroma and hue.\n * Returns the fill unchanged when white already clears — the generator picks its ink\n * from `Math.abs(white.contrastAPCA(fill))`, whose arguments are transposed, so it can\n * reach for a dark tint on a fill white already handles.\n */\nfunction darkenForWhiteInk(fillHex: string): string {\n const fill = new Color(fillHex)\n if (contrastOn(fillHex, WHITE) >= MIN_TEXT_CONTRAST) return fillHex\n const [lightness, chroma, hue] = fill.to('oklch').coords\n const darkened = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (contrastOn(darkened(mid), WHITE) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n return darkened(passes)\n}\n\nconst WHITE = '#fff' // matches the generator's own native contrast literal\nconst MIN_TEXT_CONTRAST = 4.5 // WCAG 1.4.3 — button / label text\nconst MIN_GRAPHIC_CONTRAST = 3 // WCAG 1.4.11 — a check / indicator glyph on a solid fill\n// APCA's floor for large headlines. WCAG 4.5:1 is satisfiable on any step 9 by a dark\n// enough tint, but reads as low as Lc 33 — worse than the white it replaced (Lc 66).\nconst MIN_INK_APCA = 45\n\nconst contrastOn = (bgHex: string, fgHex: string) =>\n new Color(fgHex).contrast(new Color(bgHex), 'WCAG21')\n// Color.js takes the background as the receiver; APCA is polarity-dependent, so the\n// order matters (unlike WCAG 2.x).\nconst apcaOn = (bgHex: string, fgHex: string) =>\n Math.abs(new Color(bgHex).contrastAPCA(new Color(fgHex)))\n\n// Pick the readable foreground on step 9: prefer the generator's native contrast\n// (usually white), else fall to progressively darker accent-tinted inks until one\n// clears `min`. Text uses 4.5:1; a graphical glyph (the checkmark) uses 3:1 — which\n// keeps white on most accents (incl. neutral 3.32:1, red 3.91:1) but flips to a dark\n// tint where white is too weak (e.g. orange, 2.97:1). The vendored generator keeps\n// white at APCA Lc >= 40, which can sit below both WCAG thresholds — hence this guard.\nfunction readableContrast(\n step9Hex: string,\n native: string,\n min: number,\n): string {\n const step9 = new Color(step9Hex)\n const [, chroma, hue] = step9.to('oklch').coords\n const tinted = (lightness: number) =>\n new Color('oklch', [\n lightness,\n Math.max(0.08 * (chroma ?? 0), 0.04),\n hue ?? 0,\n ])\n .to('srgb')\n .toString({ format: 'hex' })\n const ratioOf = (hex: string) => new Color(hex).contrast(step9, 'WCAG21')\n const candidates = [native, tinted(0.25), tinted(0.15)]\n const ratios = candidates.map(ratioOf)\n const passing = ratios.findIndex((r) => r >= min)\n if (passing !== -1) return candidates[passing]!\n\n // The ladder above is a *preference* (least extreme ink first), not a search — it\n // stops at 0.15 and so missed accents that only clear `min` a little darker still\n // (#936c89 landed at 4.47 when 0.05 gives 4.73). Contrast rises monotonically as\n // the ink's lightness moves away from the fill's, so search outward from step 9\n // toward whichever extreme is stronger: if a passing ink exists, this finds it.\n const lightness9 = step9.to('oklch').coords[0] ?? 0.5\n const toward = ratioOf(tinted(0)) >= ratioOf(tinted(1)) ? 0 : 1\n let fails = lightness9\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioOf(tinted(mid)) >= min) passes = mid\n else fails = mid\n }\n const found = tinted(passes)\n if (ratioOf(found) >= min) return found\n\n // No ink of any lightness clears `min` on this fill — step 9 itself is too\n // mid-toned. Return the strongest available; the palette sweep flags the accent.\n const all = [...candidates, tinted(toward)]\n const allRatios = all.map(ratioOf)\n return all[allRatios.indexOf(Math.max(...allRatios))]!\n}\n\n// Spark's own `--red-a4`, not Radix's; spark-default-parity.test.ts pins it to the stylesheet.\nexport const RED_A4: Record<Appearance, string> = {\n light: '#ff25002a',\n dark: '#ff00005a',\n}\n\n/**\n * The hover tints a static `--neutral-placeholder` must clear (field.tsx): gray\n * (`neutralA4`), invalid (`--red-a4`) and brand (`--spark-a4`) for the default accent and\n * every curated one. A runtime brand outside these is not covered.\n */\nexport function staticFieldHoverTints(\n appearance: Appearance,\n neutralA4: string,\n): string[] {\n return [\n neutralA4,\n RED_A4[appearance],\n ...[DEFAULT_ACCENT, ...Object.values(CURATED_ACCENTS)].map(\n (accent) => generateAccentScale(accent, appearance).accentScaleAlpha[3]!,\n ),\n ]\n}\n\n// 4.5 plus headroom for the 8-bit rounding of the painted fill, which moves the ratio ~0.01.\nconst PLACEHOLDER_CONTRAST = 4.55\n\nfunction compositeOver(background: Color, color: string): Color {\n const ink = new Color(color).to('srgb')\n const alpha = ink.alpha as number\n const bg = background.to('srgb').coords\n return new Color(\n 'srgb',\n [0, 1, 2].map((k) => bg[k]! * (1 - alpha) + ink.coords[k]! * alpha) as [\n number,\n number,\n number,\n ],\n )\n}\n\n/**\n * The lightest color between neutral a10 and a11 that reaches `PLACEHOLDER_CONTRAST` on a\n * `card` over `page`, bare and under each of `tints`, clamped to a11, as an sRGB alpha step\n * over `page` (opaque where the alpha solve cannot reproduce it). The card is the deepest\n * surface a field sits on, so clearing it clears the page too.\n */\nexport function solvePlaceholder({\n a10,\n a11,\n page,\n card,\n tints,\n}: {\n a10: string\n a11: string\n page: string\n card: string\n tints: string[]\n}): string {\n const bg = new Color(page)\n const onCard = compositeOver(bg, card)\n const fills = [onCard, ...tints.map((tint) => compositeOver(onCard, tint))]\n const from = compositeOver(bg, a10)\n const to = compositeOver(bg, a11)\n const at = (t: number) => from.mix(to, t, { space: 'oklch' }).to('srgb')\n const inkAt = (t: number) => {\n const solid = at(t).toString({ format: 'hex' })\n return opaqueWhereUnsolvable(\n page,\n [solid],\n [getAlphaColorSrgb(solid, page)],\n )[0]!\n }\n const passes = (t: number) =>\n fills.every(\n (fill) =>\n compositeOver(fill, inkAt(t)).contrast(fill, 'WCAG21') >=\n PLACEHOLDER_CONTRAST,\n )\n let found = 1\n if (passes(0)) found = 0\n else if (passes(1)) {\n let fails = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + found) / 2\n if (passes(mid)) found = mid\n else fails = mid\n }\n }\n return inkAt(found)\n}\n\n/** sRGB `--spark-*` declarations for a generated accent scale. */\nexport function accentSparkVars(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScale,\n scale.accentScaleAlpha,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurface,\n scale.background,\n )\n}\n\n/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */\nexport function accentSparkVarsP3(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScaleWideGamut,\n scale.accentScaleAlphaWideGamut,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurfaceWideGamut,\n scale.background,\n )\n}\n\n/** Radix's `grayColor=\"auto\"` pairing (accent family → gray), mapped to our 6 neutrals. */\nconst NEUTRAL_BY_ACCENT: Record<string, SparkNeutralFamily> = {\n blue: 'slate',\n indigo: 'slate',\n cyan: 'slate',\n violet: 'mauve',\n purple: 'mauve',\n pink: 'mauve',\n red: 'mauve',\n teal: 'sage',\n green: 'sage',\n grass: 'olive',\n orange: 'sand',\n gray: 'gray',\n}\n\n/** Radix's \"auto\" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */\nexport function getMatchingNeutral(accent: string): SparkNeutralFamily {\n const byName = NEUTRAL_BY_ACCENT[accent]\n if (byName) return byName\n try {\n const [, chroma, hueRaw] = new Color(accent).to('oklch').coords\n if (!chroma || chroma < CHROMATIC_THRESHOLD) return 'gray'\n const hue = (((hueRaw ?? 0) % 360) + 360) % 360\n if (hue >= 300 || hue < 35) return 'mauve'\n if (hue < 115) return 'sand'\n if (hue < 145) return 'olive'\n if (hue < 200) return 'sage'\n return 'slate'\n } catch {\n return 'slate'\n }\n}\n\n/**\n * Fill steps (Radix roles 1-5), measured from the surface toward the *scrim* — the pole\n * opposite the ink. Magnitudes are unchanged from the original ink-directed ramp; only\n * the direction flips, so a fill and the text on it move apart instead of together.\n */\nconst INVERTED_FILL_RAMP = [0.06, 0.12, 0.2, 0.3, 0.4]\n\n/** Strongest border step (8, the hover/active edge). 6 is solved per accent; 7 splits them. */\nconst INVERTED_BORDER_TOP = 0.82\n\n/**\n * Ink steps (9-12), measured from the surface toward the ink. Step 9 (the solid fill)\n * lands on the ink itself; 10 eases back for hover; 11-12 are full-strength ink.\n */\nconst INVERTED_INK_RAMP = [1, 0.88, 1, 1]\n\n/**\n * Lowest alpha at which the ink, laid over the surface, is 3:1 distinguishable from it\n * (WCAG 1.4.11) — the floor for a border that has to read as an edge at all.\n *\n * Solved rather than hardcoded because it moves with the accent (0.57-0.72 across the\n * curated set). Contrast against the surface rises monotonically with `t`, so bisection\n * finds the boundary.\n *\n * Targets 1% above the floor rather than landing on it: the step ships as `#rrggbbaa`, and\n * 8-bit rounding on any of the four channels can drop an exact solve back under 3:1.\n */\nfunction solveBorderFloor(surface: Color, ink: Color): number {\n const target = MIN_GRAPHIC_CONTRAST * 1.01\n const ratioAt = (t: number) =>\n new Color(Color.mix(surface, ink, t, { space: 'srgb' })).contrast(\n surface,\n 'WCAG21',\n )\n let fails = 0\n let passes = 1\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioAt(mid) >= target) passes = mid\n else fails = mid\n }\n return passes\n}\n\n/** OKLCH string in the wide-gamut format the generator emits (`oklch(96.1% 0.0155 254.2)`). */\nfunction oklchString(color: Color): string {\n const oklch = color.to('oklch')\n const lightness = +((oklch.coords[0] ?? 0) * 100).toFixed(1)\n return oklch\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${lightness}%$2`)\n}\n\n/**\n * Re-cast a scale for use ON its own solid (step-9) fill — the `inverted` knob.\n *\n * A solid accent control on a solid accent surface is invisible: both paint `--spark-9`.\n * Inverting swaps that pair — step 9 and its contrast ink trade places — so the solid fill\n * and its text are contrast-safe **by construction, for any accent**: `accentContrast` is\n * already the WCAG-checked readable ink for step 9 (`readableContrast`).\n *\n * The non-solid styles need a second idea, because the ink is also the text colour. A fill\n * blended surface→ink moves the background *toward* the foreground, so ink-on-fill contrast\n * falls monotonically with `t` — and measured across the curated set, the highest `t` that\n * still clears 4.5:1 (0.174) sits below the lowest `t` that is 3:1 distinguishable from the\n * surface (0.543). On that line no fill can be both legible and visible, on any accent.\n *\n * So fills blend the other way, toward the **scrim** — the lightness pole opposite the ink,\n * carrying the surface's own hue. Contrast against the ink now *rises* with `t`, which puts\n * every non-solid style at or above the solid pair's ratio for free. Borders keep stepping\n * toward the ink (a scrim-side border caps at ~2:1 on light accents like orange, so it can\n * never clear 1.4.11), starting at the solved 3:1 floor.\n *\n * Appearance-stable on purpose. The generator pins step 9 to the accent itself, so the\n * surface is identical in light and dark — which makes the whole inverted scale identical\n * too, and `[data-inverted]` needs no `.dark` twin.\n *\n * Two things this is deliberately NOT:\n * - **A positional mirror** (`1↔12`). It leaves the solid fill only 2.04:1 from the card in\n * dark and 2.23:1 on a light accent like orange — under the 3:1 graphical floor, i.e. still\n * invisible, which is the bug this exists to fix.\n * - **A regenerated on-surface ramp.** Feeding the surface back in as `background` produces\n * off-hue overlays (a warm wash on a blue card) and ink at 3.35:1.\n *\n * Values are literals, not `var(--spark-N)` aliases: a scale can't re-point to itself\n * (`--spark-1: var(--spark-12)` next to `--spark-12: var(--spark-1)` is a cycle, and CSS\n * discards both).\n */\nexport function invertAccentScale(scale: SparkAccentScale): SparkAccentScale {\n const surface = scale.accentScale[8]! // the solid fill this scale will sit on\n const ink = scale.accentContrast // already AA-checked against that fill\n const surfaceColor = new Color(surface)\n const inkColor = new Color(ink)\n\n // The scrim: opposite the ink in lightness, tinted with the surface's own hue by the\n // same rule readableContrast uses for its ink ladder. Derived from the surface, so this\n // introduces no new palette input.\n const [surfaceLightness, surfaceChroma, surfaceHue] =\n surfaceColor.to('oklch').coords\n const inkLightness = inkColor.to('oklch').coords[0] ?? 0\n const scrimColor = new Color('oklch', [\n inkLightness > (surfaceLightness ?? 0) ? 0.12 : 0.98,\n Math.max(0.08 * (surfaceChroma ?? 0), 0.02),\n surfaceHue ?? 0,\n ])\n\n const borderFloor = solveBorderFloor(surfaceColor, inkColor)\n const ramp: { toward: Color; t: number }[] = [\n ...INVERTED_FILL_RAMP.map((t) => ({ toward: scrimColor, t })),\n { toward: inkColor, t: borderFloor },\n { toward: inkColor, t: (borderFloor + INVERTED_BORDER_TOP) / 2 },\n { toward: inkColor, t: INVERTED_BORDER_TOP },\n ...INVERTED_INK_RAMP.map((t) => ({ toward: inkColor, t })),\n ]\n\n // sRGB, not OKLab: the browser composites `--spark-aN` as source-over in gamma sRGB, so\n // mixing the opaque twin in any other space makes `--spark-N` a different colour from it\n // (measured up to dE2000 2.8 on the old OKLab mix, over ALPHA_SOLVE_TOLERANCE).\n const blend = ({ toward, t }: { toward: Color; t: number }) =>\n new Color(Color.mix(surfaceColor, toward, t, { space: 'srgb' }))\n const steps = ramp.map((s) =>\n blend(s).to('srgb').toString({ format: 'hex' }),\n ) as typeof scale.accentScale\n const stepsWideGamut = ramp.map((s) =>\n oklchString(blend(s)),\n ) as typeof scale.accentScaleWideGamut\n // Mixing surface→X at t in sRGB is the same colour as X at alpha t over the surface, so\n // each alpha step is just its own pole at rising opacity — no solve needed.\n const alpha = ramp.map(({ toward, t }) => {\n const translucent = toward.clone()\n translucent.alpha = t\n return translucent.to('srgb').toString({ format: 'hex' })\n }) as typeof scale.accentScaleAlpha\n const alphaWideGamut = ramp.map(({ toward, t }) => {\n const translucent = toward.clone().to('p3')\n translucent.alpha = t\n return translucent\n .toString({ precision: 4 })\n .replace('color(p3 ', 'color(display-p3 ')\n }) as typeof scale.accentScaleAlphaWideGamut\n return {\n ...scale,\n accentScale: steps,\n accentScaleWideGamut: stepsWideGamut,\n accentScaleAlpha: alpha,\n accentScaleAlphaWideGamut: alphaWideGamut,\n // Ink on the inverted solid fill is the accent itself — the other half of the swapped pair.\n accentContrast: surface,\n accentOnSolid: surface,\n accentSurface: steps[1]!,\n accentSurfaceWideGamut: stepsWideGamut[1]!,\n }\n}\n\n/**\n * `neutralColor=\"auto\"` resolution: prefer the canvas's own tint when it has one — a\n * chromatic `background` seeds a visibly warmer/cooler ramp than bucketing it into one\n * of the 6 named families would (see gap #12). Falls back to accent-matched bucketing\n * when there's no tinted canvas to pull from.\n */\nexport function resolveAutoNeutral(\n accent: string,\n background?: string,\n): string {\n if (background) {\n try {\n const [, chroma] = new Color(background).to('oklch').coords\n if (chroma && chroma >= CHROMATIC_THRESHOLD) return background\n } catch {\n // fall through to accent-matched neutral\n }\n }\n return getMatchingNeutral(accent)\n}\n\n/**\n * Whether a string parses as a color at all — for validating a custom `neutralColor`\n * before it reaches the generator. An unparseable seed (e.g. a typo'd family name like\n * `\"slat\"`) would otherwise throw deep inside `generateRadixColors`, taking the whole\n * accent scale down with it via the caller's shared try/catch.\n */\nexport function isParsableColor(value: string): boolean {\n try {\n new Color(value).to('oklch')\n return true\n } catch {\n return false\n }\n}\n\n/** The 27 tokens every scale family declares, under an arbitrary prefix. */\nfunction scaleVars(\n prefix: string,\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n): string[] {\n return [\n ...steps.map((c, i) => `--${prefix}-${i + 1}: ${c};`),\n ...alpha.map((c, i) => `--${prefix}-a${i + 1}: ${c};`),\n `--${prefix}-contrast: ${contrast};`,\n `--${prefix}-on-solid: ${onSolid};`,\n `--${prefix}-surface: ${surface};`,\n ]\n}\n\nfunction sparkVars(\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n background: string,\n): string[] {\n const indicator = steps[8]! // step 9 — the brand step (matches --spark-indicator/-track)\n return [\n ...scaleVars('spark', steps, alpha, contrast, onSolid, surface),\n `--spark-background: ${background};`,\n `--spark-indicator: ${indicator};`,\n `--spark-track: ${indicator};`,\n ]\n}\n\n/**\n * The same `--neutral-*` tokens `neutralSparkVars` emits, pointed back at a static Radix\n * family instead of a generated scale — the counterpart for an appearance that has *no*\n * custom canvas of its own.\n *\n * Ambient (no explicit `appearance`) mode emits both appearances' scales into one unlayered\n * `<style>`, but a custom canvas only regenerates neutrals for the appearance it belongs to.\n * Left undeclared, the other block doesn't fall back — nothing resets a custom property, so\n * its sibling's canvas-tinted values simply stay in effect there, and being unlayered they\n * outrank `[data-neutral-color]`'s own `@layer base` mapping too. Declaring the same token\n * set closes that. `var(--<family>-*)` is itself appearance-aware (Radix ships the dark\n * scales under `.dark`), so one declaration resolves correctly wherever the block applies.\n */\nexport function neutralFamilyVars(\n family: Exclude<SparkNeutral, 'auto'>,\n): string[] {\n const ref = (suffix: string) => `var(--${family}-${suffix})`\n return [\n ...scaleVars(\n 'neutral',\n Array.from({ length: 12 }, (_, i) => ref(`${i + 1}`)),\n Array.from({ length: 12 }, (_, i) => ref(`a${i + 1}`)),\n // The companion tokens' own static defaults, from spark-default.css's .light/.dark.\n // `-contrast` is per-family there so [data-neutral-color] swaps the ink with the\n // ramp; referencing it rather than inlining #fff keeps the CSS the single source\n // of truth if the two appearances ever diverge again.\n `var(--${family}-contrast)`,\n '#fff',\n 'var(--neutral-2)',\n ),\n `--neutral-placeholder: ${ref('placeholder')};`,\n ]\n}\n\n// Emitted as sRGB on the P3 path too; the solve is a WCAG ratio, which is defined in sRGB.\nconst neutralPlaceholderVar = (scale: SparkAccentScale) =>\n `--neutral-placeholder: ${solvePlaceholder({\n a10: scale.grayScaleAlpha[9]!,\n a11: scale.grayScaleAlpha[10]!,\n page: scale.background,\n card: scale.grayScale[2]!,\n tints: [\n scale.grayScaleAlpha[3]!,\n RED_A4[appearanceForBackground(scale.background)],\n scale.accentScaleAlpha[3]!,\n ],\n })};`\n\n/**\n * `--neutral-*` declarations from the gray scale the generator produces alongside the\n * accent — normally discarded, because `[data-neutral-color]` points `--neutral-*` at a\n * static Radix family. Worth consuming only when the canvas is custom: the static\n * families' alpha steps are pre-baked against white/black, so on any other background\n * they composite wrong (every gray text/border reads through `--neutral-a*`).\n *\n * `--neutral-surface` is step 2, matching the static families' own convention rather\n * than the generator's translucent `graySurface`.\n */\nexport function neutralSparkVars(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScale,\n scale.grayScaleAlpha,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScale[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n\n/** Wide-gamut (P3) twin of `neutralSparkVars`. */\nexport function neutralSparkVarsP3(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScaleWideGamut[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"accent-theme.cjs","names":["RadixColors","Color","getAlphaColorSrgb","getAlphaColorP3","generateRadixColors","isSparkNeutralFamily"],"sources":["../../src/lib/accent-theme.ts"],"sourcesContent":["// Accent is the one runtime knob (colour math, not a CSS-var swap): generate the\n// `--spark-*` scale with the same Radix algorithm live portals use, apply as inline vars.\nimport * as RadixColors from '@radix-ui/colors'\nimport Color from 'colorjs.io'\nimport {\n generateRadixColors,\n getAlphaColorP3,\n getAlphaColorSrgb,\n} from './generate-radix-colors'\nimport {\n isSparkNeutralFamily,\n type SparkNeutral,\n type SparkNeutralFamily,\n} from './theme-types'\n\n/** Shape returned by the vendored Radix generator (it doesn't export a named type). */\nexport type GeneratedColors = ReturnType<typeof generateRadixColors>\n\n/**\n * generateAccentScale output: the raw scale, but with two readable foregrounds on the\n * solid (step-9) fill, each meeting a different WCAG bar:\n * - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.\n * - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).\n * A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white\n * where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark\n * accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for\n * the glyph over-darkened it (a black tick on a red/neutral checkbox).\n */\nexport type SparkAccentScale = GeneratedColors & { accentOnSolid: string }\n\nexport type Appearance = 'light' | 'dark'\n\n/** The epilot brand: the no-attribute default `--spark-*` in spark-default.css. */\nexport const DEFAULT_ACCENT = '#005EB4'\n\n/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */\nexport const CURATED_ACCENTS: Record<string, string> = {\n blue: RadixColors.blue.blue9,\n indigo: RadixColors.indigo.indigo9,\n violet: RadixColors.violet.violet9,\n purple: RadixColors.purple.purple9,\n cyan: RadixColors.cyan.cyan9,\n teal: RadixColors.teal.teal9,\n green: RadixColors.green.green9,\n grass: RadixColors.grass.grass9,\n orange: RadixColors.orange.orange9,\n red: RadixColors.red.red9,\n pink: RadixColors.pink.pink9,\n gray: RadixColors.gray.gray9,\n}\n\n/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */\nconst DEFAULTS: Record<Appearance, { gray: string; background: string }> = {\n light: { gray: '#333333', background: '#ffffff' },\n // Matches ECP DesignUtils' DEFAULT_DARK_BACKGROUND_COLOR -- the alpha ramp\n // is composited against this, so it must match what's actually painted.\n dark: { gray: '#8b8d98', background: '#000000' },\n}\n\n/** Radix step-9 per neutral family — the generator's `gray` seed, so a regenerated\n neutral lands on the family `[data-neutral-color]` would have selected statically. */\nconst NEUTRAL_BASE: Record<SparkNeutralFamily, string> = {\n slate: RadixColors.slate.slate9,\n mauve: RadixColors.mauve.mauve9,\n gray: RadixColors.gray.gray9,\n sage: RadixColors.sage.sage9,\n olive: RadixColors.olive.olive9,\n sand: RadixColors.sand.sand9,\n}\n\nexport interface GenerateScaleOptions {\n /**\n * Page canvas the scale is composited against. Drives the lightness curve of all 12\n * steps AND every alpha step (each is solved as `target = bg·(1-α) + fg·α`), so it\n * must match what's actually painted or the alpha ramp renders wrong.\n */\n background?: string\n /**\n * Neutral family, or an arbitrary color to seed the neutral scale (regenerated against\n * `background`, same as a named family). Pass a resolved value, not `'auto'` — this\n * option isn't itself typed to exclude it (an open string union can't forbid one\n * literal), but nothing in `<Theme>` ever forwards `'auto'` this far.\n */\n neutral?: SparkNeutral\n}\n\n/** Which appearance a canvas belongs to, by OKLCH lightness. */\nexport function appearanceForBackground(background: string): Appearance {\n const [lightness] = new Color(background).to('oklch').coords\n return (lightness ?? 1) >= 0.5 ? 'light' : 'dark'\n}\n\n/** deltaE2000 ≈ 2 is the just-noticeable bar — a good solve lands far inside it. */\nconst ALPHA_SOLVE_TOLERANCE = 2\n\n/**\n * OKLCH chroma below which a color reads as neutral rather than tinted — shared by every\n * \"is this canvas/accent actually chromatic\" check (getMatchingNeutral, resolveAutoNeutral,\n * accentScaleForCanvas).\n */\nconst CHROMATIC_THRESHOLD = 0.04\n\n/**\n * An alpha step past this needs to cover the canvas almost completely to reproduce its\n * target — at that point it isn't a tint anymore, and the opaque solid is both cheaper and\n * more predictable. Only applied to the accent ramp's canvas-tinted low steps (gap #12a,\n * ask 2 — specifically the steps `tintLowStepsFromCanvas` retargets; steps 6-12 are by-design\n * high-alpha ink/solid tokens even on plain white — capping *those* would be a regression, not\n * a fix). Same shape as the deltaE check below, widened from \"could not solve\" to \"solved, but\n * not usefully\".\n */\nconst ALPHA_CAP = 0.5\n\n/**\n * Replace alpha steps the solver could not actually solve — or, within `capAlphaSteps` leading\n * steps, solved only at a near-opaque alpha — with their opaque solid.\n *\n * Each `aN` is solved so that compositing it over the background reproduces step N\n * (`target = bg·(1-α) + fg·α`). From a saturated canvas a desaturated ink target is\n * unreachable within alpha bounds, and Radix's solver **degrades silently** rather than\n * failing — on `#FFD400` it returns `#0000ff39` for slate a11, which composites to\n * 1.66:1 body text where the solid step is a correct 9.03:1. Separately, even a solve that\n * *succeeds* can need alpha past `ALPHA_CAP` to get there — technically correct only over\n * that exact background, and no longer a usable \"tint\" anywhere else (gap #12a, ask 2).\n *\n * `space` must match whatever space `alpha` was actually solved in — `getAlphaColorP3` solves\n * per-channel in P3, and verifying that against an sRGB composite rejects perfectly good\n * solves (they were never trying to reproduce the sRGB composite in the first place).\n *\n * So verify the solve and fall back to the opaque value where either check fails. On\n * white/black every step solves exactly at low alpha, so the default output — and portal\n * parity — is untouched either way.\n */\nfunction opaqueWhereUnsolvable(\n background: string,\n solid: string[],\n alpha: string[],\n capAlphaSteps = 0,\n space: 'srgb' | 'p3' = 'srgb',\n): string[] {\n let bg: number[]\n try {\n bg = new Color(background).to(space).coords\n } catch {\n return alpha\n }\n return alpha.map((step, i) => {\n const target = solid[i]\n if (!target) return step\n try {\n const ink = new Color(step)\n const fg = ink.to(space).coords\n const a = ink.alpha as number\n if (i < capAlphaSteps && a > ALPHA_CAP) return target\n const composited = new Color(\n space,\n [0, 1, 2].map((k) => bg[k]! * (1 - a) + fg[k]! * a) as [\n number,\n number,\n number,\n ],\n )\n return composited.deltaE(new Color(target), '2000') <=\n ALPHA_SOLVE_TOLERANCE\n ? step\n : target\n } catch {\n return target\n }\n })\n}\n\n/**\n * Weight (0–1) each of the accent scale's first 5 steps is pulled toward the canvas's own\n * hue/chroma; step 6 and up (borders, solid, text) stay the true accent untouched.\n */\nconst CANVAS_TINT_WEIGHTS = [1, 0.8, 0.6, 0.4, 0.2]\n\n/**\n * Pull the accent scale's low steps toward the canvas's own color (gap #12a, ask 1): Radix's\n * generator sets every step's hue/chroma from the accent itself and only re-curves lightness\n * against the background, so on a chromatic canvas the low steps (app/component background\n * territory) stay a flat wash of the accent instead of a tint of the page — right where the\n * now-canvas-tinted neutral scale (gap #12) visibly disagrees with it. Step 9 (the solid,\n * brand-defining step) and everything above step 5 is left exactly as Radix generated it.\n *\n * Mixed in OKLab (rectangular a/b), not OKLCH (polar hue + chroma): a circular hue\n * interpolation between two far-apart hues (a blue accent on a yellow-green canvas, say) has\n * to sweep through whatever unrelated hues sit on the \"shorter\" arc between them — a vivid\n * green/teal/cyan that belongs to neither endpoint. Linearly mixing a/b instead moves along\n * the straight line between the two points, which naturally desaturates toward neutral as the\n * two colors pull in more different directions, rather than swinging through a third, unrelated\n * hue. Lightness (shared between OKLab and OKLCH) is untouched either way — Radix's own\n * background-aware curve already owns it.\n *\n * Takes/returns `Color` objects rather than strings so callers can reuse the same tinted\n * coordinates for both the sRGB and wide-gamut representations without a lossy round trip.\n */\nfunction tintLowStepsFromCanvas(\n solidScale: readonly Color[],\n bg: Color,\n): Color[] {\n const [, bgA, bgB] = bg.to('oklab').coords\n return solidScale.map((step, i) => {\n const weight = CANVAS_TINT_WEIGHTS[i]\n if (!weight) return step\n const [lightness, a, b] = step.to('oklab').coords\n return new Color('oklab', [\n lightness ?? 0,\n (a ?? 0) * (1 - weight) + (bgA ?? 0) * weight,\n (b ?? 0) * (1 - weight) + (bgB ?? 0) * weight,\n ])\n })\n}\n\n/** Mirrors generate-radix-colors.tsx's private toOklchString (not exported by the vendored file). */\nfunction toOklchWideGamutString(color: Color): string {\n const L = +((color.coords[0] ?? 0) * 100).toFixed(1)\n return color\n .to('oklch')\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${L}%$2`)\n}\n\n/** Same fixed alpha the vendored generator uses for `accentSurface` — a stylistic choice, not a solve. */\nfunction surfaceAlphaFor(appearance: Appearance): number {\n return appearance === 'light' ? 0.8 : 0.5\n}\n\n/**\n * Canvas-tinted low steps for the accent scale, or the untouched scale (and `tinted: false`)\n * when the canvas isn't chromatic enough to bother (gap #12a, ask 1). Only indices\n * `0..CANVAS_TINT_WEIGHTS.length-1` are ever replaced — steps 6-12 come back byte-identical to\n * what Radix generated, in both representations (no lossy sRGB round trip for the untouched\n * wide-gamut steps).\n *\n * `tinted` tells the caller whether to scope `opaqueWhereUnsolvable`'s alpha cap in at all —\n * the cap only makes sense for steps this function actually retargeted; a caller that applied\n * it unconditionally on any custom `background` would wrongly force perfectly fine low-alpha\n * steps opaque on an *achromatic* custom background (an explicit `#000000`/`#f5f5f5`, say),\n * where nothing here ever ran.\n *\n * Re-solves alpha for the tinted indices too: the generator's own `accentScaleAlpha` was\n * solved against the *pre-tint* solid, so once that solid moves the old alpha value no longer\n * reproduces it — left alone, every tinted step would fail `opaqueWhereUnsolvable`'s deltaE\n * check and go opaque, defeating the point (the `soft`-card fill this exists for would never\n * actually be translucent on a tinted canvas). Reuses Radix's own alpha solver so the result\n * matches what the generator itself would have produced had it been given this target directly.\n *\n * `accentSurface` (the `surface` card variant's fill) gets the same treatment: the generator\n * computes it as a fixed-alpha composite of step 2 — index 1, always tinted — over the\n * background, so recomputing it from the *tinted* step 2 at that same fixed alpha (0.8 light,\n * 0.5 dark; not a solve) keeps it in sync with `soft`'s fill instead of staying the old,\n * untinted wash (review-findings.md #13).\n */\nfunction accentScaleForCanvas(\n scale: GeneratedColors,\n background: string,\n appearance: Appearance,\n): Pick<\n GeneratedColors,\n | 'accentScale'\n | 'accentScaleWideGamut'\n | 'accentScaleAlpha'\n | 'accentScaleAlphaWideGamut'\n | 'accentSurface'\n | 'accentSurfaceWideGamut'\n> & { tinted: boolean } {\n try {\n const bg = new Color(background).to('oklch')\n const [, bgChroma] = bg.coords\n if (!bgChroma || bgChroma < CHROMATIC_THRESHOLD)\n return { ...scale, tinted: false }\n const tinted = tintLowStepsFromCanvas(\n scale.accentScale.map((hex) => new Color(hex).to('oklch')),\n bg,\n )\n const accentScale = scale.accentScale.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? tinted[i]!.to('srgb').toString({ format: 'hex' })\n : original,\n ) as typeof scale.accentScale\n const accentScaleWideGamut = scale.accentScaleWideGamut.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i] ? toOklchWideGamutString(tinted[i]!) : original,\n ) as typeof scale.accentScaleWideGamut\n const accentScaleAlpha = scale.accentScaleAlpha.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorSrgb(accentScale[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlpha\n const accentScaleAlphaWideGamut = scale.accentScaleAlphaWideGamut.map(\n (original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorP3(accentScaleWideGamut[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlphaWideGamut\n const surfaceAlpha = surfaceAlphaFor(appearance)\n const accentSurface = getAlphaColorSrgb(\n accentScale[1]!,\n scale.background,\n surfaceAlpha,\n )\n const accentSurfaceWideGamut = getAlphaColorP3(\n accentScaleWideGamut[1]!,\n scale.background,\n surfaceAlpha,\n )\n return {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha,\n accentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted: true,\n }\n } catch {\n return { ...scale, tinted: false }\n }\n}\n\n/** Build the Radix-style scale for one appearance from a single accent color. */\nfunction buildScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): GeneratedColors {\n const background = opts?.background ?? DEFAULTS[appearance].background\n const scale = generateRadixColors({\n appearance,\n accent,\n gray: opts?.neutral\n ? isSparkNeutralFamily(opts.neutral)\n ? NEUTRAL_BASE[opts.neutral]\n : opts.neutral\n : DEFAULTS[appearance].gray,\n background,\n })\n const {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha: canvasAccentScaleAlpha,\n accentScaleAlphaWideGamut: canvasAccentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted,\n } = opts?.background\n ? accentScaleForCanvas(scale, background, appearance)\n : { ...scale, tinted: false }\n const capAlphaSteps = tinted ? CANVAS_TINT_WEIGHTS.length : 0\n const solved = opts?.background\n ? {\n ...scale,\n accentScale,\n accentScaleWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n accentScaleAlpha: opaqueWhereUnsolvable(\n background,\n accentScale,\n canvasAccentScaleAlpha,\n capAlphaSteps,\n ) as typeof scale.accentScaleAlpha,\n accentScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n accentScaleWideGamut,\n canvasAccentScaleAlphaWideGamut,\n capAlphaSteps,\n 'p3',\n ) as typeof scale.accentScaleAlphaWideGamut,\n grayScaleAlpha: opaqueWhereUnsolvable(\n background,\n scale.grayScale,\n scale.grayScaleAlpha,\n ) as typeof scale.grayScaleAlpha,\n grayScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n 0,\n 'p3',\n ) as typeof scale.grayScaleAlphaWideGamut,\n }\n : scale\n return solved\n}\n\n/**\n * Generate the Radix-style accent scale for one appearance from a single accent color.\n *\n * Solid-fill inks come from `readableContrast`, which clears WCAG but can only reach an\n * unreadable APCA on a light, saturated step 9 (a near-black tint at Lc 33-44). Where it\n * does, the fill is darkened until plain white clears 4.5:1 and the scale is re-seeded\n * from that fill — see `darkenForWhiteInk`.\n */\nexport function generateAccentScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): SparkAccentScale {\n const solved = withNativeInks(buildScale(accent, appearance, opts))\n const fill = solved.accentScale[8]!\n if (apcaOn(fill, solved.accentContrast) >= MIN_INK_APCA)\n return withReadableTintInk(solved)\n\n // Re-seed rather than patch step 9: steps 10-12 are derived from it by a fixed\n // lightness offset, so moving 9 alone leaves hover lighter than the fill.\n const reseeded = buildScale(darkenForWhiteInk(fill), appearance, opts)\n const reseededFill = reseeded.accentScale[8]!\n if (contrastOn(reseededFill, WHITE) < MIN_TEXT_CONTRAST)\n return withReadableTintInk(solved)\n return withReadableTintInk({\n ...reseeded,\n accentContrast: WHITE,\n accentOnSolid: WHITE,\n })\n}\n\n/**\n * Step 11 is the ink of every non-solid style, read on the a3 fill and the a4 hover. Both\n * are translucent, so on a soft card (neutral-a3 ≈ neutral-3 over the canvas) they\n * composite darker than on the canvas and Radix's step 11 drops under 4.5:1 (red 3.72:1).\n * Move step 11 away from the fill, holding chroma and hue, until a11 on a4 clears 4.5:1\n * over every neutral knob's step 3, and re-solve a11 from it. Unchanged when it clears.\n */\nexport function withReadableTintInk<T extends GeneratedColors>(scale: T): T {\n const canvas = scale.background\n const dark = appearanceForBackground(canvas) === 'dark'\n const neutral3s = [\n scale.grayScale[2]!,\n ...Object.keys(NEUTRAL_BASE).map((family) => {\n const steps = RadixColors[\n (dark ? `${family}Dark` : family) as keyof typeof RadixColors\n ] as Record<string, string>\n return steps[`${family}3`]!\n }),\n ]\n const hoverFills = neutral3s.map((page) =>\n composite(scale.accentScaleAlpha[3]!, page),\n )\n const inkOn = (step11: string) => {\n const ink = getAlphaColorSrgb(step11, canvas)\n return Math.min(\n ...hoverFills.map((fill) => contrastOn(fill, composite(ink, fill))),\n )\n }\n const original = scale.accentScale[10]!\n if (inkOn(original) >= MIN_TEXT_CONTRAST) return scale\n\n const [lightness, chroma, hue] = new Color(original).to('oklch').coords\n const [, wideChroma, wideHue] = new Color(scale.accentScaleWideGamut[10]!).to(\n 'oklch',\n ).coords\n const toward = contrastOn(canvas, '#000') > contrastOn(canvas, '#fff') ? 0 : 1\n const srgbAt = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (inkOn(srgbAt(mid)) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n const step11 = srgbAt(passes)\n const wideStep11 = toOklchWideGamutString(\n new Color('oklch', [passes, wideChroma ?? 0, wideHue ?? 0]).toGamut({\n space: 'p3',\n }),\n )\n const withStep = <A extends readonly string[]>(steps: A, value: string) =>\n steps.map((v, i) => (i === 10 ? value : v)) as unknown as A\n return {\n ...scale,\n accentScale: withStep(scale.accentScale, step11),\n accentScaleWideGamut: withStep(scale.accentScaleWideGamut, wideStep11),\n accentScaleAlpha: withStep(\n scale.accentScaleAlpha,\n getAlphaColorSrgb(step11, canvas),\n ),\n accentScaleAlphaWideGamut: withStep(\n scale.accentScaleAlphaWideGamut,\n getAlphaColorP3(wideStep11, canvas),\n ),\n }\n}\n\n// Rounds each 8-bit channel term the way browsers blend (see blendAlpha in the vendored\n// generator); the solve lands on the 4.5:1 boundary, where unrounded math is off by ~0.01.\nconst composite = (top: string, bottom: string) => {\n const fg = new Color(top).to('srgb')\n const bg = new Color(bottom).to('srgb').coords\n const a = fg.alpha as number\n const channel = (k: number) =>\n (Math.round(bg[k]! * 255 * (1 - a)) + Math.round(fg.coords[k]! * 255 * a)) /\n 255\n return new Color('srgb', [channel(0), channel(1), channel(2)]).toString({\n format: 'hex',\n })\n}\n\n// accentContrast → 4.5:1 (button text); accentOnSolid → 3:1 (graphical glyph,\n// see SparkAccentScale). Same picker, different WCAG threshold.\nfunction withNativeInks(scale: GeneratedColors): SparkAccentScale {\n const fill = scale.accentScale[8]!\n return {\n ...scale,\n accentContrast: readableContrast(\n fill,\n scale.accentContrast,\n MIN_TEXT_CONTRAST,\n ),\n accentOnSolid: readableContrast(\n fill,\n scale.accentContrast,\n MIN_GRAPHIC_CONTRAST,\n ),\n }\n}\n\n/**\n * Darkest the fill needs to be for white text to clear 4.5:1, holding chroma and hue.\n * Returns the fill unchanged when white already clears — the generator picks its ink\n * from `Math.abs(white.contrastAPCA(fill))`, whose arguments are transposed, so it can\n * reach for a dark tint on a fill white already handles.\n */\nfunction darkenForWhiteInk(fillHex: string): string {\n const fill = new Color(fillHex)\n if (contrastOn(fillHex, WHITE) >= MIN_TEXT_CONTRAST) return fillHex\n const [lightness, chroma, hue] = fill.to('oklch').coords\n const darkened = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (contrastOn(darkened(mid), WHITE) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n return darkened(passes)\n}\n\nconst WHITE = '#fff' // matches the generator's own native contrast literal\nconst MIN_TEXT_CONTRAST = 4.5 // WCAG 1.4.3 — button / label text\nconst MIN_GRAPHIC_CONTRAST = 3 // WCAG 1.4.11 — a check / indicator glyph on a solid fill\n// APCA's floor for large headlines. WCAG 4.5:1 is satisfiable on any step 9 by a dark\n// enough tint, but reads as low as Lc 33 — worse than the white it replaced (Lc 66).\nconst MIN_INK_APCA = 45\n\nconst contrastOn = (bgHex: string, fgHex: string) =>\n new Color(fgHex).contrast(new Color(bgHex), 'WCAG21')\n// Color.js takes the background as the receiver; APCA is polarity-dependent, so the\n// order matters (unlike WCAG 2.x).\nconst apcaOn = (bgHex: string, fgHex: string) =>\n Math.abs(new Color(bgHex).contrastAPCA(new Color(fgHex)))\n\n// Pick the readable foreground on step 9: prefer the generator's native contrast\n// (usually white), else fall to progressively darker accent-tinted inks until one\n// clears `min`. Text uses 4.5:1; a graphical glyph (the checkmark) uses 3:1 — which\n// keeps white on most accents (incl. neutral 3.32:1, red 3.91:1) but flips to a dark\n// tint where white is too weak (e.g. orange, 2.97:1). The vendored generator keeps\n// white at APCA Lc >= 40, which can sit below both WCAG thresholds — hence this guard.\nfunction readableContrast(\n step9Hex: string,\n native: string,\n min: number,\n): string {\n const step9 = new Color(step9Hex)\n const [, chroma, hue] = step9.to('oklch').coords\n const tinted = (lightness: number) =>\n new Color('oklch', [\n lightness,\n Math.max(0.08 * (chroma ?? 0), 0.04),\n hue ?? 0,\n ])\n .to('srgb')\n .toString({ format: 'hex' })\n const ratioOf = (hex: string) => new Color(hex).contrast(step9, 'WCAG21')\n const candidates = [native, tinted(0.25), tinted(0.15)]\n const ratios = candidates.map(ratioOf)\n const passing = ratios.findIndex((r) => r >= min)\n if (passing !== -1) return candidates[passing]!\n\n // The ladder above is a *preference* (least extreme ink first), not a search — it\n // stops at 0.15 and so missed accents that only clear `min` a little darker still\n // (#936c89 landed at 4.47 when 0.05 gives 4.73). Contrast rises monotonically as\n // the ink's lightness moves away from the fill's, so search outward from step 9\n // toward whichever extreme is stronger: if a passing ink exists, this finds it.\n const lightness9 = step9.to('oklch').coords[0] ?? 0.5\n const toward = ratioOf(tinted(0)) >= ratioOf(tinted(1)) ? 0 : 1\n let fails = lightness9\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioOf(tinted(mid)) >= min) passes = mid\n else fails = mid\n }\n const found = tinted(passes)\n if (ratioOf(found) >= min) return found\n\n // No ink of any lightness clears `min` on this fill — step 9 itself is too\n // mid-toned. Return the strongest available; the palette sweep flags the accent.\n const all = [...candidates, tinted(toward)]\n const allRatios = all.map(ratioOf)\n return all[allRatios.indexOf(Math.max(...allRatios))]!\n}\n\n// Radix `--red-a4`. The invalid-field hover paints that step, and the placeholder\n// solver has to clear it. spark-default.css does not override the red scale.\nexport const RED_A4: Record<Appearance, string> = {\n light: '#ff000824',\n dark: '#fe0a3b44',\n}\n\n/**\n * The hover tints a static `--neutral-placeholder` must clear (field.tsx): gray\n * (`neutralA4`), invalid (`--red-a4`) and brand (`--spark-a4`) for the default accent and\n * every curated one. A runtime brand outside these is not covered.\n */\nexport function staticFieldHoverTints(\n appearance: Appearance,\n neutralA4: string,\n): string[] {\n return [\n neutralA4,\n RED_A4[appearance],\n ...[DEFAULT_ACCENT, ...Object.values(CURATED_ACCENTS)].map(\n (accent) => generateAccentScale(accent, appearance).accentScaleAlpha[3]!,\n ),\n ]\n}\n\n// 4.5 plus headroom for the 8-bit rounding of the painted fill, which moves the ratio ~0.01.\nconst PLACEHOLDER_CONTRAST = 4.55\n\nfunction compositeOver(background: Color, color: string): Color {\n const ink = new Color(color).to('srgb')\n const alpha = ink.alpha as number\n const bg = background.to('srgb').coords\n return new Color(\n 'srgb',\n [0, 1, 2].map((k) => bg[k]! * (1 - alpha) + ink.coords[k]! * alpha) as [\n number,\n number,\n number,\n ],\n )\n}\n\n/**\n * The lightest color between neutral a10 and a11 that reaches `PLACEHOLDER_CONTRAST` on a\n * `card` over `page`, bare and under each of `tints`, clamped to a11, as an sRGB alpha step\n * over `page` (opaque where the alpha solve cannot reproduce it). The card is the deepest\n * surface a field sits on, so clearing it clears the page too.\n */\nexport function solvePlaceholder({\n a10,\n a11,\n page,\n card,\n tints,\n}: {\n a10: string\n a11: string\n page: string\n card: string\n tints: string[]\n}): string {\n const bg = new Color(page)\n const onCard = compositeOver(bg, card)\n const fills = [onCard, ...tints.map((tint) => compositeOver(onCard, tint))]\n const from = compositeOver(bg, a10)\n const to = compositeOver(bg, a11)\n const at = (t: number) => from.mix(to, t, { space: 'oklch' }).to('srgb')\n const inkAt = (t: number) => {\n const solid = at(t).toString({ format: 'hex' })\n return opaqueWhereUnsolvable(\n page,\n [solid],\n [getAlphaColorSrgb(solid, page)],\n )[0]!\n }\n const passes = (t: number) =>\n fills.every(\n (fill) =>\n compositeOver(fill, inkAt(t)).contrast(fill, 'WCAG21') >=\n PLACEHOLDER_CONTRAST,\n )\n let found = 1\n if (passes(0)) found = 0\n else if (passes(1)) {\n let fails = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + found) / 2\n if (passes(mid)) found = mid\n else fails = mid\n }\n }\n return inkAt(found)\n}\n\n/** sRGB `--spark-*` declarations for a generated accent scale. */\nexport function accentSparkVars(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScale,\n scale.accentScaleAlpha,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurface,\n scale.background,\n )\n}\n\n/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */\nexport function accentSparkVarsP3(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScaleWideGamut,\n scale.accentScaleAlphaWideGamut,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurfaceWideGamut,\n scale.background,\n )\n}\n\n/** Radix's `grayColor=\"auto\"` pairing (accent family → gray), mapped to our 6 neutrals. */\nconst NEUTRAL_BY_ACCENT: Record<string, SparkNeutralFamily> = {\n blue: 'slate',\n indigo: 'slate',\n cyan: 'slate',\n violet: 'mauve',\n purple: 'mauve',\n pink: 'mauve',\n red: 'mauve',\n teal: 'sage',\n green: 'sage',\n grass: 'olive',\n orange: 'sand',\n gray: 'gray',\n}\n\n/** Radix's \"auto\" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */\nexport function getMatchingNeutral(accent: string): SparkNeutralFamily {\n const byName = NEUTRAL_BY_ACCENT[accent]\n if (byName) return byName\n try {\n const [, chroma, hueRaw] = new Color(accent).to('oklch').coords\n if (!chroma || chroma < CHROMATIC_THRESHOLD) return 'gray'\n const hue = (((hueRaw ?? 0) % 360) + 360) % 360\n if (hue >= 300 || hue < 35) return 'mauve'\n if (hue < 115) return 'sand'\n if (hue < 145) return 'olive'\n if (hue < 200) return 'sage'\n return 'slate'\n } catch {\n return 'slate'\n }\n}\n\n/**\n * Fill steps (Radix roles 1-5), measured from the surface toward the *scrim* — the pole\n * opposite the ink. Magnitudes are unchanged from the original ink-directed ramp; only\n * the direction flips, so a fill and the text on it move apart instead of together.\n */\nconst INVERTED_FILL_RAMP = [0.06, 0.12, 0.2, 0.3, 0.4]\n\n/** Strongest border step (8, the hover/active edge). 6 is solved per accent; 7 splits them. */\nconst INVERTED_BORDER_TOP = 0.82\n\n/**\n * Ink steps (9-12), measured from the surface toward the ink. Step 9 (the solid fill)\n * lands on the ink itself; 10 eases back for hover; 11-12 are full-strength ink.\n */\nconst INVERTED_INK_RAMP = [1, 0.88, 1, 1]\n\n/**\n * Lowest alpha at which the ink, laid over the surface, is 3:1 distinguishable from it\n * (WCAG 1.4.11) — the floor for a border that has to read as an edge at all.\n *\n * Solved rather than hardcoded because it moves with the accent (0.57-0.72 across the\n * curated set). Contrast against the surface rises monotonically with `t`, so bisection\n * finds the boundary.\n *\n * Targets 1% above the floor rather than landing on it: the step ships as `#rrggbbaa`, and\n * 8-bit rounding on any of the four channels can drop an exact solve back under 3:1.\n */\nfunction solveBorderFloor(surface: Color, ink: Color): number {\n const target = MIN_GRAPHIC_CONTRAST * 1.01\n const ratioAt = (t: number) =>\n new Color(Color.mix(surface, ink, t, { space: 'srgb' })).contrast(\n surface,\n 'WCAG21',\n )\n let fails = 0\n let passes = 1\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioAt(mid) >= target) passes = mid\n else fails = mid\n }\n return passes\n}\n\n/** OKLCH string in the wide-gamut format the generator emits (`oklch(96.1% 0.0155 254.2)`). */\nfunction oklchString(color: Color): string {\n const oklch = color.to('oklch')\n const lightness = +((oklch.coords[0] ?? 0) * 100).toFixed(1)\n return oklch\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${lightness}%$2`)\n}\n\n/**\n * Re-cast a scale for use ON its own solid (step-9) fill — the `inverted` knob.\n *\n * A solid accent control on a solid accent surface is invisible: both paint `--spark-9`.\n * Inverting swaps that pair — step 9 and its contrast ink trade places — so the solid fill\n * and its text are contrast-safe **by construction, for any accent**: `accentContrast` is\n * already the WCAG-checked readable ink for step 9 (`readableContrast`).\n *\n * The non-solid styles need a second idea, because the ink is also the text colour. A fill\n * blended surface→ink moves the background *toward* the foreground, so ink-on-fill contrast\n * falls monotonically with `t` — and measured across the curated set, the highest `t` that\n * still clears 4.5:1 (0.174) sits below the lowest `t` that is 3:1 distinguishable from the\n * surface (0.543). On that line no fill can be both legible and visible, on any accent.\n *\n * So fills blend the other way, toward the **scrim** — the lightness pole opposite the ink,\n * carrying the surface's own hue. Contrast against the ink now *rises* with `t`, which puts\n * every non-solid style at or above the solid pair's ratio for free. Borders keep stepping\n * toward the ink (a scrim-side border caps at ~2:1 on light accents like orange, so it can\n * never clear 1.4.11), starting at the solved 3:1 floor.\n *\n * Appearance-stable on purpose. The generator pins step 9 to the accent itself, so the\n * surface is identical in light and dark — which makes the whole inverted scale identical\n * too, and `[data-inverted]` needs no `.dark` twin.\n *\n * Two things this is deliberately NOT:\n * - **A positional mirror** (`1↔12`). It leaves the solid fill only 2.04:1 from the card in\n * dark and 2.23:1 on a light accent like orange — under the 3:1 graphical floor, i.e. still\n * invisible, which is the bug this exists to fix.\n * - **A regenerated on-surface ramp.** Feeding the surface back in as `background` produces\n * off-hue overlays (a warm wash on a blue card) and ink at 3.35:1.\n *\n * Values are literals, not `var(--spark-N)` aliases: a scale can't re-point to itself\n * (`--spark-1: var(--spark-12)` next to `--spark-12: var(--spark-1)` is a cycle, and CSS\n * discards both).\n */\nexport function invertAccentScale(scale: SparkAccentScale): SparkAccentScale {\n const surface = scale.accentScale[8]! // the solid fill this scale will sit on\n const ink = scale.accentContrast // already AA-checked against that fill\n const surfaceColor = new Color(surface)\n const inkColor = new Color(ink)\n\n // The scrim: opposite the ink in lightness, tinted with the surface's own hue by the\n // same rule readableContrast uses for its ink ladder. Derived from the surface, so this\n // introduces no new palette input.\n const [surfaceLightness, surfaceChroma, surfaceHue] =\n surfaceColor.to('oklch').coords\n const inkLightness = inkColor.to('oklch').coords[0] ?? 0\n const scrimColor = new Color('oklch', [\n inkLightness > (surfaceLightness ?? 0) ? 0.12 : 0.98,\n Math.max(0.08 * (surfaceChroma ?? 0), 0.02),\n surfaceHue ?? 0,\n ])\n\n const borderFloor = solveBorderFloor(surfaceColor, inkColor)\n const ramp: { toward: Color; t: number }[] = [\n ...INVERTED_FILL_RAMP.map((t) => ({ toward: scrimColor, t })),\n { toward: inkColor, t: borderFloor },\n { toward: inkColor, t: (borderFloor + INVERTED_BORDER_TOP) / 2 },\n { toward: inkColor, t: INVERTED_BORDER_TOP },\n ...INVERTED_INK_RAMP.map((t) => ({ toward: inkColor, t })),\n ]\n\n // sRGB, not OKLab: the browser composites `--spark-aN` as source-over in gamma sRGB, so\n // mixing the opaque twin in any other space makes `--spark-N` a different colour from it\n // (measured up to dE2000 2.8 on the old OKLab mix, over ALPHA_SOLVE_TOLERANCE).\n const blend = ({ toward, t }: { toward: Color; t: number }) =>\n new Color(Color.mix(surfaceColor, toward, t, { space: 'srgb' }))\n const steps = ramp.map((s) =>\n blend(s).to('srgb').toString({ format: 'hex' }),\n ) as typeof scale.accentScale\n const stepsWideGamut = ramp.map((s) =>\n oklchString(blend(s)),\n ) as typeof scale.accentScaleWideGamut\n // Mixing surface→X at t in sRGB is the same colour as X at alpha t over the surface, so\n // each alpha step is just its own pole at rising opacity — no solve needed.\n const alpha = ramp.map(({ toward, t }) => {\n const translucent = toward.clone()\n translucent.alpha = t\n return translucent.to('srgb').toString({ format: 'hex' })\n }) as typeof scale.accentScaleAlpha\n const alphaWideGamut = ramp.map(({ toward, t }) => {\n const translucent = toward.clone().to('p3')\n translucent.alpha = t\n return translucent\n .toString({ precision: 4 })\n .replace('color(p3 ', 'color(display-p3 ')\n }) as typeof scale.accentScaleAlphaWideGamut\n return {\n ...scale,\n accentScale: steps,\n accentScaleWideGamut: stepsWideGamut,\n accentScaleAlpha: alpha,\n accentScaleAlphaWideGamut: alphaWideGamut,\n // Ink on the inverted solid fill is the accent itself — the other half of the swapped pair.\n accentContrast: surface,\n accentOnSolid: surface,\n accentSurface: steps[1]!,\n accentSurfaceWideGamut: stepsWideGamut[1]!,\n }\n}\n\n/**\n * `neutralColor=\"auto\"` resolution: prefer the canvas's own tint when it has one — a\n * chromatic `background` seeds a visibly warmer/cooler ramp than bucketing it into one\n * of the 6 named families would (see gap #12). Falls back to accent-matched bucketing\n * when there's no tinted canvas to pull from.\n */\nexport function resolveAutoNeutral(\n accent: string,\n background?: string,\n): string {\n if (background) {\n try {\n const [, chroma] = new Color(background).to('oklch').coords\n if (chroma && chroma >= CHROMATIC_THRESHOLD) return background\n } catch {\n // fall through to accent-matched neutral\n }\n }\n return getMatchingNeutral(accent)\n}\n\n/**\n * Whether a string parses as a color at all — for validating a custom `neutralColor`\n * before it reaches the generator. An unparseable seed (e.g. a typo'd family name like\n * `\"slat\"`) would otherwise throw deep inside `generateRadixColors`, taking the whole\n * accent scale down with it via the caller's shared try/catch.\n */\nexport function isParsableColor(value: string): boolean {\n try {\n new Color(value).to('oklch')\n return true\n } catch {\n return false\n }\n}\n\n/** The 27 tokens every scale family declares, under an arbitrary prefix. */\nfunction scaleVars(\n prefix: string,\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n): string[] {\n return [\n ...steps.map((c, i) => `--${prefix}-${i + 1}: ${c};`),\n ...alpha.map((c, i) => `--${prefix}-a${i + 1}: ${c};`),\n `--${prefix}-contrast: ${contrast};`,\n `--${prefix}-on-solid: ${onSolid};`,\n `--${prefix}-surface: ${surface};`,\n ]\n}\n\nfunction sparkVars(\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n background: string,\n): string[] {\n const indicator = steps[8]! // step 9 — the brand step (matches --spark-indicator/-track)\n return [\n ...scaleVars('spark', steps, alpha, contrast, onSolid, surface),\n `--spark-background: ${background};`,\n `--spark-indicator: ${indicator};`,\n `--spark-track: ${indicator};`,\n ]\n}\n\n/**\n * The same `--neutral-*` tokens `neutralSparkVars` emits, pointed back at a static Radix\n * family instead of a generated scale — the counterpart for an appearance that has *no*\n * custom canvas of its own.\n *\n * Ambient (no explicit `appearance`) mode emits both appearances' scales into one unlayered\n * `<style>`, but a custom canvas only regenerates neutrals for the appearance it belongs to.\n * Left undeclared, the other block doesn't fall back — nothing resets a custom property, so\n * its sibling's canvas-tinted values simply stay in effect there, and being unlayered they\n * outrank `[data-neutral-color]`'s own `@layer base` mapping too. Declaring the same token\n * set closes that. `var(--<family>-*)` is itself appearance-aware (Radix ships the dark\n * scales under `.dark`), so one declaration resolves correctly wherever the block applies.\n */\nexport function neutralFamilyVars(\n family: Exclude<SparkNeutral, 'auto'>,\n): string[] {\n const ref = (suffix: string) => `var(--${family}-${suffix})`\n return [\n ...scaleVars(\n 'neutral',\n Array.from({ length: 12 }, (_, i) => ref(`${i + 1}`)),\n Array.from({ length: 12 }, (_, i) => ref(`a${i + 1}`)),\n // The companion tokens' own static defaults, from spark-default.css's .light/.dark.\n // `-contrast` is per-family there so [data-neutral-color] swaps the ink with the\n // ramp; referencing it rather than inlining #fff keeps the CSS the single source\n // of truth if the two appearances ever diverge again.\n `var(--${family}-contrast)`,\n '#fff',\n 'var(--neutral-2)',\n ),\n `--neutral-placeholder: ${ref('placeholder')};`,\n ]\n}\n\n// Emitted as sRGB on the P3 path too; the solve is a WCAG ratio, which is defined in sRGB.\nconst neutralPlaceholderVar = (scale: SparkAccentScale) =>\n `--neutral-placeholder: ${solvePlaceholder({\n a10: scale.grayScaleAlpha[9]!,\n a11: scale.grayScaleAlpha[10]!,\n page: scale.background,\n card: scale.grayScale[2]!,\n tints: [\n scale.grayScaleAlpha[3]!,\n RED_A4[appearanceForBackground(scale.background)],\n scale.accentScaleAlpha[3]!,\n ],\n })};`\n\n/**\n * `--neutral-*` declarations from the gray scale the generator produces alongside the\n * accent — normally discarded, because `[data-neutral-color]` points `--neutral-*` at a\n * static Radix family. Worth consuming only when the canvas is custom: the static\n * families' alpha steps are pre-baked against white/black, so on any other background\n * they composite wrong (every gray text/border reads through `--neutral-a*`).\n *\n * `--neutral-surface` is step 2, matching the static families' own convention rather\n * than the generator's translucent `graySurface`.\n */\nexport function neutralSparkVars(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScale,\n scale.grayScaleAlpha,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScale[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n\n/** Wide-gamut (P3) twin of `neutralSparkVars`. */\nexport function neutralSparkVarsP3(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScaleWideGamut[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n"],"mappings":"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"}
|
package/dist/lib/accent-theme.js
CHANGED
|
@@ -396,8 +396,8 @@ function readableContrast(step9Hex, native, min) {
|
|
|
396
396
|
return all[allRatios.indexOf(Math.max(...allRatios))];
|
|
397
397
|
}
|
|
398
398
|
const RED_A4 = {
|
|
399
|
-
light: "#
|
|
400
|
-
dark: "#
|
|
399
|
+
light: "#ff000824",
|
|
400
|
+
dark: "#fe0a3b44"
|
|
401
401
|
};
|
|
402
402
|
/**
|
|
403
403
|
* The hover tints a static `--neutral-placeholder` must clear (field.tsx): gray
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accent-theme.js","names":[],"sources":["../../src/lib/accent-theme.ts"],"sourcesContent":["// Accent is the one runtime knob (colour math, not a CSS-var swap): generate the\n// `--spark-*` scale with the same Radix algorithm live portals use, apply as inline vars.\nimport * as RadixColors from '@radix-ui/colors'\nimport Color from 'colorjs.io'\nimport {\n generateRadixColors,\n getAlphaColorP3,\n getAlphaColorSrgb,\n} from './generate-radix-colors'\nimport {\n isSparkNeutralFamily,\n type SparkNeutral,\n type SparkNeutralFamily,\n} from './theme-types'\n\n/** Shape returned by the vendored Radix generator (it doesn't export a named type). */\nexport type GeneratedColors = ReturnType<typeof generateRadixColors>\n\n/**\n * generateAccentScale output: the raw scale, but with two readable foregrounds on the\n * solid (step-9) fill, each meeting a different WCAG bar:\n * - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.\n * - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).\n * A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white\n * where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark\n * accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for\n * the glyph over-darkened it (a black tick on a red/neutral checkbox).\n */\nexport type SparkAccentScale = GeneratedColors & { accentOnSolid: string }\n\nexport type Appearance = 'light' | 'dark'\n\n/** The epilot brand: the no-attribute default `--spark-*` in spark-default.css. */\nexport const DEFAULT_ACCENT = '#005EB4'\n\n/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */\nexport const CURATED_ACCENTS: Record<string, string> = {\n blue: RadixColors.blue.blue9,\n indigo: RadixColors.indigo.indigo9,\n violet: RadixColors.violet.violet9,\n purple: RadixColors.purple.purple9,\n cyan: RadixColors.cyan.cyan9,\n teal: RadixColors.teal.teal9,\n green: RadixColors.green.green9,\n grass: RadixColors.grass.grass9,\n orange: RadixColors.orange.orange9,\n red: RadixColors.red.red9,\n pink: RadixColors.pink.pink9,\n gray: RadixColors.gray.gray9,\n}\n\n/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */\nconst DEFAULTS: Record<Appearance, { gray: string; background: string }> = {\n light: { gray: '#333333', background: '#ffffff' },\n // Matches ECP DesignUtils' DEFAULT_DARK_BACKGROUND_COLOR -- the alpha ramp\n // is composited against this, so it must match what's actually painted.\n dark: { gray: '#8b8d98', background: '#000000' },\n}\n\n/** Radix step-9 per neutral family — the generator's `gray` seed, so a regenerated\n neutral lands on the family `[data-neutral-color]` would have selected statically. */\nconst NEUTRAL_BASE: Record<SparkNeutralFamily, string> = {\n slate: RadixColors.slate.slate9,\n mauve: RadixColors.mauve.mauve9,\n gray: RadixColors.gray.gray9,\n sage: RadixColors.sage.sage9,\n olive: RadixColors.olive.olive9,\n sand: RadixColors.sand.sand9,\n}\n\nexport interface GenerateScaleOptions {\n /**\n * Page canvas the scale is composited against. Drives the lightness curve of all 12\n * steps AND every alpha step (each is solved as `target = bg·(1-α) + fg·α`), so it\n * must match what's actually painted or the alpha ramp renders wrong.\n */\n background?: string\n /**\n * Neutral family, or an arbitrary color to seed the neutral scale (regenerated against\n * `background`, same as a named family). Pass a resolved value, not `'auto'` — this\n * option isn't itself typed to exclude it (an open string union can't forbid one\n * literal), but nothing in `<Theme>` ever forwards `'auto'` this far.\n */\n neutral?: SparkNeutral\n}\n\n/** Which appearance a canvas belongs to, by OKLCH lightness. */\nexport function appearanceForBackground(background: string): Appearance {\n const [lightness] = new Color(background).to('oklch').coords\n return (lightness ?? 1) >= 0.5 ? 'light' : 'dark'\n}\n\n/** deltaE2000 ≈ 2 is the just-noticeable bar — a good solve lands far inside it. */\nconst ALPHA_SOLVE_TOLERANCE = 2\n\n/**\n * OKLCH chroma below which a color reads as neutral rather than tinted — shared by every\n * \"is this canvas/accent actually chromatic\" check (getMatchingNeutral, resolveAutoNeutral,\n * accentScaleForCanvas).\n */\nconst CHROMATIC_THRESHOLD = 0.04\n\n/**\n * An alpha step past this needs to cover the canvas almost completely to reproduce its\n * target — at that point it isn't a tint anymore, and the opaque solid is both cheaper and\n * more predictable. Only applied to the accent ramp's canvas-tinted low steps (gap #12a,\n * ask 2 — specifically the steps `tintLowStepsFromCanvas` retargets; steps 6-12 are by-design\n * high-alpha ink/solid tokens even on plain white — capping *those* would be a regression, not\n * a fix). Same shape as the deltaE check below, widened from \"could not solve\" to \"solved, but\n * not usefully\".\n */\nconst ALPHA_CAP = 0.5\n\n/**\n * Replace alpha steps the solver could not actually solve — or, within `capAlphaSteps` leading\n * steps, solved only at a near-opaque alpha — with their opaque solid.\n *\n * Each `aN` is solved so that compositing it over the background reproduces step N\n * (`target = bg·(1-α) + fg·α`). From a saturated canvas a desaturated ink target is\n * unreachable within alpha bounds, and Radix's solver **degrades silently** rather than\n * failing — on `#FFD400` it returns `#0000ff39` for slate a11, which composites to\n * 1.66:1 body text where the solid step is a correct 9.03:1. Separately, even a solve that\n * *succeeds* can need alpha past `ALPHA_CAP` to get there — technically correct only over\n * that exact background, and no longer a usable \"tint\" anywhere else (gap #12a, ask 2).\n *\n * `space` must match whatever space `alpha` was actually solved in — `getAlphaColorP3` solves\n * per-channel in P3, and verifying that against an sRGB composite rejects perfectly good\n * solves (they were never trying to reproduce the sRGB composite in the first place).\n *\n * So verify the solve and fall back to the opaque value where either check fails. On\n * white/black every step solves exactly at low alpha, so the default output — and portal\n * parity — is untouched either way.\n */\nfunction opaqueWhereUnsolvable(\n background: string,\n solid: string[],\n alpha: string[],\n capAlphaSteps = 0,\n space: 'srgb' | 'p3' = 'srgb',\n): string[] {\n let bg: number[]\n try {\n bg = new Color(background).to(space).coords\n } catch {\n return alpha\n }\n return alpha.map((step, i) => {\n const target = solid[i]\n if (!target) return step\n try {\n const ink = new Color(step)\n const fg = ink.to(space).coords\n const a = ink.alpha as number\n if (i < capAlphaSteps && a > ALPHA_CAP) return target\n const composited = new Color(\n space,\n [0, 1, 2].map((k) => bg[k]! * (1 - a) + fg[k]! * a) as [\n number,\n number,\n number,\n ],\n )\n return composited.deltaE(new Color(target), '2000') <=\n ALPHA_SOLVE_TOLERANCE\n ? step\n : target\n } catch {\n return target\n }\n })\n}\n\n/**\n * Weight (0–1) each of the accent scale's first 5 steps is pulled toward the canvas's own\n * hue/chroma; step 6 and up (borders, solid, text) stay the true accent untouched.\n */\nconst CANVAS_TINT_WEIGHTS = [1, 0.8, 0.6, 0.4, 0.2]\n\n/**\n * Pull the accent scale's low steps toward the canvas's own color (gap #12a, ask 1): Radix's\n * generator sets every step's hue/chroma from the accent itself and only re-curves lightness\n * against the background, so on a chromatic canvas the low steps (app/component background\n * territory) stay a flat wash of the accent instead of a tint of the page — right where the\n * now-canvas-tinted neutral scale (gap #12) visibly disagrees with it. Step 9 (the solid,\n * brand-defining step) and everything above step 5 is left exactly as Radix generated it.\n *\n * Mixed in OKLab (rectangular a/b), not OKLCH (polar hue + chroma): a circular hue\n * interpolation between two far-apart hues (a blue accent on a yellow-green canvas, say) has\n * to sweep through whatever unrelated hues sit on the \"shorter\" arc between them — a vivid\n * green/teal/cyan that belongs to neither endpoint. Linearly mixing a/b instead moves along\n * the straight line between the two points, which naturally desaturates toward neutral as the\n * two colors pull in more different directions, rather than swinging through a third, unrelated\n * hue. Lightness (shared between OKLab and OKLCH) is untouched either way — Radix's own\n * background-aware curve already owns it.\n *\n * Takes/returns `Color` objects rather than strings so callers can reuse the same tinted\n * coordinates for both the sRGB and wide-gamut representations without a lossy round trip.\n */\nfunction tintLowStepsFromCanvas(\n solidScale: readonly Color[],\n bg: Color,\n): Color[] {\n const [, bgA, bgB] = bg.to('oklab').coords\n return solidScale.map((step, i) => {\n const weight = CANVAS_TINT_WEIGHTS[i]\n if (!weight) return step\n const [lightness, a, b] = step.to('oklab').coords\n return new Color('oklab', [\n lightness ?? 0,\n (a ?? 0) * (1 - weight) + (bgA ?? 0) * weight,\n (b ?? 0) * (1 - weight) + (bgB ?? 0) * weight,\n ])\n })\n}\n\n/** Mirrors generate-radix-colors.tsx's private toOklchString (not exported by the vendored file). */\nfunction toOklchWideGamutString(color: Color): string {\n const L = +((color.coords[0] ?? 0) * 100).toFixed(1)\n return color\n .to('oklch')\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${L}%$2`)\n}\n\n/** Same fixed alpha the vendored generator uses for `accentSurface` — a stylistic choice, not a solve. */\nfunction surfaceAlphaFor(appearance: Appearance): number {\n return appearance === 'light' ? 0.8 : 0.5\n}\n\n/**\n * Canvas-tinted low steps for the accent scale, or the untouched scale (and `tinted: false`)\n * when the canvas isn't chromatic enough to bother (gap #12a, ask 1). Only indices\n * `0..CANVAS_TINT_WEIGHTS.length-1` are ever replaced — steps 6-12 come back byte-identical to\n * what Radix generated, in both representations (no lossy sRGB round trip for the untouched\n * wide-gamut steps).\n *\n * `tinted` tells the caller whether to scope `opaqueWhereUnsolvable`'s alpha cap in at all —\n * the cap only makes sense for steps this function actually retargeted; a caller that applied\n * it unconditionally on any custom `background` would wrongly force perfectly fine low-alpha\n * steps opaque on an *achromatic* custom background (an explicit `#000000`/`#f5f5f5`, say),\n * where nothing here ever ran.\n *\n * Re-solves alpha for the tinted indices too: the generator's own `accentScaleAlpha` was\n * solved against the *pre-tint* solid, so once that solid moves the old alpha value no longer\n * reproduces it — left alone, every tinted step would fail `opaqueWhereUnsolvable`'s deltaE\n * check and go opaque, defeating the point (the `soft`-card fill this exists for would never\n * actually be translucent on a tinted canvas). Reuses Radix's own alpha solver so the result\n * matches what the generator itself would have produced had it been given this target directly.\n *\n * `accentSurface` (the `surface` card variant's fill) gets the same treatment: the generator\n * computes it as a fixed-alpha composite of step 2 — index 1, always tinted — over the\n * background, so recomputing it from the *tinted* step 2 at that same fixed alpha (0.8 light,\n * 0.5 dark; not a solve) keeps it in sync with `soft`'s fill instead of staying the old,\n * untinted wash (review-findings.md #13).\n */\nfunction accentScaleForCanvas(\n scale: GeneratedColors,\n background: string,\n appearance: Appearance,\n): Pick<\n GeneratedColors,\n | 'accentScale'\n | 'accentScaleWideGamut'\n | 'accentScaleAlpha'\n | 'accentScaleAlphaWideGamut'\n | 'accentSurface'\n | 'accentSurfaceWideGamut'\n> & { tinted: boolean } {\n try {\n const bg = new Color(background).to('oklch')\n const [, bgChroma] = bg.coords\n if (!bgChroma || bgChroma < CHROMATIC_THRESHOLD)\n return { ...scale, tinted: false }\n const tinted = tintLowStepsFromCanvas(\n scale.accentScale.map((hex) => new Color(hex).to('oklch')),\n bg,\n )\n const accentScale = scale.accentScale.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? tinted[i]!.to('srgb').toString({ format: 'hex' })\n : original,\n ) as typeof scale.accentScale\n const accentScaleWideGamut = scale.accentScaleWideGamut.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i] ? toOklchWideGamutString(tinted[i]!) : original,\n ) as typeof scale.accentScaleWideGamut\n const accentScaleAlpha = scale.accentScaleAlpha.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorSrgb(accentScale[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlpha\n const accentScaleAlphaWideGamut = scale.accentScaleAlphaWideGamut.map(\n (original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorP3(accentScaleWideGamut[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlphaWideGamut\n const surfaceAlpha = surfaceAlphaFor(appearance)\n const accentSurface = getAlphaColorSrgb(\n accentScale[1]!,\n scale.background,\n surfaceAlpha,\n )\n const accentSurfaceWideGamut = getAlphaColorP3(\n accentScaleWideGamut[1]!,\n scale.background,\n surfaceAlpha,\n )\n return {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha,\n accentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted: true,\n }\n } catch {\n return { ...scale, tinted: false }\n }\n}\n\n/** Build the Radix-style scale for one appearance from a single accent color. */\nfunction buildScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): GeneratedColors {\n const background = opts?.background ?? DEFAULTS[appearance].background\n const scale = generateRadixColors({\n appearance,\n accent,\n gray: opts?.neutral\n ? isSparkNeutralFamily(opts.neutral)\n ? NEUTRAL_BASE[opts.neutral]\n : opts.neutral\n : DEFAULTS[appearance].gray,\n background,\n })\n const {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha: canvasAccentScaleAlpha,\n accentScaleAlphaWideGamut: canvasAccentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted,\n } = opts?.background\n ? accentScaleForCanvas(scale, background, appearance)\n : { ...scale, tinted: false }\n const capAlphaSteps = tinted ? CANVAS_TINT_WEIGHTS.length : 0\n const solved = opts?.background\n ? {\n ...scale,\n accentScale,\n accentScaleWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n accentScaleAlpha: opaqueWhereUnsolvable(\n background,\n accentScale,\n canvasAccentScaleAlpha,\n capAlphaSteps,\n ) as typeof scale.accentScaleAlpha,\n accentScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n accentScaleWideGamut,\n canvasAccentScaleAlphaWideGamut,\n capAlphaSteps,\n 'p3',\n ) as typeof scale.accentScaleAlphaWideGamut,\n grayScaleAlpha: opaqueWhereUnsolvable(\n background,\n scale.grayScale,\n scale.grayScaleAlpha,\n ) as typeof scale.grayScaleAlpha,\n grayScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n 0,\n 'p3',\n ) as typeof scale.grayScaleAlphaWideGamut,\n }\n : scale\n return solved\n}\n\n/**\n * Generate the Radix-style accent scale for one appearance from a single accent color.\n *\n * Solid-fill inks come from `readableContrast`, which clears WCAG but can only reach an\n * unreadable APCA on a light, saturated step 9 (a near-black tint at Lc 33-44). Where it\n * does, the fill is darkened until plain white clears 4.5:1 and the scale is re-seeded\n * from that fill — see `darkenForWhiteInk`.\n */\nexport function generateAccentScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): SparkAccentScale {\n const solved = withNativeInks(buildScale(accent, appearance, opts))\n const fill = solved.accentScale[8]!\n if (apcaOn(fill, solved.accentContrast) >= MIN_INK_APCA)\n return withReadableTintInk(solved)\n\n // Re-seed rather than patch step 9: steps 10-12 are derived from it by a fixed\n // lightness offset, so moving 9 alone leaves hover lighter than the fill.\n const reseeded = buildScale(darkenForWhiteInk(fill), appearance, opts)\n const reseededFill = reseeded.accentScale[8]!\n if (contrastOn(reseededFill, WHITE) < MIN_TEXT_CONTRAST)\n return withReadableTintInk(solved)\n return withReadableTintInk({\n ...reseeded,\n accentContrast: WHITE,\n accentOnSolid: WHITE,\n })\n}\n\n/**\n * Step 11 is the ink of every non-solid style, read on the a3 fill and the a4 hover. Both\n * are translucent, so on a soft card (neutral-a3 ≈ neutral-3 over the canvas) they\n * composite darker than on the canvas and Radix's step 11 drops under 4.5:1 (red 3.72:1).\n * Move step 11 away from the fill, holding chroma and hue, until a11 on a4 clears 4.5:1\n * over every neutral knob's step 3, and re-solve a11 from it. Unchanged when it clears.\n */\nexport function withReadableTintInk<T extends GeneratedColors>(scale: T): T {\n const canvas = scale.background\n const dark = appearanceForBackground(canvas) === 'dark'\n const neutral3s = [\n scale.grayScale[2]!,\n ...Object.keys(NEUTRAL_BASE).map((family) => {\n const steps = RadixColors[\n (dark ? `${family}Dark` : family) as keyof typeof RadixColors\n ] as Record<string, string>\n return steps[`${family}3`]!\n }),\n ]\n const hoverFills = neutral3s.map((page) =>\n composite(scale.accentScaleAlpha[3]!, page),\n )\n const inkOn = (step11: string) => {\n const ink = getAlphaColorSrgb(step11, canvas)\n return Math.min(\n ...hoverFills.map((fill) => contrastOn(fill, composite(ink, fill))),\n )\n }\n const original = scale.accentScale[10]!\n if (inkOn(original) >= MIN_TEXT_CONTRAST) return scale\n\n const [lightness, chroma, hue] = new Color(original).to('oklch').coords\n const [, wideChroma, wideHue] = new Color(scale.accentScaleWideGamut[10]!).to(\n 'oklch',\n ).coords\n const toward = contrastOn(canvas, '#000') > contrastOn(canvas, '#fff') ? 0 : 1\n const srgbAt = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (inkOn(srgbAt(mid)) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n const step11 = srgbAt(passes)\n const wideStep11 = toOklchWideGamutString(\n new Color('oklch', [passes, wideChroma ?? 0, wideHue ?? 0]).toGamut({\n space: 'p3',\n }),\n )\n const withStep = <A extends readonly string[]>(steps: A, value: string) =>\n steps.map((v, i) => (i === 10 ? value : v)) as unknown as A\n return {\n ...scale,\n accentScale: withStep(scale.accentScale, step11),\n accentScaleWideGamut: withStep(scale.accentScaleWideGamut, wideStep11),\n accentScaleAlpha: withStep(\n scale.accentScaleAlpha,\n getAlphaColorSrgb(step11, canvas),\n ),\n accentScaleAlphaWideGamut: withStep(\n scale.accentScaleAlphaWideGamut,\n getAlphaColorP3(wideStep11, canvas),\n ),\n }\n}\n\n// Rounds each 8-bit channel term the way browsers blend (see blendAlpha in the vendored\n// generator); the solve lands on the 4.5:1 boundary, where unrounded math is off by ~0.01.\nconst composite = (top: string, bottom: string) => {\n const fg = new Color(top).to('srgb')\n const bg = new Color(bottom).to('srgb').coords\n const a = fg.alpha as number\n const channel = (k: number) =>\n (Math.round(bg[k]! * 255 * (1 - a)) + Math.round(fg.coords[k]! * 255 * a)) /\n 255\n return new Color('srgb', [channel(0), channel(1), channel(2)]).toString({\n format: 'hex',\n })\n}\n\n// accentContrast → 4.5:1 (button text); accentOnSolid → 3:1 (graphical glyph,\n// see SparkAccentScale). Same picker, different WCAG threshold.\nfunction withNativeInks(scale: GeneratedColors): SparkAccentScale {\n const fill = scale.accentScale[8]!\n return {\n ...scale,\n accentContrast: readableContrast(\n fill,\n scale.accentContrast,\n MIN_TEXT_CONTRAST,\n ),\n accentOnSolid: readableContrast(\n fill,\n scale.accentContrast,\n MIN_GRAPHIC_CONTRAST,\n ),\n }\n}\n\n/**\n * Darkest the fill needs to be for white text to clear 4.5:1, holding chroma and hue.\n * Returns the fill unchanged when white already clears — the generator picks its ink\n * from `Math.abs(white.contrastAPCA(fill))`, whose arguments are transposed, so it can\n * reach for a dark tint on a fill white already handles.\n */\nfunction darkenForWhiteInk(fillHex: string): string {\n const fill = new Color(fillHex)\n if (contrastOn(fillHex, WHITE) >= MIN_TEXT_CONTRAST) return fillHex\n const [lightness, chroma, hue] = fill.to('oklch').coords\n const darkened = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (contrastOn(darkened(mid), WHITE) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n return darkened(passes)\n}\n\nconst WHITE = '#fff' // matches the generator's own native contrast literal\nconst MIN_TEXT_CONTRAST = 4.5 // WCAG 1.4.3 — button / label text\nconst MIN_GRAPHIC_CONTRAST = 3 // WCAG 1.4.11 — a check / indicator glyph on a solid fill\n// APCA's floor for large headlines. WCAG 4.5:1 is satisfiable on any step 9 by a dark\n// enough tint, but reads as low as Lc 33 — worse than the white it replaced (Lc 66).\nconst MIN_INK_APCA = 45\n\nconst contrastOn = (bgHex: string, fgHex: string) =>\n new Color(fgHex).contrast(new Color(bgHex), 'WCAG21')\n// Color.js takes the background as the receiver; APCA is polarity-dependent, so the\n// order matters (unlike WCAG 2.x).\nconst apcaOn = (bgHex: string, fgHex: string) =>\n Math.abs(new Color(bgHex).contrastAPCA(new Color(fgHex)))\n\n// Pick the readable foreground on step 9: prefer the generator's native contrast\n// (usually white), else fall to progressively darker accent-tinted inks until one\n// clears `min`. Text uses 4.5:1; a graphical glyph (the checkmark) uses 3:1 — which\n// keeps white on most accents (incl. neutral 3.32:1, red 3.91:1) but flips to a dark\n// tint where white is too weak (e.g. orange, 2.97:1). The vendored generator keeps\n// white at APCA Lc >= 40, which can sit below both WCAG thresholds — hence this guard.\nfunction readableContrast(\n step9Hex: string,\n native: string,\n min: number,\n): string {\n const step9 = new Color(step9Hex)\n const [, chroma, hue] = step9.to('oklch').coords\n const tinted = (lightness: number) =>\n new Color('oklch', [\n lightness,\n Math.max(0.08 * (chroma ?? 0), 0.04),\n hue ?? 0,\n ])\n .to('srgb')\n .toString({ format: 'hex' })\n const ratioOf = (hex: string) => new Color(hex).contrast(step9, 'WCAG21')\n const candidates = [native, tinted(0.25), tinted(0.15)]\n const ratios = candidates.map(ratioOf)\n const passing = ratios.findIndex((r) => r >= min)\n if (passing !== -1) return candidates[passing]!\n\n // The ladder above is a *preference* (least extreme ink first), not a search — it\n // stops at 0.15 and so missed accents that only clear `min` a little darker still\n // (#936c89 landed at 4.47 when 0.05 gives 4.73). Contrast rises monotonically as\n // the ink's lightness moves away from the fill's, so search outward from step 9\n // toward whichever extreme is stronger: if a passing ink exists, this finds it.\n const lightness9 = step9.to('oklch').coords[0] ?? 0.5\n const toward = ratioOf(tinted(0)) >= ratioOf(tinted(1)) ? 0 : 1\n let fails = lightness9\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioOf(tinted(mid)) >= min) passes = mid\n else fails = mid\n }\n const found = tinted(passes)\n if (ratioOf(found) >= min) return found\n\n // No ink of any lightness clears `min` on this fill — step 9 itself is too\n // mid-toned. Return the strongest available; the palette sweep flags the accent.\n const all = [...candidates, tinted(toward)]\n const allRatios = all.map(ratioOf)\n return all[allRatios.indexOf(Math.max(...allRatios))]!\n}\n\n// Spark's own `--red-a4`, not Radix's; spark-default-parity.test.ts pins it to the stylesheet.\nexport const RED_A4: Record<Appearance, string> = {\n light: '#ff25002a',\n dark: '#ff00005a',\n}\n\n/**\n * The hover tints a static `--neutral-placeholder` must clear (field.tsx): gray\n * (`neutralA4`), invalid (`--red-a4`) and brand (`--spark-a4`) for the default accent and\n * every curated one. A runtime brand outside these is not covered.\n */\nexport function staticFieldHoverTints(\n appearance: Appearance,\n neutralA4: string,\n): string[] {\n return [\n neutralA4,\n RED_A4[appearance],\n ...[DEFAULT_ACCENT, ...Object.values(CURATED_ACCENTS)].map(\n (accent) => generateAccentScale(accent, appearance).accentScaleAlpha[3]!,\n ),\n ]\n}\n\n// 4.5 plus headroom for the 8-bit rounding of the painted fill, which moves the ratio ~0.01.\nconst PLACEHOLDER_CONTRAST = 4.55\n\nfunction compositeOver(background: Color, color: string): Color {\n const ink = new Color(color).to('srgb')\n const alpha = ink.alpha as number\n const bg = background.to('srgb').coords\n return new Color(\n 'srgb',\n [0, 1, 2].map((k) => bg[k]! * (1 - alpha) + ink.coords[k]! * alpha) as [\n number,\n number,\n number,\n ],\n )\n}\n\n/**\n * The lightest color between neutral a10 and a11 that reaches `PLACEHOLDER_CONTRAST` on a\n * `card` over `page`, bare and under each of `tints`, clamped to a11, as an sRGB alpha step\n * over `page` (opaque where the alpha solve cannot reproduce it). The card is the deepest\n * surface a field sits on, so clearing it clears the page too.\n */\nexport function solvePlaceholder({\n a10,\n a11,\n page,\n card,\n tints,\n}: {\n a10: string\n a11: string\n page: string\n card: string\n tints: string[]\n}): string {\n const bg = new Color(page)\n const onCard = compositeOver(bg, card)\n const fills = [onCard, ...tints.map((tint) => compositeOver(onCard, tint))]\n const from = compositeOver(bg, a10)\n const to = compositeOver(bg, a11)\n const at = (t: number) => from.mix(to, t, { space: 'oklch' }).to('srgb')\n const inkAt = (t: number) => {\n const solid = at(t).toString({ format: 'hex' })\n return opaqueWhereUnsolvable(\n page,\n [solid],\n [getAlphaColorSrgb(solid, page)],\n )[0]!\n }\n const passes = (t: number) =>\n fills.every(\n (fill) =>\n compositeOver(fill, inkAt(t)).contrast(fill, 'WCAG21') >=\n PLACEHOLDER_CONTRAST,\n )\n let found = 1\n if (passes(0)) found = 0\n else if (passes(1)) {\n let fails = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + found) / 2\n if (passes(mid)) found = mid\n else fails = mid\n }\n }\n return inkAt(found)\n}\n\n/** sRGB `--spark-*` declarations for a generated accent scale. */\nexport function accentSparkVars(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScale,\n scale.accentScaleAlpha,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurface,\n scale.background,\n )\n}\n\n/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */\nexport function accentSparkVarsP3(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScaleWideGamut,\n scale.accentScaleAlphaWideGamut,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurfaceWideGamut,\n scale.background,\n )\n}\n\n/** Radix's `grayColor=\"auto\"` pairing (accent family → gray), mapped to our 6 neutrals. */\nconst NEUTRAL_BY_ACCENT: Record<string, SparkNeutralFamily> = {\n blue: 'slate',\n indigo: 'slate',\n cyan: 'slate',\n violet: 'mauve',\n purple: 'mauve',\n pink: 'mauve',\n red: 'mauve',\n teal: 'sage',\n green: 'sage',\n grass: 'olive',\n orange: 'sand',\n gray: 'gray',\n}\n\n/** Radix's \"auto\" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */\nexport function getMatchingNeutral(accent: string): SparkNeutralFamily {\n const byName = NEUTRAL_BY_ACCENT[accent]\n if (byName) return byName\n try {\n const [, chroma, hueRaw] = new Color(accent).to('oklch').coords\n if (!chroma || chroma < CHROMATIC_THRESHOLD) return 'gray'\n const hue = (((hueRaw ?? 0) % 360) + 360) % 360\n if (hue >= 300 || hue < 35) return 'mauve'\n if (hue < 115) return 'sand'\n if (hue < 145) return 'olive'\n if (hue < 200) return 'sage'\n return 'slate'\n } catch {\n return 'slate'\n }\n}\n\n/**\n * Fill steps (Radix roles 1-5), measured from the surface toward the *scrim* — the pole\n * opposite the ink. Magnitudes are unchanged from the original ink-directed ramp; only\n * the direction flips, so a fill and the text on it move apart instead of together.\n */\nconst INVERTED_FILL_RAMP = [0.06, 0.12, 0.2, 0.3, 0.4]\n\n/** Strongest border step (8, the hover/active edge). 6 is solved per accent; 7 splits them. */\nconst INVERTED_BORDER_TOP = 0.82\n\n/**\n * Ink steps (9-12), measured from the surface toward the ink. Step 9 (the solid fill)\n * lands on the ink itself; 10 eases back for hover; 11-12 are full-strength ink.\n */\nconst INVERTED_INK_RAMP = [1, 0.88, 1, 1]\n\n/**\n * Lowest alpha at which the ink, laid over the surface, is 3:1 distinguishable from it\n * (WCAG 1.4.11) — the floor for a border that has to read as an edge at all.\n *\n * Solved rather than hardcoded because it moves with the accent (0.57-0.72 across the\n * curated set). Contrast against the surface rises monotonically with `t`, so bisection\n * finds the boundary.\n *\n * Targets 1% above the floor rather than landing on it: the step ships as `#rrggbbaa`, and\n * 8-bit rounding on any of the four channels can drop an exact solve back under 3:1.\n */\nfunction solveBorderFloor(surface: Color, ink: Color): number {\n const target = MIN_GRAPHIC_CONTRAST * 1.01\n const ratioAt = (t: number) =>\n new Color(Color.mix(surface, ink, t, { space: 'srgb' })).contrast(\n surface,\n 'WCAG21',\n )\n let fails = 0\n let passes = 1\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioAt(mid) >= target) passes = mid\n else fails = mid\n }\n return passes\n}\n\n/** OKLCH string in the wide-gamut format the generator emits (`oklch(96.1% 0.0155 254.2)`). */\nfunction oklchString(color: Color): string {\n const oklch = color.to('oklch')\n const lightness = +((oklch.coords[0] ?? 0) * 100).toFixed(1)\n return oklch\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${lightness}%$2`)\n}\n\n/**\n * Re-cast a scale for use ON its own solid (step-9) fill — the `inverted` knob.\n *\n * A solid accent control on a solid accent surface is invisible: both paint `--spark-9`.\n * Inverting swaps that pair — step 9 and its contrast ink trade places — so the solid fill\n * and its text are contrast-safe **by construction, for any accent**: `accentContrast` is\n * already the WCAG-checked readable ink for step 9 (`readableContrast`).\n *\n * The non-solid styles need a second idea, because the ink is also the text colour. A fill\n * blended surface→ink moves the background *toward* the foreground, so ink-on-fill contrast\n * falls monotonically with `t` — and measured across the curated set, the highest `t` that\n * still clears 4.5:1 (0.174) sits below the lowest `t` that is 3:1 distinguishable from the\n * surface (0.543). On that line no fill can be both legible and visible, on any accent.\n *\n * So fills blend the other way, toward the **scrim** — the lightness pole opposite the ink,\n * carrying the surface's own hue. Contrast against the ink now *rises* with `t`, which puts\n * every non-solid style at or above the solid pair's ratio for free. Borders keep stepping\n * toward the ink (a scrim-side border caps at ~2:1 on light accents like orange, so it can\n * never clear 1.4.11), starting at the solved 3:1 floor.\n *\n * Appearance-stable on purpose. The generator pins step 9 to the accent itself, so the\n * surface is identical in light and dark — which makes the whole inverted scale identical\n * too, and `[data-inverted]` needs no `.dark` twin.\n *\n * Two things this is deliberately NOT:\n * - **A positional mirror** (`1↔12`). It leaves the solid fill only 2.04:1 from the card in\n * dark and 2.23:1 on a light accent like orange — under the 3:1 graphical floor, i.e. still\n * invisible, which is the bug this exists to fix.\n * - **A regenerated on-surface ramp.** Feeding the surface back in as `background` produces\n * off-hue overlays (a warm wash on a blue card) and ink at 3.35:1.\n *\n * Values are literals, not `var(--spark-N)` aliases: a scale can't re-point to itself\n * (`--spark-1: var(--spark-12)` next to `--spark-12: var(--spark-1)` is a cycle, and CSS\n * discards both).\n */\nexport function invertAccentScale(scale: SparkAccentScale): SparkAccentScale {\n const surface = scale.accentScale[8]! // the solid fill this scale will sit on\n const ink = scale.accentContrast // already AA-checked against that fill\n const surfaceColor = new Color(surface)\n const inkColor = new Color(ink)\n\n // The scrim: opposite the ink in lightness, tinted with the surface's own hue by the\n // same rule readableContrast uses for its ink ladder. Derived from the surface, so this\n // introduces no new palette input.\n const [surfaceLightness, surfaceChroma, surfaceHue] =\n surfaceColor.to('oklch').coords\n const inkLightness = inkColor.to('oklch').coords[0] ?? 0\n const scrimColor = new Color('oklch', [\n inkLightness > (surfaceLightness ?? 0) ? 0.12 : 0.98,\n Math.max(0.08 * (surfaceChroma ?? 0), 0.02),\n surfaceHue ?? 0,\n ])\n\n const borderFloor = solveBorderFloor(surfaceColor, inkColor)\n const ramp: { toward: Color; t: number }[] = [\n ...INVERTED_FILL_RAMP.map((t) => ({ toward: scrimColor, t })),\n { toward: inkColor, t: borderFloor },\n { toward: inkColor, t: (borderFloor + INVERTED_BORDER_TOP) / 2 },\n { toward: inkColor, t: INVERTED_BORDER_TOP },\n ...INVERTED_INK_RAMP.map((t) => ({ toward: inkColor, t })),\n ]\n\n // sRGB, not OKLab: the browser composites `--spark-aN` as source-over in gamma sRGB, so\n // mixing the opaque twin in any other space makes `--spark-N` a different colour from it\n // (measured up to dE2000 2.8 on the old OKLab mix, over ALPHA_SOLVE_TOLERANCE).\n const blend = ({ toward, t }: { toward: Color; t: number }) =>\n new Color(Color.mix(surfaceColor, toward, t, { space: 'srgb' }))\n const steps = ramp.map((s) =>\n blend(s).to('srgb').toString({ format: 'hex' }),\n ) as typeof scale.accentScale\n const stepsWideGamut = ramp.map((s) =>\n oklchString(blend(s)),\n ) as typeof scale.accentScaleWideGamut\n // Mixing surface→X at t in sRGB is the same colour as X at alpha t over the surface, so\n // each alpha step is just its own pole at rising opacity — no solve needed.\n const alpha = ramp.map(({ toward, t }) => {\n const translucent = toward.clone()\n translucent.alpha = t\n return translucent.to('srgb').toString({ format: 'hex' })\n }) as typeof scale.accentScaleAlpha\n const alphaWideGamut = ramp.map(({ toward, t }) => {\n const translucent = toward.clone().to('p3')\n translucent.alpha = t\n return translucent\n .toString({ precision: 4 })\n .replace('color(p3 ', 'color(display-p3 ')\n }) as typeof scale.accentScaleAlphaWideGamut\n return {\n ...scale,\n accentScale: steps,\n accentScaleWideGamut: stepsWideGamut,\n accentScaleAlpha: alpha,\n accentScaleAlphaWideGamut: alphaWideGamut,\n // Ink on the inverted solid fill is the accent itself — the other half of the swapped pair.\n accentContrast: surface,\n accentOnSolid: surface,\n accentSurface: steps[1]!,\n accentSurfaceWideGamut: stepsWideGamut[1]!,\n }\n}\n\n/**\n * `neutralColor=\"auto\"` resolution: prefer the canvas's own tint when it has one — a\n * chromatic `background` seeds a visibly warmer/cooler ramp than bucketing it into one\n * of the 6 named families would (see gap #12). Falls back to accent-matched bucketing\n * when there's no tinted canvas to pull from.\n */\nexport function resolveAutoNeutral(\n accent: string,\n background?: string,\n): string {\n if (background) {\n try {\n const [, chroma] = new Color(background).to('oklch').coords\n if (chroma && chroma >= CHROMATIC_THRESHOLD) return background\n } catch {\n // fall through to accent-matched neutral\n }\n }\n return getMatchingNeutral(accent)\n}\n\n/**\n * Whether a string parses as a color at all — for validating a custom `neutralColor`\n * before it reaches the generator. An unparseable seed (e.g. a typo'd family name like\n * `\"slat\"`) would otherwise throw deep inside `generateRadixColors`, taking the whole\n * accent scale down with it via the caller's shared try/catch.\n */\nexport function isParsableColor(value: string): boolean {\n try {\n new Color(value).to('oklch')\n return true\n } catch {\n return false\n }\n}\n\n/** The 27 tokens every scale family declares, under an arbitrary prefix. */\nfunction scaleVars(\n prefix: string,\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n): string[] {\n return [\n ...steps.map((c, i) => `--${prefix}-${i + 1}: ${c};`),\n ...alpha.map((c, i) => `--${prefix}-a${i + 1}: ${c};`),\n `--${prefix}-contrast: ${contrast};`,\n `--${prefix}-on-solid: ${onSolid};`,\n `--${prefix}-surface: ${surface};`,\n ]\n}\n\nfunction sparkVars(\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n background: string,\n): string[] {\n const indicator = steps[8]! // step 9 — the brand step (matches --spark-indicator/-track)\n return [\n ...scaleVars('spark', steps, alpha, contrast, onSolid, surface),\n `--spark-background: ${background};`,\n `--spark-indicator: ${indicator};`,\n `--spark-track: ${indicator};`,\n ]\n}\n\n/**\n * The same `--neutral-*` tokens `neutralSparkVars` emits, pointed back at a static Radix\n * family instead of a generated scale — the counterpart for an appearance that has *no*\n * custom canvas of its own.\n *\n * Ambient (no explicit `appearance`) mode emits both appearances' scales into one unlayered\n * `<style>`, but a custom canvas only regenerates neutrals for the appearance it belongs to.\n * Left undeclared, the other block doesn't fall back — nothing resets a custom property, so\n * its sibling's canvas-tinted values simply stay in effect there, and being unlayered they\n * outrank `[data-neutral-color]`'s own `@layer base` mapping too. Declaring the same token\n * set closes that. `var(--<family>-*)` is itself appearance-aware (Radix ships the dark\n * scales under `.dark`), so one declaration resolves correctly wherever the block applies.\n */\nexport function neutralFamilyVars(\n family: Exclude<SparkNeutral, 'auto'>,\n): string[] {\n const ref = (suffix: string) => `var(--${family}-${suffix})`\n return [\n ...scaleVars(\n 'neutral',\n Array.from({ length: 12 }, (_, i) => ref(`${i + 1}`)),\n Array.from({ length: 12 }, (_, i) => ref(`a${i + 1}`)),\n // The companion tokens' own static defaults, from spark-default.css's .light/.dark.\n // `-contrast` is per-family there so [data-neutral-color] swaps the ink with the\n // ramp; referencing it rather than inlining #fff keeps the CSS the single source\n // of truth if the two appearances ever diverge again.\n `var(--${family}-contrast)`,\n '#fff',\n 'var(--neutral-2)',\n ),\n `--neutral-placeholder: ${ref('placeholder')};`,\n ]\n}\n\n// Emitted as sRGB on the P3 path too; the solve is a WCAG ratio, which is defined in sRGB.\nconst neutralPlaceholderVar = (scale: SparkAccentScale) =>\n `--neutral-placeholder: ${solvePlaceholder({\n a10: scale.grayScaleAlpha[9]!,\n a11: scale.grayScaleAlpha[10]!,\n page: scale.background,\n card: scale.grayScale[2]!,\n tints: [\n scale.grayScaleAlpha[3]!,\n RED_A4[appearanceForBackground(scale.background)],\n scale.accentScaleAlpha[3]!,\n ],\n })};`\n\n/**\n * `--neutral-*` declarations from the gray scale the generator produces alongside the\n * accent — normally discarded, because `[data-neutral-color]` points `--neutral-*` at a\n * static Radix family. Worth consuming only when the canvas is custom: the static\n * families' alpha steps are pre-baked against white/black, so on any other background\n * they composite wrong (every gray text/border reads through `--neutral-a*`).\n *\n * `--neutral-surface` is step 2, matching the static families' own convention rather\n * than the generator's translucent `graySurface`.\n */\nexport function neutralSparkVars(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScale,\n scale.grayScaleAlpha,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScale[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n\n/** Wide-gamut (P3) twin of `neutralSparkVars`. */\nexport function neutralSparkVarsP3(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScaleWideGamut[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"accent-theme.js","names":[],"sources":["../../src/lib/accent-theme.ts"],"sourcesContent":["// Accent is the one runtime knob (colour math, not a CSS-var swap): generate the\n// `--spark-*` scale with the same Radix algorithm live portals use, apply as inline vars.\nimport * as RadixColors from '@radix-ui/colors'\nimport Color from 'colorjs.io'\nimport {\n generateRadixColors,\n getAlphaColorP3,\n getAlphaColorSrgb,\n} from './generate-radix-colors'\nimport {\n isSparkNeutralFamily,\n type SparkNeutral,\n type SparkNeutralFamily,\n} from './theme-types'\n\n/** Shape returned by the vendored Radix generator (it doesn't export a named type). */\nexport type GeneratedColors = ReturnType<typeof generateRadixColors>\n\n/**\n * generateAccentScale output: the raw scale, but with two readable foregrounds on the\n * solid (step-9) fill, each meeting a different WCAG bar:\n * - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.\n * - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).\n * A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white\n * where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark\n * accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for\n * the glyph over-darkened it (a black tick on a red/neutral checkbox).\n */\nexport type SparkAccentScale = GeneratedColors & { accentOnSolid: string }\n\nexport type Appearance = 'light' | 'dark'\n\n/** The epilot brand: the no-attribute default `--spark-*` in spark-default.css. */\nexport const DEFAULT_ACCENT = '#005EB4'\n\n/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */\nexport const CURATED_ACCENTS: Record<string, string> = {\n blue: RadixColors.blue.blue9,\n indigo: RadixColors.indigo.indigo9,\n violet: RadixColors.violet.violet9,\n purple: RadixColors.purple.purple9,\n cyan: RadixColors.cyan.cyan9,\n teal: RadixColors.teal.teal9,\n green: RadixColors.green.green9,\n grass: RadixColors.grass.grass9,\n orange: RadixColors.orange.orange9,\n red: RadixColors.red.red9,\n pink: RadixColors.pink.pink9,\n gray: RadixColors.gray.gray9,\n}\n\n/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */\nconst DEFAULTS: Record<Appearance, { gray: string; background: string }> = {\n light: { gray: '#333333', background: '#ffffff' },\n // Matches ECP DesignUtils' DEFAULT_DARK_BACKGROUND_COLOR -- the alpha ramp\n // is composited against this, so it must match what's actually painted.\n dark: { gray: '#8b8d98', background: '#000000' },\n}\n\n/** Radix step-9 per neutral family — the generator's `gray` seed, so a regenerated\n neutral lands on the family `[data-neutral-color]` would have selected statically. */\nconst NEUTRAL_BASE: Record<SparkNeutralFamily, string> = {\n slate: RadixColors.slate.slate9,\n mauve: RadixColors.mauve.mauve9,\n gray: RadixColors.gray.gray9,\n sage: RadixColors.sage.sage9,\n olive: RadixColors.olive.olive9,\n sand: RadixColors.sand.sand9,\n}\n\nexport interface GenerateScaleOptions {\n /**\n * Page canvas the scale is composited against. Drives the lightness curve of all 12\n * steps AND every alpha step (each is solved as `target = bg·(1-α) + fg·α`), so it\n * must match what's actually painted or the alpha ramp renders wrong.\n */\n background?: string\n /**\n * Neutral family, or an arbitrary color to seed the neutral scale (regenerated against\n * `background`, same as a named family). Pass a resolved value, not `'auto'` — this\n * option isn't itself typed to exclude it (an open string union can't forbid one\n * literal), but nothing in `<Theme>` ever forwards `'auto'` this far.\n */\n neutral?: SparkNeutral\n}\n\n/** Which appearance a canvas belongs to, by OKLCH lightness. */\nexport function appearanceForBackground(background: string): Appearance {\n const [lightness] = new Color(background).to('oklch').coords\n return (lightness ?? 1) >= 0.5 ? 'light' : 'dark'\n}\n\n/** deltaE2000 ≈ 2 is the just-noticeable bar — a good solve lands far inside it. */\nconst ALPHA_SOLVE_TOLERANCE = 2\n\n/**\n * OKLCH chroma below which a color reads as neutral rather than tinted — shared by every\n * \"is this canvas/accent actually chromatic\" check (getMatchingNeutral, resolveAutoNeutral,\n * accentScaleForCanvas).\n */\nconst CHROMATIC_THRESHOLD = 0.04\n\n/**\n * An alpha step past this needs to cover the canvas almost completely to reproduce its\n * target — at that point it isn't a tint anymore, and the opaque solid is both cheaper and\n * more predictable. Only applied to the accent ramp's canvas-tinted low steps (gap #12a,\n * ask 2 — specifically the steps `tintLowStepsFromCanvas` retargets; steps 6-12 are by-design\n * high-alpha ink/solid tokens even on plain white — capping *those* would be a regression, not\n * a fix). Same shape as the deltaE check below, widened from \"could not solve\" to \"solved, but\n * not usefully\".\n */\nconst ALPHA_CAP = 0.5\n\n/**\n * Replace alpha steps the solver could not actually solve — or, within `capAlphaSteps` leading\n * steps, solved only at a near-opaque alpha — with their opaque solid.\n *\n * Each `aN` is solved so that compositing it over the background reproduces step N\n * (`target = bg·(1-α) + fg·α`). From a saturated canvas a desaturated ink target is\n * unreachable within alpha bounds, and Radix's solver **degrades silently** rather than\n * failing — on `#FFD400` it returns `#0000ff39` for slate a11, which composites to\n * 1.66:1 body text where the solid step is a correct 9.03:1. Separately, even a solve that\n * *succeeds* can need alpha past `ALPHA_CAP` to get there — technically correct only over\n * that exact background, and no longer a usable \"tint\" anywhere else (gap #12a, ask 2).\n *\n * `space` must match whatever space `alpha` was actually solved in — `getAlphaColorP3` solves\n * per-channel in P3, and verifying that against an sRGB composite rejects perfectly good\n * solves (they were never trying to reproduce the sRGB composite in the first place).\n *\n * So verify the solve and fall back to the opaque value where either check fails. On\n * white/black every step solves exactly at low alpha, so the default output — and portal\n * parity — is untouched either way.\n */\nfunction opaqueWhereUnsolvable(\n background: string,\n solid: string[],\n alpha: string[],\n capAlphaSteps = 0,\n space: 'srgb' | 'p3' = 'srgb',\n): string[] {\n let bg: number[]\n try {\n bg = new Color(background).to(space).coords\n } catch {\n return alpha\n }\n return alpha.map((step, i) => {\n const target = solid[i]\n if (!target) return step\n try {\n const ink = new Color(step)\n const fg = ink.to(space).coords\n const a = ink.alpha as number\n if (i < capAlphaSteps && a > ALPHA_CAP) return target\n const composited = new Color(\n space,\n [0, 1, 2].map((k) => bg[k]! * (1 - a) + fg[k]! * a) as [\n number,\n number,\n number,\n ],\n )\n return composited.deltaE(new Color(target), '2000') <=\n ALPHA_SOLVE_TOLERANCE\n ? step\n : target\n } catch {\n return target\n }\n })\n}\n\n/**\n * Weight (0–1) each of the accent scale's first 5 steps is pulled toward the canvas's own\n * hue/chroma; step 6 and up (borders, solid, text) stay the true accent untouched.\n */\nconst CANVAS_TINT_WEIGHTS = [1, 0.8, 0.6, 0.4, 0.2]\n\n/**\n * Pull the accent scale's low steps toward the canvas's own color (gap #12a, ask 1): Radix's\n * generator sets every step's hue/chroma from the accent itself and only re-curves lightness\n * against the background, so on a chromatic canvas the low steps (app/component background\n * territory) stay a flat wash of the accent instead of a tint of the page — right where the\n * now-canvas-tinted neutral scale (gap #12) visibly disagrees with it. Step 9 (the solid,\n * brand-defining step) and everything above step 5 is left exactly as Radix generated it.\n *\n * Mixed in OKLab (rectangular a/b), not OKLCH (polar hue + chroma): a circular hue\n * interpolation between two far-apart hues (a blue accent on a yellow-green canvas, say) has\n * to sweep through whatever unrelated hues sit on the \"shorter\" arc between them — a vivid\n * green/teal/cyan that belongs to neither endpoint. Linearly mixing a/b instead moves along\n * the straight line between the two points, which naturally desaturates toward neutral as the\n * two colors pull in more different directions, rather than swinging through a third, unrelated\n * hue. Lightness (shared between OKLab and OKLCH) is untouched either way — Radix's own\n * background-aware curve already owns it.\n *\n * Takes/returns `Color` objects rather than strings so callers can reuse the same tinted\n * coordinates for both the sRGB and wide-gamut representations without a lossy round trip.\n */\nfunction tintLowStepsFromCanvas(\n solidScale: readonly Color[],\n bg: Color,\n): Color[] {\n const [, bgA, bgB] = bg.to('oklab').coords\n return solidScale.map((step, i) => {\n const weight = CANVAS_TINT_WEIGHTS[i]\n if (!weight) return step\n const [lightness, a, b] = step.to('oklab').coords\n return new Color('oklab', [\n lightness ?? 0,\n (a ?? 0) * (1 - weight) + (bgA ?? 0) * weight,\n (b ?? 0) * (1 - weight) + (bgB ?? 0) * weight,\n ])\n })\n}\n\n/** Mirrors generate-radix-colors.tsx's private toOklchString (not exported by the vendored file). */\nfunction toOklchWideGamutString(color: Color): string {\n const L = +((color.coords[0] ?? 0) * 100).toFixed(1)\n return color\n .to('oklch')\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${L}%$2`)\n}\n\n/** Same fixed alpha the vendored generator uses for `accentSurface` — a stylistic choice, not a solve. */\nfunction surfaceAlphaFor(appearance: Appearance): number {\n return appearance === 'light' ? 0.8 : 0.5\n}\n\n/**\n * Canvas-tinted low steps for the accent scale, or the untouched scale (and `tinted: false`)\n * when the canvas isn't chromatic enough to bother (gap #12a, ask 1). Only indices\n * `0..CANVAS_TINT_WEIGHTS.length-1` are ever replaced — steps 6-12 come back byte-identical to\n * what Radix generated, in both representations (no lossy sRGB round trip for the untouched\n * wide-gamut steps).\n *\n * `tinted` tells the caller whether to scope `opaqueWhereUnsolvable`'s alpha cap in at all —\n * the cap only makes sense for steps this function actually retargeted; a caller that applied\n * it unconditionally on any custom `background` would wrongly force perfectly fine low-alpha\n * steps opaque on an *achromatic* custom background (an explicit `#000000`/`#f5f5f5`, say),\n * where nothing here ever ran.\n *\n * Re-solves alpha for the tinted indices too: the generator's own `accentScaleAlpha` was\n * solved against the *pre-tint* solid, so once that solid moves the old alpha value no longer\n * reproduces it — left alone, every tinted step would fail `opaqueWhereUnsolvable`'s deltaE\n * check and go opaque, defeating the point (the `soft`-card fill this exists for would never\n * actually be translucent on a tinted canvas). Reuses Radix's own alpha solver so the result\n * matches what the generator itself would have produced had it been given this target directly.\n *\n * `accentSurface` (the `surface` card variant's fill) gets the same treatment: the generator\n * computes it as a fixed-alpha composite of step 2 — index 1, always tinted — over the\n * background, so recomputing it from the *tinted* step 2 at that same fixed alpha (0.8 light,\n * 0.5 dark; not a solve) keeps it in sync with `soft`'s fill instead of staying the old,\n * untinted wash (review-findings.md #13).\n */\nfunction accentScaleForCanvas(\n scale: GeneratedColors,\n background: string,\n appearance: Appearance,\n): Pick<\n GeneratedColors,\n | 'accentScale'\n | 'accentScaleWideGamut'\n | 'accentScaleAlpha'\n | 'accentScaleAlphaWideGamut'\n | 'accentSurface'\n | 'accentSurfaceWideGamut'\n> & { tinted: boolean } {\n try {\n const bg = new Color(background).to('oklch')\n const [, bgChroma] = bg.coords\n if (!bgChroma || bgChroma < CHROMATIC_THRESHOLD)\n return { ...scale, tinted: false }\n const tinted = tintLowStepsFromCanvas(\n scale.accentScale.map((hex) => new Color(hex).to('oklch')),\n bg,\n )\n const accentScale = scale.accentScale.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? tinted[i]!.to('srgb').toString({ format: 'hex' })\n : original,\n ) as typeof scale.accentScale\n const accentScaleWideGamut = scale.accentScaleWideGamut.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i] ? toOklchWideGamutString(tinted[i]!) : original,\n ) as typeof scale.accentScaleWideGamut\n const accentScaleAlpha = scale.accentScaleAlpha.map((original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorSrgb(accentScale[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlpha\n const accentScaleAlphaWideGamut = scale.accentScaleAlphaWideGamut.map(\n (original, i) =>\n CANVAS_TINT_WEIGHTS[i]\n ? getAlphaColorP3(accentScaleWideGamut[i]!, scale.background)\n : original,\n ) as typeof scale.accentScaleAlphaWideGamut\n const surfaceAlpha = surfaceAlphaFor(appearance)\n const accentSurface = getAlphaColorSrgb(\n accentScale[1]!,\n scale.background,\n surfaceAlpha,\n )\n const accentSurfaceWideGamut = getAlphaColorP3(\n accentScaleWideGamut[1]!,\n scale.background,\n surfaceAlpha,\n )\n return {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha,\n accentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted: true,\n }\n } catch {\n return { ...scale, tinted: false }\n }\n}\n\n/** Build the Radix-style scale for one appearance from a single accent color. */\nfunction buildScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): GeneratedColors {\n const background = opts?.background ?? DEFAULTS[appearance].background\n const scale = generateRadixColors({\n appearance,\n accent,\n gray: opts?.neutral\n ? isSparkNeutralFamily(opts.neutral)\n ? NEUTRAL_BASE[opts.neutral]\n : opts.neutral\n : DEFAULTS[appearance].gray,\n background,\n })\n const {\n accentScale,\n accentScaleWideGamut,\n accentScaleAlpha: canvasAccentScaleAlpha,\n accentScaleAlphaWideGamut: canvasAccentScaleAlphaWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n tinted,\n } = opts?.background\n ? accentScaleForCanvas(scale, background, appearance)\n : { ...scale, tinted: false }\n const capAlphaSteps = tinted ? CANVAS_TINT_WEIGHTS.length : 0\n const solved = opts?.background\n ? {\n ...scale,\n accentScale,\n accentScaleWideGamut,\n accentSurface,\n accentSurfaceWideGamut,\n accentScaleAlpha: opaqueWhereUnsolvable(\n background,\n accentScale,\n canvasAccentScaleAlpha,\n capAlphaSteps,\n ) as typeof scale.accentScaleAlpha,\n accentScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n accentScaleWideGamut,\n canvasAccentScaleAlphaWideGamut,\n capAlphaSteps,\n 'p3',\n ) as typeof scale.accentScaleAlphaWideGamut,\n grayScaleAlpha: opaqueWhereUnsolvable(\n background,\n scale.grayScale,\n scale.grayScaleAlpha,\n ) as typeof scale.grayScaleAlpha,\n grayScaleAlphaWideGamut: opaqueWhereUnsolvable(\n background,\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n 0,\n 'p3',\n ) as typeof scale.grayScaleAlphaWideGamut,\n }\n : scale\n return solved\n}\n\n/**\n * Generate the Radix-style accent scale for one appearance from a single accent color.\n *\n * Solid-fill inks come from `readableContrast`, which clears WCAG but can only reach an\n * unreadable APCA on a light, saturated step 9 (a near-black tint at Lc 33-44). Where it\n * does, the fill is darkened until plain white clears 4.5:1 and the scale is re-seeded\n * from that fill — see `darkenForWhiteInk`.\n */\nexport function generateAccentScale(\n accent: string,\n appearance: Appearance,\n opts?: GenerateScaleOptions,\n): SparkAccentScale {\n const solved = withNativeInks(buildScale(accent, appearance, opts))\n const fill = solved.accentScale[8]!\n if (apcaOn(fill, solved.accentContrast) >= MIN_INK_APCA)\n return withReadableTintInk(solved)\n\n // Re-seed rather than patch step 9: steps 10-12 are derived from it by a fixed\n // lightness offset, so moving 9 alone leaves hover lighter than the fill.\n const reseeded = buildScale(darkenForWhiteInk(fill), appearance, opts)\n const reseededFill = reseeded.accentScale[8]!\n if (contrastOn(reseededFill, WHITE) < MIN_TEXT_CONTRAST)\n return withReadableTintInk(solved)\n return withReadableTintInk({\n ...reseeded,\n accentContrast: WHITE,\n accentOnSolid: WHITE,\n })\n}\n\n/**\n * Step 11 is the ink of every non-solid style, read on the a3 fill and the a4 hover. Both\n * are translucent, so on a soft card (neutral-a3 ≈ neutral-3 over the canvas) they\n * composite darker than on the canvas and Radix's step 11 drops under 4.5:1 (red 3.72:1).\n * Move step 11 away from the fill, holding chroma and hue, until a11 on a4 clears 4.5:1\n * over every neutral knob's step 3, and re-solve a11 from it. Unchanged when it clears.\n */\nexport function withReadableTintInk<T extends GeneratedColors>(scale: T): T {\n const canvas = scale.background\n const dark = appearanceForBackground(canvas) === 'dark'\n const neutral3s = [\n scale.grayScale[2]!,\n ...Object.keys(NEUTRAL_BASE).map((family) => {\n const steps = RadixColors[\n (dark ? `${family}Dark` : family) as keyof typeof RadixColors\n ] as Record<string, string>\n return steps[`${family}3`]!\n }),\n ]\n const hoverFills = neutral3s.map((page) =>\n composite(scale.accentScaleAlpha[3]!, page),\n )\n const inkOn = (step11: string) => {\n const ink = getAlphaColorSrgb(step11, canvas)\n return Math.min(\n ...hoverFills.map((fill) => contrastOn(fill, composite(ink, fill))),\n )\n }\n const original = scale.accentScale[10]!\n if (inkOn(original) >= MIN_TEXT_CONTRAST) return scale\n\n const [lightness, chroma, hue] = new Color(original).to('oklch').coords\n const [, wideChroma, wideHue] = new Color(scale.accentScaleWideGamut[10]!).to(\n 'oklch',\n ).coords\n const toward = contrastOn(canvas, '#000') > contrastOn(canvas, '#fff') ? 0 : 1\n const srgbAt = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (inkOn(srgbAt(mid)) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n const step11 = srgbAt(passes)\n const wideStep11 = toOklchWideGamutString(\n new Color('oklch', [passes, wideChroma ?? 0, wideHue ?? 0]).toGamut({\n space: 'p3',\n }),\n )\n const withStep = <A extends readonly string[]>(steps: A, value: string) =>\n steps.map((v, i) => (i === 10 ? value : v)) as unknown as A\n return {\n ...scale,\n accentScale: withStep(scale.accentScale, step11),\n accentScaleWideGamut: withStep(scale.accentScaleWideGamut, wideStep11),\n accentScaleAlpha: withStep(\n scale.accentScaleAlpha,\n getAlphaColorSrgb(step11, canvas),\n ),\n accentScaleAlphaWideGamut: withStep(\n scale.accentScaleAlphaWideGamut,\n getAlphaColorP3(wideStep11, canvas),\n ),\n }\n}\n\n// Rounds each 8-bit channel term the way browsers blend (see blendAlpha in the vendored\n// generator); the solve lands on the 4.5:1 boundary, where unrounded math is off by ~0.01.\nconst composite = (top: string, bottom: string) => {\n const fg = new Color(top).to('srgb')\n const bg = new Color(bottom).to('srgb').coords\n const a = fg.alpha as number\n const channel = (k: number) =>\n (Math.round(bg[k]! * 255 * (1 - a)) + Math.round(fg.coords[k]! * 255 * a)) /\n 255\n return new Color('srgb', [channel(0), channel(1), channel(2)]).toString({\n format: 'hex',\n })\n}\n\n// accentContrast → 4.5:1 (button text); accentOnSolid → 3:1 (graphical glyph,\n// see SparkAccentScale). Same picker, different WCAG threshold.\nfunction withNativeInks(scale: GeneratedColors): SparkAccentScale {\n const fill = scale.accentScale[8]!\n return {\n ...scale,\n accentContrast: readableContrast(\n fill,\n scale.accentContrast,\n MIN_TEXT_CONTRAST,\n ),\n accentOnSolid: readableContrast(\n fill,\n scale.accentContrast,\n MIN_GRAPHIC_CONTRAST,\n ),\n }\n}\n\n/**\n * Darkest the fill needs to be for white text to clear 4.5:1, holding chroma and hue.\n * Returns the fill unchanged when white already clears — the generator picks its ink\n * from `Math.abs(white.contrastAPCA(fill))`, whose arguments are transposed, so it can\n * reach for a dark tint on a fill white already handles.\n */\nfunction darkenForWhiteInk(fillHex: string): string {\n const fill = new Color(fillHex)\n if (contrastOn(fillHex, WHITE) >= MIN_TEXT_CONTRAST) return fillHex\n const [lightness, chroma, hue] = fill.to('oklch').coords\n const darkened = (l: number) =>\n new Color('oklch', [l, chroma ?? 0, hue ?? 0])\n .toGamut({ space: 'srgb' })\n .to('srgb')\n .toString({ format: 'hex' })\n let fails = lightness ?? 0.5\n let passes = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (contrastOn(darkened(mid), WHITE) >= MIN_TEXT_CONTRAST) passes = mid\n else fails = mid\n }\n return darkened(passes)\n}\n\nconst WHITE = '#fff' // matches the generator's own native contrast literal\nconst MIN_TEXT_CONTRAST = 4.5 // WCAG 1.4.3 — button / label text\nconst MIN_GRAPHIC_CONTRAST = 3 // WCAG 1.4.11 — a check / indicator glyph on a solid fill\n// APCA's floor for large headlines. WCAG 4.5:1 is satisfiable on any step 9 by a dark\n// enough tint, but reads as low as Lc 33 — worse than the white it replaced (Lc 66).\nconst MIN_INK_APCA = 45\n\nconst contrastOn = (bgHex: string, fgHex: string) =>\n new Color(fgHex).contrast(new Color(bgHex), 'WCAG21')\n// Color.js takes the background as the receiver; APCA is polarity-dependent, so the\n// order matters (unlike WCAG 2.x).\nconst apcaOn = (bgHex: string, fgHex: string) =>\n Math.abs(new Color(bgHex).contrastAPCA(new Color(fgHex)))\n\n// Pick the readable foreground on step 9: prefer the generator's native contrast\n// (usually white), else fall to progressively darker accent-tinted inks until one\n// clears `min`. Text uses 4.5:1; a graphical glyph (the checkmark) uses 3:1 — which\n// keeps white on most accents (incl. neutral 3.32:1, red 3.91:1) but flips to a dark\n// tint where white is too weak (e.g. orange, 2.97:1). The vendored generator keeps\n// white at APCA Lc >= 40, which can sit below both WCAG thresholds — hence this guard.\nfunction readableContrast(\n step9Hex: string,\n native: string,\n min: number,\n): string {\n const step9 = new Color(step9Hex)\n const [, chroma, hue] = step9.to('oklch').coords\n const tinted = (lightness: number) =>\n new Color('oklch', [\n lightness,\n Math.max(0.08 * (chroma ?? 0), 0.04),\n hue ?? 0,\n ])\n .to('srgb')\n .toString({ format: 'hex' })\n const ratioOf = (hex: string) => new Color(hex).contrast(step9, 'WCAG21')\n const candidates = [native, tinted(0.25), tinted(0.15)]\n const ratios = candidates.map(ratioOf)\n const passing = ratios.findIndex((r) => r >= min)\n if (passing !== -1) return candidates[passing]!\n\n // The ladder above is a *preference* (least extreme ink first), not a search — it\n // stops at 0.15 and so missed accents that only clear `min` a little darker still\n // (#936c89 landed at 4.47 when 0.05 gives 4.73). Contrast rises monotonically as\n // the ink's lightness moves away from the fill's, so search outward from step 9\n // toward whichever extreme is stronger: if a passing ink exists, this finds it.\n const lightness9 = step9.to('oklch').coords[0] ?? 0.5\n const toward = ratioOf(tinted(0)) >= ratioOf(tinted(1)) ? 0 : 1\n let fails = lightness9\n let passes = toward\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioOf(tinted(mid)) >= min) passes = mid\n else fails = mid\n }\n const found = tinted(passes)\n if (ratioOf(found) >= min) return found\n\n // No ink of any lightness clears `min` on this fill — step 9 itself is too\n // mid-toned. Return the strongest available; the palette sweep flags the accent.\n const all = [...candidates, tinted(toward)]\n const allRatios = all.map(ratioOf)\n return all[allRatios.indexOf(Math.max(...allRatios))]!\n}\n\n// Radix `--red-a4`. The invalid-field hover paints that step, and the placeholder\n// solver has to clear it. spark-default.css does not override the red scale.\nexport const RED_A4: Record<Appearance, string> = {\n light: '#ff000824',\n dark: '#fe0a3b44',\n}\n\n/**\n * The hover tints a static `--neutral-placeholder` must clear (field.tsx): gray\n * (`neutralA4`), invalid (`--red-a4`) and brand (`--spark-a4`) for the default accent and\n * every curated one. A runtime brand outside these is not covered.\n */\nexport function staticFieldHoverTints(\n appearance: Appearance,\n neutralA4: string,\n): string[] {\n return [\n neutralA4,\n RED_A4[appearance],\n ...[DEFAULT_ACCENT, ...Object.values(CURATED_ACCENTS)].map(\n (accent) => generateAccentScale(accent, appearance).accentScaleAlpha[3]!,\n ),\n ]\n}\n\n// 4.5 plus headroom for the 8-bit rounding of the painted fill, which moves the ratio ~0.01.\nconst PLACEHOLDER_CONTRAST = 4.55\n\nfunction compositeOver(background: Color, color: string): Color {\n const ink = new Color(color).to('srgb')\n const alpha = ink.alpha as number\n const bg = background.to('srgb').coords\n return new Color(\n 'srgb',\n [0, 1, 2].map((k) => bg[k]! * (1 - alpha) + ink.coords[k]! * alpha) as [\n number,\n number,\n number,\n ],\n )\n}\n\n/**\n * The lightest color between neutral a10 and a11 that reaches `PLACEHOLDER_CONTRAST` on a\n * `card` over `page`, bare and under each of `tints`, clamped to a11, as an sRGB alpha step\n * over `page` (opaque where the alpha solve cannot reproduce it). The card is the deepest\n * surface a field sits on, so clearing it clears the page too.\n */\nexport function solvePlaceholder({\n a10,\n a11,\n page,\n card,\n tints,\n}: {\n a10: string\n a11: string\n page: string\n card: string\n tints: string[]\n}): string {\n const bg = new Color(page)\n const onCard = compositeOver(bg, card)\n const fills = [onCard, ...tints.map((tint) => compositeOver(onCard, tint))]\n const from = compositeOver(bg, a10)\n const to = compositeOver(bg, a11)\n const at = (t: number) => from.mix(to, t, { space: 'oklch' }).to('srgb')\n const inkAt = (t: number) => {\n const solid = at(t).toString({ format: 'hex' })\n return opaqueWhereUnsolvable(\n page,\n [solid],\n [getAlphaColorSrgb(solid, page)],\n )[0]!\n }\n const passes = (t: number) =>\n fills.every(\n (fill) =>\n compositeOver(fill, inkAt(t)).contrast(fill, 'WCAG21') >=\n PLACEHOLDER_CONTRAST,\n )\n let found = 1\n if (passes(0)) found = 0\n else if (passes(1)) {\n let fails = 0\n for (let i = 0; i < 16; i++) {\n const mid = (fails + found) / 2\n if (passes(mid)) found = mid\n else fails = mid\n }\n }\n return inkAt(found)\n}\n\n/** sRGB `--spark-*` declarations for a generated accent scale. */\nexport function accentSparkVars(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScale,\n scale.accentScaleAlpha,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurface,\n scale.background,\n )\n}\n\n/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */\nexport function accentSparkVarsP3(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScaleWideGamut,\n scale.accentScaleAlphaWideGamut,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurfaceWideGamut,\n scale.background,\n )\n}\n\n/** Radix's `grayColor=\"auto\"` pairing (accent family → gray), mapped to our 6 neutrals. */\nconst NEUTRAL_BY_ACCENT: Record<string, SparkNeutralFamily> = {\n blue: 'slate',\n indigo: 'slate',\n cyan: 'slate',\n violet: 'mauve',\n purple: 'mauve',\n pink: 'mauve',\n red: 'mauve',\n teal: 'sage',\n green: 'sage',\n grass: 'olive',\n orange: 'sand',\n gray: 'gray',\n}\n\n/** Radix's \"auto\" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */\nexport function getMatchingNeutral(accent: string): SparkNeutralFamily {\n const byName = NEUTRAL_BY_ACCENT[accent]\n if (byName) return byName\n try {\n const [, chroma, hueRaw] = new Color(accent).to('oklch').coords\n if (!chroma || chroma < CHROMATIC_THRESHOLD) return 'gray'\n const hue = (((hueRaw ?? 0) % 360) + 360) % 360\n if (hue >= 300 || hue < 35) return 'mauve'\n if (hue < 115) return 'sand'\n if (hue < 145) return 'olive'\n if (hue < 200) return 'sage'\n return 'slate'\n } catch {\n return 'slate'\n }\n}\n\n/**\n * Fill steps (Radix roles 1-5), measured from the surface toward the *scrim* — the pole\n * opposite the ink. Magnitudes are unchanged from the original ink-directed ramp; only\n * the direction flips, so a fill and the text on it move apart instead of together.\n */\nconst INVERTED_FILL_RAMP = [0.06, 0.12, 0.2, 0.3, 0.4]\n\n/** Strongest border step (8, the hover/active edge). 6 is solved per accent; 7 splits them. */\nconst INVERTED_BORDER_TOP = 0.82\n\n/**\n * Ink steps (9-12), measured from the surface toward the ink. Step 9 (the solid fill)\n * lands on the ink itself; 10 eases back for hover; 11-12 are full-strength ink.\n */\nconst INVERTED_INK_RAMP = [1, 0.88, 1, 1]\n\n/**\n * Lowest alpha at which the ink, laid over the surface, is 3:1 distinguishable from it\n * (WCAG 1.4.11) — the floor for a border that has to read as an edge at all.\n *\n * Solved rather than hardcoded because it moves with the accent (0.57-0.72 across the\n * curated set). Contrast against the surface rises monotonically with `t`, so bisection\n * finds the boundary.\n *\n * Targets 1% above the floor rather than landing on it: the step ships as `#rrggbbaa`, and\n * 8-bit rounding on any of the four channels can drop an exact solve back under 3:1.\n */\nfunction solveBorderFloor(surface: Color, ink: Color): number {\n const target = MIN_GRAPHIC_CONTRAST * 1.01\n const ratioAt = (t: number) =>\n new Color(Color.mix(surface, ink, t, { space: 'srgb' })).contrast(\n surface,\n 'WCAG21',\n )\n let fails = 0\n let passes = 1\n for (let i = 0; i < 16; i++) {\n const mid = (fails + passes) / 2\n if (ratioAt(mid) >= target) passes = mid\n else fails = mid\n }\n return passes\n}\n\n/** OKLCH string in the wide-gamut format the generator emits (`oklch(96.1% 0.0155 254.2)`). */\nfunction oklchString(color: Color): string {\n const oklch = color.to('oklch')\n const lightness = +((oklch.coords[0] ?? 0) * 100).toFixed(1)\n return oklch\n .toString({ precision: 4 })\n .replace(/(\\S+)(.+)/, `oklch(${lightness}%$2`)\n}\n\n/**\n * Re-cast a scale for use ON its own solid (step-9) fill — the `inverted` knob.\n *\n * A solid accent control on a solid accent surface is invisible: both paint `--spark-9`.\n * Inverting swaps that pair — step 9 and its contrast ink trade places — so the solid fill\n * and its text are contrast-safe **by construction, for any accent**: `accentContrast` is\n * already the WCAG-checked readable ink for step 9 (`readableContrast`).\n *\n * The non-solid styles need a second idea, because the ink is also the text colour. A fill\n * blended surface→ink moves the background *toward* the foreground, so ink-on-fill contrast\n * falls monotonically with `t` — and measured across the curated set, the highest `t` that\n * still clears 4.5:1 (0.174) sits below the lowest `t` that is 3:1 distinguishable from the\n * surface (0.543). On that line no fill can be both legible and visible, on any accent.\n *\n * So fills blend the other way, toward the **scrim** — the lightness pole opposite the ink,\n * carrying the surface's own hue. Contrast against the ink now *rises* with `t`, which puts\n * every non-solid style at or above the solid pair's ratio for free. Borders keep stepping\n * toward the ink (a scrim-side border caps at ~2:1 on light accents like orange, so it can\n * never clear 1.4.11), starting at the solved 3:1 floor.\n *\n * Appearance-stable on purpose. The generator pins step 9 to the accent itself, so the\n * surface is identical in light and dark — which makes the whole inverted scale identical\n * too, and `[data-inverted]` needs no `.dark` twin.\n *\n * Two things this is deliberately NOT:\n * - **A positional mirror** (`1↔12`). It leaves the solid fill only 2.04:1 from the card in\n * dark and 2.23:1 on a light accent like orange — under the 3:1 graphical floor, i.e. still\n * invisible, which is the bug this exists to fix.\n * - **A regenerated on-surface ramp.** Feeding the surface back in as `background` produces\n * off-hue overlays (a warm wash on a blue card) and ink at 3.35:1.\n *\n * Values are literals, not `var(--spark-N)` aliases: a scale can't re-point to itself\n * (`--spark-1: var(--spark-12)` next to `--spark-12: var(--spark-1)` is a cycle, and CSS\n * discards both).\n */\nexport function invertAccentScale(scale: SparkAccentScale): SparkAccentScale {\n const surface = scale.accentScale[8]! // the solid fill this scale will sit on\n const ink = scale.accentContrast // already AA-checked against that fill\n const surfaceColor = new Color(surface)\n const inkColor = new Color(ink)\n\n // The scrim: opposite the ink in lightness, tinted with the surface's own hue by the\n // same rule readableContrast uses for its ink ladder. Derived from the surface, so this\n // introduces no new palette input.\n const [surfaceLightness, surfaceChroma, surfaceHue] =\n surfaceColor.to('oklch').coords\n const inkLightness = inkColor.to('oklch').coords[0] ?? 0\n const scrimColor = new Color('oklch', [\n inkLightness > (surfaceLightness ?? 0) ? 0.12 : 0.98,\n Math.max(0.08 * (surfaceChroma ?? 0), 0.02),\n surfaceHue ?? 0,\n ])\n\n const borderFloor = solveBorderFloor(surfaceColor, inkColor)\n const ramp: { toward: Color; t: number }[] = [\n ...INVERTED_FILL_RAMP.map((t) => ({ toward: scrimColor, t })),\n { toward: inkColor, t: borderFloor },\n { toward: inkColor, t: (borderFloor + INVERTED_BORDER_TOP) / 2 },\n { toward: inkColor, t: INVERTED_BORDER_TOP },\n ...INVERTED_INK_RAMP.map((t) => ({ toward: inkColor, t })),\n ]\n\n // sRGB, not OKLab: the browser composites `--spark-aN` as source-over in gamma sRGB, so\n // mixing the opaque twin in any other space makes `--spark-N` a different colour from it\n // (measured up to dE2000 2.8 on the old OKLab mix, over ALPHA_SOLVE_TOLERANCE).\n const blend = ({ toward, t }: { toward: Color; t: number }) =>\n new Color(Color.mix(surfaceColor, toward, t, { space: 'srgb' }))\n const steps = ramp.map((s) =>\n blend(s).to('srgb').toString({ format: 'hex' }),\n ) as typeof scale.accentScale\n const stepsWideGamut = ramp.map((s) =>\n oklchString(blend(s)),\n ) as typeof scale.accentScaleWideGamut\n // Mixing surface→X at t in sRGB is the same colour as X at alpha t over the surface, so\n // each alpha step is just its own pole at rising opacity — no solve needed.\n const alpha = ramp.map(({ toward, t }) => {\n const translucent = toward.clone()\n translucent.alpha = t\n return translucent.to('srgb').toString({ format: 'hex' })\n }) as typeof scale.accentScaleAlpha\n const alphaWideGamut = ramp.map(({ toward, t }) => {\n const translucent = toward.clone().to('p3')\n translucent.alpha = t\n return translucent\n .toString({ precision: 4 })\n .replace('color(p3 ', 'color(display-p3 ')\n }) as typeof scale.accentScaleAlphaWideGamut\n return {\n ...scale,\n accentScale: steps,\n accentScaleWideGamut: stepsWideGamut,\n accentScaleAlpha: alpha,\n accentScaleAlphaWideGamut: alphaWideGamut,\n // Ink on the inverted solid fill is the accent itself — the other half of the swapped pair.\n accentContrast: surface,\n accentOnSolid: surface,\n accentSurface: steps[1]!,\n accentSurfaceWideGamut: stepsWideGamut[1]!,\n }\n}\n\n/**\n * `neutralColor=\"auto\"` resolution: prefer the canvas's own tint when it has one — a\n * chromatic `background` seeds a visibly warmer/cooler ramp than bucketing it into one\n * of the 6 named families would (see gap #12). Falls back to accent-matched bucketing\n * when there's no tinted canvas to pull from.\n */\nexport function resolveAutoNeutral(\n accent: string,\n background?: string,\n): string {\n if (background) {\n try {\n const [, chroma] = new Color(background).to('oklch').coords\n if (chroma && chroma >= CHROMATIC_THRESHOLD) return background\n } catch {\n // fall through to accent-matched neutral\n }\n }\n return getMatchingNeutral(accent)\n}\n\n/**\n * Whether a string parses as a color at all — for validating a custom `neutralColor`\n * before it reaches the generator. An unparseable seed (e.g. a typo'd family name like\n * `\"slat\"`) would otherwise throw deep inside `generateRadixColors`, taking the whole\n * accent scale down with it via the caller's shared try/catch.\n */\nexport function isParsableColor(value: string): boolean {\n try {\n new Color(value).to('oklch')\n return true\n } catch {\n return false\n }\n}\n\n/** The 27 tokens every scale family declares, under an arbitrary prefix. */\nfunction scaleVars(\n prefix: string,\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n): string[] {\n return [\n ...steps.map((c, i) => `--${prefix}-${i + 1}: ${c};`),\n ...alpha.map((c, i) => `--${prefix}-a${i + 1}: ${c};`),\n `--${prefix}-contrast: ${contrast};`,\n `--${prefix}-on-solid: ${onSolid};`,\n `--${prefix}-surface: ${surface};`,\n ]\n}\n\nfunction sparkVars(\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n background: string,\n): string[] {\n const indicator = steps[8]! // step 9 — the brand step (matches --spark-indicator/-track)\n return [\n ...scaleVars('spark', steps, alpha, contrast, onSolid, surface),\n `--spark-background: ${background};`,\n `--spark-indicator: ${indicator};`,\n `--spark-track: ${indicator};`,\n ]\n}\n\n/**\n * The same `--neutral-*` tokens `neutralSparkVars` emits, pointed back at a static Radix\n * family instead of a generated scale — the counterpart for an appearance that has *no*\n * custom canvas of its own.\n *\n * Ambient (no explicit `appearance`) mode emits both appearances' scales into one unlayered\n * `<style>`, but a custom canvas only regenerates neutrals for the appearance it belongs to.\n * Left undeclared, the other block doesn't fall back — nothing resets a custom property, so\n * its sibling's canvas-tinted values simply stay in effect there, and being unlayered they\n * outrank `[data-neutral-color]`'s own `@layer base` mapping too. Declaring the same token\n * set closes that. `var(--<family>-*)` is itself appearance-aware (Radix ships the dark\n * scales under `.dark`), so one declaration resolves correctly wherever the block applies.\n */\nexport function neutralFamilyVars(\n family: Exclude<SparkNeutral, 'auto'>,\n): string[] {\n const ref = (suffix: string) => `var(--${family}-${suffix})`\n return [\n ...scaleVars(\n 'neutral',\n Array.from({ length: 12 }, (_, i) => ref(`${i + 1}`)),\n Array.from({ length: 12 }, (_, i) => ref(`a${i + 1}`)),\n // The companion tokens' own static defaults, from spark-default.css's .light/.dark.\n // `-contrast` is per-family there so [data-neutral-color] swaps the ink with the\n // ramp; referencing it rather than inlining #fff keeps the CSS the single source\n // of truth if the two appearances ever diverge again.\n `var(--${family}-contrast)`,\n '#fff',\n 'var(--neutral-2)',\n ),\n `--neutral-placeholder: ${ref('placeholder')};`,\n ]\n}\n\n// Emitted as sRGB on the P3 path too; the solve is a WCAG ratio, which is defined in sRGB.\nconst neutralPlaceholderVar = (scale: SparkAccentScale) =>\n `--neutral-placeholder: ${solvePlaceholder({\n a10: scale.grayScaleAlpha[9]!,\n a11: scale.grayScaleAlpha[10]!,\n page: scale.background,\n card: scale.grayScale[2]!,\n tints: [\n scale.grayScaleAlpha[3]!,\n RED_A4[appearanceForBackground(scale.background)],\n scale.accentScaleAlpha[3]!,\n ],\n })};`\n\n/**\n * `--neutral-*` declarations from the gray scale the generator produces alongside the\n * accent — normally discarded, because `[data-neutral-color]` points `--neutral-*` at a\n * static Radix family. Worth consuming only when the canvas is custom: the static\n * families' alpha steps are pre-baked against white/black, so on any other background\n * they composite wrong (every gray text/border reads through `--neutral-a*`).\n *\n * `--neutral-surface` is step 2, matching the static families' own convention rather\n * than the generator's translucent `graySurface`.\n */\nexport function neutralSparkVars(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScale,\n scale.grayScaleAlpha,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScale[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n\n/** Wide-gamut (P3) twin of `neutralSparkVars`. */\nexport function neutralSparkVarsP3(scale: SparkAccentScale): string[] {\n return [\n ...scaleVars(\n 'neutral',\n scale.grayScaleWideGamut,\n scale.grayScaleAlphaWideGamut,\n readableContrast(scale.grayScale[8]!, '#fff', MIN_TEXT_CONTRAST),\n readableContrast(scale.grayScale[8]!, '#fff', MIN_GRAPHIC_CONTRAST),\n scale.grayScaleWideGamut[1]!,\n ),\n neutralPlaceholderVar(scale),\n ]\n}\n"],"mappings":"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"}
|
package/dist/styles/index.css
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
@import '@radix-ui/colors/black-alpha.css' layer(base);
|
|
76
76
|
|
|
77
77
|
/* Default Spark brand scale */
|
|
78
|
-
@import './spark-default.css'
|
|
78
|
+
@import './spark-default.css';
|
|
79
79
|
|
|
80
80
|
/* Tailwind v4 theme: scales, semantic tokens, dark variant, safelist */
|
|
81
81
|
@import './theme.css';
|