okchroma 0.3.1 → 0.4.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.
@@ -32,6 +32,7 @@ __export(index_exports, {
32
32
  RESOLVER_ID: () => RESOLVER_ID,
33
33
  SCALE_STOP_COUNT: () => SCALE_STOP_COUNT,
34
34
  SCRIM_ALPHA: () => SCRIM_ALPHA,
35
+ SCRIM_VAR: () => SCRIM_VAR,
35
36
  SECONDARY_DISTINCT_DELTA_E: () => SECONDARY_DISTINCT_DELTA_E,
36
37
  SHADOW_ALPHAS: () => SHADOW_ALPHAS,
37
38
  SIGNALS: () => SIGNALS2,
@@ -59,6 +60,7 @@ __export(index_exports, {
59
60
  generateScale: () => generateScale,
60
61
  generateSubtleSecondary: () => generateSubtleSecondary,
61
62
  neutralCss: () => neutralCss,
63
+ neutralTintHue: () => neutralTintHue,
62
64
  pageStopFor: () => pageStopFor,
63
65
  parseToken: () => parseToken,
64
66
  resolveBrand: () => resolveBrand,
@@ -2717,6 +2719,12 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2717
2719
  pen100Dark: penPole(dark, 1)
2718
2720
  };
2719
2721
  }
2722
+ function neutralTintHue(primaryH, source, secondaryH, customHex) {
2723
+ if (source === "secondary" && secondaryH !== void 0) return secondaryH;
2724
+ if (source === "custom" && customHex && /^#?[0-9a-fA-F]{6}$/.test(customHex))
2725
+ return hexToOklch(customHex.startsWith("#") ? customHex : `#${customHex}`).H;
2726
+ return primaryH;
2727
+ }
2720
2728
  function generateNeutralScale(brandH, level = "default", contrastProfile) {
2721
2729
  const h = (brandH % 360 + 360) % 360;
2722
2730
  const { r, g: g2, b } = oklchToSrgbUnclamped(0.5, 6e-3, h);
@@ -3522,9 +3530,12 @@ var SHADOW_ALPHAS = {
3522
3530
  var SCRIM_ALPHA = 0.6;
3523
3531
  var DISABLED_OPACITY = 0.38;
3524
3532
  var TRANSPARENT_VAR = "--alpha-transparent";
3533
+ var SCRIM_VAR = "--abs-black-060";
3525
3534
  var offsetRgba = (rung, mode) => mode === "light" ? `rgba(0, 0, 0, ${OFFSET_ALPHAS[rung]})` : `rgba(255, 255, 255, ${OFFSET_ALPHAS[rung]})`;
3526
3535
  var alphaRootVars = (mode) => [
3527
3536
  ` ${TRANSPARENT_VAR}: transparent;`,
3537
+ // mode-invariant like transparent; repeated in the dark block to keep the set together
3538
+ ` ${SCRIM_VAR}: rgba(0, 0, 0, ${SCRIM_ALPHA});`,
3528
3539
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
3529
3540
  // the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
3530
3541
  // same rungs, pole flipped per mode — state layers for INVERTED grounds (never
@@ -3540,7 +3551,7 @@ function ctaNeedsBorder(s, mode, page) {
3540
3551
  if (!page) return false;
3541
3552
  return ctaPageLc(s, mode, page) < CTA_BORDER_LC_FLOOR;
3542
3553
  }
3543
- function brandKindBody(prefix, s, mode, page) {
3554
+ function brandKindBody(prefix, s, mode, page, on = prefix === CSS_NEUTRAL ? "soft" : "solid") {
3544
3555
  const stops = mode === "light" ? s.light : s.dark;
3545
3556
  const f = ctaFamilyOf(s, mode);
3546
3557
  const onCta = mode === "light" ? s.onFillTextIsWhite : s.onFillTextIsWhiteDark;
@@ -3559,10 +3570,10 @@ function brandKindBody(prefix, s, mode, page) {
3559
3570
  // button text takes the pole AT ALPHA like the default-model secondary's — composited
3560
3571
  // over whatever state the fill is in, so hover/pressed carry their own legibility. Same
3561
3572
  // register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
3562
- // primitive. Emitted HERE rather than as a post-body override because the neutral's is
3563
- // unconditional — the secondary's rides the cascade only because it gates on the style
3564
- // chip. Loud fills (brand, signals, the cta escape) keep the solid pole.
3565
- prefix === CSS_NEUTRAL ? ` --${CSS_NEUTRAL}-stamp-on: rgba(${onCta ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});` : ` --${prefix}-stamp-on: ${onColor(onCta)};`
3573
+ // primitive. The neutral's is unconditional (the parameter default); the secondary's is
3574
+ // the caller's per-mode verdict. Loud fills (brand, signals, the cta escape) keep the
3575
+ // solid pole.
3576
+ on === "soft" ? ` --${prefix}-stamp-on: rgba(${onCta ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});` : ` --${prefix}-stamp-on: ${onColor(onCta)};`
3566
3577
  ];
3567
3578
  }
3568
3579
  function ctaFamilyOf(s, mode) {
@@ -3629,7 +3640,8 @@ function signalsCss(contrastProfile) {
3629
3640
  // the system alpha variables every family's cta-border aliases (owner 2026-07-29) — this is
3630
3641
  // the engine's one global :root, the CSS counterpart of the Figma side's system/alpha/* rows.
3631
3642
  // Emitted in BOTH blocks because the offset rungs are scheme-DIVERGENT (black in light,
3632
- // white in dark); --alpha-transparent repeats harmlessly and keeps the set together.
3643
+ // white in dark); --alpha-transparent and the scrim (both mode-invariant) repeat
3644
+ // harmlessly and keep the set together.
3633
3645
  ...alphaRootVars("light"),
3634
3646
  ...lightBlocks,
3635
3647
  `}`,
@@ -3678,8 +3690,9 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3678
3690
  alias("stamp-on")
3679
3691
  ];
3680
3692
  };
3681
- const secondaryLight = secondary ? brandKindBody(CSS_SECONDARY, secondary, "light", page.light) : mirrorBody(CSS_SECONDARY, "light");
3682
- const secondaryDark = secondary ? brandKindBody(CSS_SECONDARY, secondary, "dark", page.dark) : mirrorBody(CSS_SECONDARY, "dark");
3693
+ const secondaryOn = (mode) => secondary && secondaryStyle !== "outline" && softOnCtaPasses(secondary, mode) ? "soft" : "solid";
3694
+ const secondaryLight = secondary ? brandKindBody(CSS_SECONDARY, secondary, "light", page.light, secondaryOn("light")) : mirrorBody(CSS_SECONDARY, "light");
3695
+ const secondaryDark = secondary ? brandKindBody(CSS_SECONDARY, secondary, "dark", page.dark, secondaryOn("dark")) : mirrorBody(CSS_SECONDARY, "dark");
3683
3696
  const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
3684
3697
  const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
3685
3698
  const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
@@ -3713,12 +3726,6 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3713
3726
  ` --${CSS_BRAND}-stamp-on: ${onColor(esc.onFillIsWhite)};`
3714
3727
  ];
3715
3728
  };
3716
- const softOnCta = (mode) => {
3717
- if (!secondary || secondaryStyle === "outline") return [];
3718
- if (!softOnCtaPasses(secondary, mode)) return [];
3719
- const white = mode === "light" ? secondary.onFillTextIsWhite : secondary.onFillTextIsWhiteDark;
3720
- return [` --${CSS_SECONDARY}-stamp-on: rgba(${white ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});`];
3721
- };
3722
3729
  const outline = (mode) => {
3723
3730
  if (secondaryStyle !== "outline" || !secondary) return [];
3724
3731
  const s8 = (mode === "light" ? secondary.light : secondary.dark).find((s) => s.stop === 8);
@@ -3762,7 +3769,6 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3762
3769
  ...linkInverse("light"),
3763
3770
  brandIdentity,
3764
3771
  ...secondaryLight,
3765
- ...softOnCta("light"),
3766
3772
  ...outline("light"),
3767
3773
  secondaryIdentity,
3768
3774
  ...brandKindBody(CSS_NEUTRAL, nScale, "light", page.light),
@@ -3775,7 +3781,6 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3775
3781
  ...link("dark"),
3776
3782
  ...linkInverse("dark"),
3777
3783
  ...secondaryDark,
3778
- ...softOnCta("dark"),
3779
3784
  ...outline("dark"),
3780
3785
  ...brandKindBody(CSS_NEUTRAL, nScale, "dark", page.dark),
3781
3786
  ...effOverrides.flatMap((o) => brandKindBody(SIGNAL_EMIT_NAME[o.name], o.scale, "dark", page.dark)),
@@ -2638,6 +2638,12 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2638
2638
  pen100Dark: penPole(dark, 1)
2639
2639
  };
2640
2640
  }
2641
+ function neutralTintHue(primaryH, source, secondaryH, customHex) {
2642
+ if (source === "secondary" && secondaryH !== void 0) return secondaryH;
2643
+ if (source === "custom" && customHex && /^#?[0-9a-fA-F]{6}$/.test(customHex))
2644
+ return hexToOklch(customHex.startsWith("#") ? customHex : `#${customHex}`).H;
2645
+ return primaryH;
2646
+ }
2641
2647
  function generateNeutralScale(brandH, level = "default", contrastProfile) {
2642
2648
  const h = (brandH % 360 + 360) % 360;
2643
2649
  const { r, g: g2, b } = oklchToSrgbUnclamped(0.5, 6e-3, h);
@@ -3443,9 +3449,12 @@ var SHADOW_ALPHAS = {
3443
3449
  var SCRIM_ALPHA = 0.6;
3444
3450
  var DISABLED_OPACITY = 0.38;
3445
3451
  var TRANSPARENT_VAR = "--alpha-transparent";
3452
+ var SCRIM_VAR = "--abs-black-060";
3446
3453
  var offsetRgba = (rung, mode) => mode === "light" ? `rgba(0, 0, 0, ${OFFSET_ALPHAS[rung]})` : `rgba(255, 255, 255, ${OFFSET_ALPHAS[rung]})`;
3447
3454
  var alphaRootVars = (mode) => [
3448
3455
  ` ${TRANSPARENT_VAR}: transparent;`,
3456
+ // mode-invariant like transparent; repeated in the dark block to keep the set together
3457
+ ` ${SCRIM_VAR}: rgba(0, 0, 0, ${SCRIM_ALPHA});`,
3449
3458
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
3450
3459
  // the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
3451
3460
  // same rungs, pole flipped per mode — state layers for INVERTED grounds (never
@@ -3461,7 +3470,7 @@ function ctaNeedsBorder(s, mode, page) {
3461
3470
  if (!page) return false;
3462
3471
  return ctaPageLc(s, mode, page) < CTA_BORDER_LC_FLOOR;
3463
3472
  }
3464
- function brandKindBody(prefix, s, mode, page) {
3473
+ function brandKindBody(prefix, s, mode, page, on = prefix === CSS_NEUTRAL ? "soft" : "solid") {
3465
3474
  const stops = mode === "light" ? s.light : s.dark;
3466
3475
  const f = ctaFamilyOf(s, mode);
3467
3476
  const onCta = mode === "light" ? s.onFillTextIsWhite : s.onFillTextIsWhiteDark;
@@ -3480,10 +3489,10 @@ function brandKindBody(prefix, s, mode, page) {
3480
3489
  // button text takes the pole AT ALPHA like the default-model secondary's — composited
3481
3490
  // over whatever state the fill is in, so hover/pressed carry their own legibility. Same
3482
3491
  // register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
3483
- // primitive. Emitted HERE rather than as a post-body override because the neutral's is
3484
- // unconditional — the secondary's rides the cascade only because it gates on the style
3485
- // chip. Loud fills (brand, signals, the cta escape) keep the solid pole.
3486
- prefix === CSS_NEUTRAL ? ` --${CSS_NEUTRAL}-stamp-on: rgba(${onCta ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});` : ` --${prefix}-stamp-on: ${onColor(onCta)};`
3492
+ // primitive. The neutral's is unconditional (the parameter default); the secondary's is
3493
+ // the caller's per-mode verdict. Loud fills (brand, signals, the cta escape) keep the
3494
+ // solid pole.
3495
+ on === "soft" ? ` --${prefix}-stamp-on: rgba(${onCta ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});` : ` --${prefix}-stamp-on: ${onColor(onCta)};`
3487
3496
  ];
3488
3497
  }
3489
3498
  function ctaFamilyOf(s, mode) {
@@ -3550,7 +3559,8 @@ function signalsCss(contrastProfile) {
3550
3559
  // the system alpha variables every family's cta-border aliases (owner 2026-07-29) — this is
3551
3560
  // the engine's one global :root, the CSS counterpart of the Figma side's system/alpha/* rows.
3552
3561
  // Emitted in BOTH blocks because the offset rungs are scheme-DIVERGENT (black in light,
3553
- // white in dark); --alpha-transparent repeats harmlessly and keeps the set together.
3562
+ // white in dark); --alpha-transparent and the scrim (both mode-invariant) repeat
3563
+ // harmlessly and keep the set together.
3554
3564
  ...alphaRootVars("light"),
3555
3565
  ...lightBlocks,
3556
3566
  `}`,
@@ -3599,8 +3609,9 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3599
3609
  alias("stamp-on")
3600
3610
  ];
3601
3611
  };
3602
- const secondaryLight = secondary ? brandKindBody(CSS_SECONDARY, secondary, "light", page.light) : mirrorBody(CSS_SECONDARY, "light");
3603
- const secondaryDark = secondary ? brandKindBody(CSS_SECONDARY, secondary, "dark", page.dark) : mirrorBody(CSS_SECONDARY, "dark");
3612
+ const secondaryOn = (mode) => secondary && secondaryStyle !== "outline" && softOnCtaPasses(secondary, mode) ? "soft" : "solid";
3613
+ const secondaryLight = secondary ? brandKindBody(CSS_SECONDARY, secondary, "light", page.light, secondaryOn("light")) : mirrorBody(CSS_SECONDARY, "light");
3614
+ const secondaryDark = secondary ? brandKindBody(CSS_SECONDARY, secondary, "dark", page.dark, secondaryOn("dark")) : mirrorBody(CSS_SECONDARY, "dark");
3604
3615
  const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
3605
3616
  const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
3606
3617
  const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
@@ -3634,12 +3645,6 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3634
3645
  ` --${CSS_BRAND}-stamp-on: ${onColor(esc.onFillIsWhite)};`
3635
3646
  ];
3636
3647
  };
3637
- const softOnCta = (mode) => {
3638
- if (!secondary || secondaryStyle === "outline") return [];
3639
- if (!softOnCtaPasses(secondary, mode)) return [];
3640
- const white = mode === "light" ? secondary.onFillTextIsWhite : secondary.onFillTextIsWhiteDark;
3641
- return [` --${CSS_SECONDARY}-stamp-on: rgba(${white ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});`];
3642
- };
3643
3648
  const outline = (mode) => {
3644
3649
  if (secondaryStyle !== "outline" || !secondary) return [];
3645
3650
  const s8 = (mode === "light" ? secondary.light : secondary.dark).find((s) => s.stop === 8);
@@ -3683,7 +3688,6 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3683
3688
  ...linkInverse("light"),
3684
3689
  brandIdentity,
3685
3690
  ...secondaryLight,
3686
- ...softOnCta("light"),
3687
3691
  ...outline("light"),
3688
3692
  secondaryIdentity,
3689
3693
  ...brandKindBody(CSS_NEUTRAL, nScale, "light", page.light),
@@ -3696,7 +3700,6 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3696
3700
  ...link("dark"),
3697
3701
  ...linkInverse("dark"),
3698
3702
  ...secondaryDark,
3699
- ...softOnCta("dark"),
3700
3703
  ...outline("dark"),
3701
3704
  ...brandKindBody(CSS_NEUTRAL, nScale, "dark", page.dark),
3702
3705
  ...effOverrides.flatMap((o) => brandKindBody(SIGNAL_EMIT_NAME[o.name], o.scale, "dark", page.dark)),
@@ -3976,6 +3979,7 @@ export {
3976
3979
  RESOLVER_ID,
3977
3980
  SCALE_STOP_COUNT,
3978
3981
  SCRIM_ALPHA,
3982
+ SCRIM_VAR,
3979
3983
  SECONDARY_DISTINCT_DELTA_E,
3980
3984
  SHADOW_ALPHAS,
3981
3985
  SIGNALS2 as SIGNALS,
@@ -4003,6 +4007,7 @@ export {
4003
4007
  generateScale,
4004
4008
  generateSubtleSecondary,
4005
4009
  neutralCss,
4010
+ neutralTintHue,
4006
4011
  pageStopFor,
4007
4012
  parseToken,
4008
4013
  resolveBrand,
@@ -35,11 +35,12 @@ export type ShadowRung = keyof typeof SHADOW_ALPHAS;
35
35
  export declare const SCRIM_ALPHA = 0.6;
36
36
  export declare const DISABLED_OPACITY = 0.38;
37
37
  export declare const TRANSPARENT_VAR = "--alpha-transparent";
38
+ export declare const SCRIM_VAR = "--abs-black-060";
38
39
  export declare const offsetRgba: (rung: OffsetRung, mode: "light" | "dark") => string;
39
40
  export declare const alphaRootVars: (mode: "light" | "dark") => string[];
40
41
  export declare function ctaPageLc(s: GeneratedScale, mode: 'light' | 'dark', page: ColorStop): number;
41
42
  export declare function ctaNeedsBorder(s: GeneratedScale, mode: 'light' | 'dark', page: ColorStop | undefined): boolean;
42
- export declare function brandKindBody(prefix: string, s: GeneratedScale, mode: 'light' | 'dark', page: ColorStop | undefined): string[];
43
+ export declare function brandKindBody(prefix: string, s: GeneratedScale, mode: 'light' | 'dark', page: ColorStop | undefined, on?: 'soft' | 'solid'): string[];
43
44
  export declare function brandKindP3Body(prefix: string, s: GeneratedScale, mode: 'light' | 'dark'): string[];
44
45
  export declare function neutralCss(selector: string, brandH: number, level?: NeutralLevel, contrastProfile?: ContrastProfile, ctaBorder?: boolean): string;
45
46
  export declare function signalsCss(contrastProfile?: ContrastProfile): string;
@@ -1,6 +1,6 @@
1
- export { generateScale, generateNeutralScale, generateSubtleSecondary, type GeneratedScale, type ColorStop, type GenerateOptions, type NeutralLevel, type ContrastProfile, } from './engine/colorEngine';
1
+ export { generateScale, generateNeutralScale, generateSubtleSecondary, neutralTintHue, type NeutralSource, type GeneratedScale, type ColorStop, type GenerateOptions, type NeutralLevel, type ContrastProfile, } from './engine/colorEngine';
2
2
  export { resolveBrand, resolveTheme, resolveLinkTrio, resolveLinkInverseTrio, DEFAULT_LINK_HEX, SIGNAL_SCALES, signalScalesFor, SECONDARY_DISTINCT_DELTA_E, SUBTLE_TINT_MULT, SUBTLE_PASTEL_K, OUTLINE_HOVER_ALPHA, type ResolvedBrand, type ResolvedTheme, type ResolvedSecondary, type SecondaryLevel, type SecondaryStyle, type SignalOverride, } from './engine/resolve';
3
- export { brandCss, neutralCss, signalsCss, stopsToVars, toHex, stopHex, OFFSET_ALPHAS, SHADOW_ALPHAS, SCRIM_ALPHA, DISABLED_OPACITY, type ShadowRung, ctaNeedsBorder, ctaBorderRung, pageStopFor, } from './engine/cssRender';
3
+ export { brandCss, neutralCss, signalsCss, stopsToVars, toHex, stopHex, OFFSET_ALPHAS, SHADOW_ALPHAS, SCRIM_ALPHA, SCRIM_VAR, DISABLED_OPACITY, type ShadowRung, ctaNeedsBorder, ctaBorderRung, pageStopFor, } from './engine/cssRender';
4
4
  export { CSS_FAMILY } from './engine/tokenDescriptions';
5
5
  export { themeToFigma, type FigmaGroup, type FigmaColorToken, type ThemeInput, } from './engine/figmaRender';
6
6
  export { emitDtcgRamp, resolveDtcgRamp, parseToken, EXT_KEY, RESOLVER_ID, type DtcgRampGroup, type DtcgRequirementToken, type DtcgSeedToken, type DtcgColorValue, } from './engine/requirements/dtcg';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okchroma",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Color token engine: one brand hex in, a full WCAG-enforced light+dark token system out (CSS variables, DTCG JSON, Figma variables)",
6
6
  "repository": {
@@ -1,20 +1,18 @@
1
1
  /*
2
- Semantic token layer — maps role names to primitive tokens.
2
+ Semantic token layer: maps role names to primitive tokens.
3
3
  This file never changes per brand. Only the primitives (--brand-*, --neutral-*) change.
4
4
  */
5
5
 
6
6
  /* ─── Global surface / fg / border ─────────────────────────────────────────── */
7
7
 
8
8
  [data-brand] {
9
- /* Elevation planes sunken → low → base → high (owner spec 2026-07-24 Figma
10
- elevation study; renamed 2026-08-12 — base is the raised plane components sit
11
- on, the page behind it is low, the two extremes are edge cases): both themes
12
- use the SAME four stops, in reversed order — elevation always moves toward
13
- paper-0's side of the ramp. Light descends the ramp as elevation rises
14
- (sunken paper-5 → high paper-0 white); dark ascends it (sunken paper-0
15
- black → high paper-5). paper-0 flips white→black with the mode, so it is
16
- always the extreme pole: the high in light, the sunken in dark. The stops
17
- themselves have never moved through any of the renames. */
9
+ /* Elevation planes dim, low, mid, high: mid is the raised plane components sit on,
10
+ the page behind it is low, and the two extremes are edge cases. Both themes use
11
+ the SAME four stops in reversed order, so elevation always moves toward paper-0's
12
+ side of the ramp. Light descends the ramp as elevation rises (dim paper-5 to high
13
+ paper-0, white); dark ascends it (dim paper-0, the deep tinted ground, to high
14
+ paper-5). paper-0 flips with the mode, so it is always the extreme pole: the high
15
+ in light, the dim in dark. */
18
16
  --surface-dim: var(--neutral-paper-5);
19
17
  --surface-low: var(--neutral-paper-3);
20
18
  --surface-mid: var(--neutral-paper-1);
@@ -22,41 +20,40 @@
22
20
 
23
21
  --fg-default: var(--neutral-pen-70);
24
22
  --fg-subtle: var(--neutral-pencil-47);
25
- /* --fg-link moved to the SYSTEM LINK trio below (owner 2026-07-16: one link per
26
- theme, riding --link — no longer the brand's raw pen pair) */
27
-
28
- /* --border-default IS THE ACCESSIBILITY BORDER (owner 2026-07-29): a border that has
29
- to be there — inputs and other controls — so it rides crayon-26, the stop that
30
- carries the WCAG 1.4.11 3:1 non-text require (highlight-8 pre-Stage-B). It sat on
31
- --neutral-highlight-9 until that stop was deleted; crayon-26 is where the
32
- requirement actually lives, and it is the one stop this round does not move.
33
- Decorative borders are --border-subtle (highlighter-11) and the per-family
34
- --*-border-default (highlighter-15). */
23
+
24
+ /* --border-default IS THE ACCESSIBILITY BORDER: a border that has to be there (inputs
25
+ and other controls), so it rides crayon-26, the stop that carries the 3:1 non-text
26
+ contrast requirement. Decorative borders are --border-subtle (highlighter-11) and
27
+ the per-family --*-border-default (highlighter-15). */
35
28
  --border-default: var(--neutral-crayon-26);
36
29
  --border-subtle: var(--neutral-highlighter-11);
37
30
 
38
- /* Shadow transparencies (owner 2026-07-27) — pure black, mirrors the plugin's
39
- system/alpha/shadow-04|08|12 rows 1:1. Dark is heavier by necessity: near
40
- black a light-mode alpha vanishes, so the dark values carry the same roles
41
- at 32/48/64%. Elevation recipes (--elev-*) compose these, so dark shadows
42
- fall out of the token swap with nothing bespoke per style. */
31
+ /* Shadow transparencies: pure black, the engine's SHADOW_ALPHAS, the same rows the
32
+ plugins write as shadow-04|08|12. Dark is heavier by necessity: near black a
33
+ light-mode alpha vanishes, so the dark values carry the same roles at heavier
34
+ alphas. Elevation recipes compose these, so dark shadows fall out of the token
35
+ swap with nothing bespoke per style. */
43
36
  --shadow-04: rgba(0, 0, 0, 0.04);
44
37
  --shadow-08: rgba(0, 0, 0, 0.08);
45
38
  --shadow-12: rgba(0, 0, 0, 0.12);
46
39
 
47
- /* Disabled opacity (owner 2026-08-29): disabled is a COMPONENT-LEVEL opacity —
48
- colors stay the enabled ones and never swap. Mode-invariant; mirrors the JS
49
- API's DISABLED_OPACITY constant 1:1. */
40
+ /* The scrim: the modal veil, black at 60% in both modes. The primitive
41
+ (--abs-black-060) rides signalsCss's :root, the same row the plugins write
42
+ as abs-black-060; this alias is the job word. */
43
+ --scrim: var(--abs-black-060);
44
+
45
+ /* Disabled opacity: disabled is a COMPONENT-LEVEL opacity. Colors stay the enabled
46
+ ones and never swap. Mode-invariant; mirrors the JS API's DISABLED_OPACITY
47
+ constant 1:1. */
50
48
  --disabled-opacity: 0.38;
51
49
  }
52
50
 
53
51
  [data-brand][data-theme="dark"] {
54
- /* Dark runs the same four stops upward: sunken paper-0 (black) → low paper-1 →
55
- base paper-3 → high paper-5 — the one-level highest background (C27: every
52
+ /* Dark runs the same four stops upward: dim paper-0 (the deep tinted ground), low
53
+ paper-1, mid paper-3, high paper-5, the one-level highest background: every
56
54
  family's paper-5 shares one photometric level, so chips sit flush on the high
57
- plane by construction; a generated candidate for it was tried and retired,
58
- owner 2026-07-28). Low needs its own override here because the shared block's
59
- low is paper-3 (the light value); the stops themselves did not move. */
55
+ plane by construction. Low needs its own override here because the shared
56
+ block's low is paper-3, the light value. */
60
57
  --surface-dim: var(--paper-0, #000000);
61
58
  --surface-low: var(--neutral-paper-1);
62
59
  --surface-mid: var(--neutral-paper-3);
@@ -90,15 +87,15 @@
90
87
  --brand-border-emphasis: var(--brand-stamp-fill);
91
88
  --brand-border-emphasis-hover: var(--brand-stamp-fill-hover);
92
89
 
93
- /* the SYSTEM LINK (one trio per theme — aliases the primary's pen stops unless a custom
90
+ /* the SYSTEM LINK (one trio per theme; aliases the primary's pen stops unless a custom
94
91
  link color de-conflicts it; --link/-hover/-pressed are emitted per brand) */
95
92
  --fg-link: var(--link);
96
93
  --fg-link-hover: var(--link-hover);
97
94
  --fg-link-pressed: var(--link-pressed);
98
95
 
99
- /* the INVERSE link trio (owner round 2026-08-19): the same link seed re-solved for
100
- text on inverted surfaces (a pen-70 fill); always its own emitted values —
101
- no stop is anchored against that ground, so there is no alias posture */
96
+ /* the INVERSE link trio: the same link seed re-solved for text on inverted surfaces
97
+ (a pen-70 fill); always its own emitted values, since no stop is anchored against
98
+ that ground and so there is no alias posture */
102
99
  --fg-link-inverse: var(--link-inverse);
103
100
  --fg-link-inverse-hover: var(--link-inverse-hover);
104
101
  --fg-link-inverse-pressed: var(--link-inverse-pressed);
@@ -131,20 +128,15 @@
131
128
  /* ─── Signal roles ──────────────────────────────────────────────────────────
132
129
  Aliases onto engine-generated signal primitives (dist/signals.css).
133
130
  Mode switching happens at the primitive layer, so one block covers both.
134
- Signal families emit by ROLE (owner 2026-07-27: critical/warning/positive/
135
- info — the re-pointable in-between tier; identity stays engine-internal),
136
- so the role tokens and the family ramp share a prefix: --critical-fg rides
137
- --critical-pen-70. The old alert-high/alert-med role prefixes collapsed
138
- into critical/warning in the same round.
139
-
140
- -fg-on-emphasis rides --paper-0, NOT a solved on-color: the engine's on-highlight
141
- token was deleted with the highlight band (owner 2026-07-29). paper-0 is the
142
- mode-flipping paper extreme — white in light, the tinted deep ground in dark — and
143
- it clears 4.5 against pencil-47 everywhere (agnostic 360-seed sweep, worst 4.96
144
- light / 8.04 dark). -bg-emphasis / -border-emphasis moved from highlight-9 to
145
- pencil-47 (ink-9 pre-Stage-B) with it; in dark that reads lighter (contrast vs
146
- paper-5, median 6.01 → 7.72). Stage B (owner 2026-08-07, names only) relabeled
147
- every leaf in the block below; the stops themselves did not move. */
131
+ Signal families emit by ROLE (critical/warning/positive/info, the re-pointable
132
+ in-between tier; identity stays engine-internal), so the role tokens and the
133
+ family ramp share a prefix: --critical-fg rides --critical-pen-70.
134
+
135
+ -fg-on-emphasis rides --paper-0, NOT a solved on-color. paper-0 is the
136
+ mode-flipping paper extreme (white in light, the deep tinted ground in dark) and
137
+ it clears the 4.5:1 text bar against pencil-47 in every family and both modes;
138
+ the band audit holds that floor over an agnostic sweep. -bg-emphasis and
139
+ -border-emphasis ride pencil-47. */
148
140
 
149
141
  [data-brand] {
150
142
  --critical-fg: var(--critical-pen-70);