okchroma 0.1.0 → 0.1.2

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.
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ CSS_FAMILY: () => CSS_FAMILY,
23
24
  DEFAULT_LINK_HEX: () => DEFAULT_LINK_HEX,
24
25
  EXT_KEY: () => EXT_KEY,
25
26
  INK_0: () => INK_0,
@@ -29,7 +30,9 @@ __export(index_exports, {
29
30
  PAPER_100: () => PAPER_100,
30
31
  RESOLVER_ID: () => RESOLVER_ID,
31
32
  SCALE_STOP_COUNT: () => SCALE_STOP_COUNT,
33
+ SCRIM_ALPHA: () => SCRIM_ALPHA,
32
34
  SECONDARY_DISTINCT_DELTA_E: () => SECONDARY_DISTINCT_DELTA_E,
35
+ SHADOW_ALPHAS: () => SHADOW_ALPHAS,
33
36
  SIGNALS: () => SIGNALS2,
34
37
  SIGNAL_EMIT_NAME: () => SIGNAL_EMIT_NAME,
35
38
  SIGNAL_SCALES: () => SIGNAL_SCALES,
@@ -41,16 +44,21 @@ __export(index_exports, {
41
44
  STAMP_STATE_LEAVES: () => STAMP_STATE_LEAVES,
42
45
  SUBTLE_PASTEL_K: () => SUBTLE_PASTEL_K,
43
46
  SUBTLE_TINT_MULT: () => SUBTLE_TINT_MULT,
47
+ SURFACE_PLANE_LAW: () => SURFACE_PLANE_LAW,
48
+ SYSTEM_LEAF: () => SYSTEM_LEAF,
44
49
  brandCss: () => brandCss,
45
50
  checkAllCollisions: () => checkAllCollisions,
46
51
  checkCollision: () => checkCollision,
47
52
  checkHueCollision: () => checkHueCollision,
48
53
  classifyArchetype: () => classifyArchetype,
54
+ ctaBorderRung: () => ctaBorderRung,
55
+ ctaNeedsBorder: () => ctaNeedsBorder,
49
56
  emitDtcgRamp: () => emitDtcgRamp,
50
57
  generateNeutralScale: () => generateNeutralScale,
51
58
  generateScale: () => generateScale,
52
59
  generateSubtleSecondary: () => generateSubtleSecondary,
53
60
  neutralCss: () => neutralCss,
61
+ pageStopFor: () => pageStopFor,
54
62
  parseToken: () => parseToken,
55
63
  resolveBrand: () => resolveBrand,
56
64
  resolveDtcgRamp: () => resolveDtcgRamp,
@@ -90,9 +98,11 @@ function stateStepL(restL, _mode, k) {
90
98
  return k * STATE_HOVER_K;
91
99
  }
92
100
  var OVERRIDE_LIGHT_BELOW = ARCHETYPES.find((a) => a.name === "rich").min;
101
+ var OVERRIDE_DARK_ABOVE = ARCHETYPES.find((a) => a.name === "light").min;
93
102
  function stateFillL(restL, mode, k) {
94
103
  const natural = mode === "dark" ? 1 : -1;
95
- const preferred = mode === "light" && restL < OVERRIDE_LIGHT_BELOW ? -natural : natural;
104
+ const overridden = mode === "light" ? restL < OVERRIDE_LIGHT_BELOW : restL > OVERRIDE_DARK_ABOVE;
105
+ const preferred = overridden ? -natural : natural;
96
106
  const fits = (d) => {
97
107
  const p = restL + d * stateStepL(restL, mode, 2);
98
108
  return p >= STATE_L_MIN && p <= STATE_L_MAX;
@@ -2619,7 +2629,7 @@ function generateNeutralScale(brandH, level = "default", contrastProfile) {
2619
2629
  scale.ctaHoverDark = nState(10, "dark", scale.ctaDark, 1);
2620
2630
  scale.ctaPressedDark = nState(11, "dark", scale.ctaDark, 2);
2621
2631
  const onEnforce = contrastProfile !== "apca";
2622
- const onFloor = contrastProfile === "apca" ? void 0 : 4.5;
2632
+ const onFloor = 4.5;
2623
2633
  scale.onFillTextIsWhite = onTextIsWhite(apcaY(scale.cta.r, scale.cta.g, scale.cta.b), scale.cta.L, scale.cta.C, scale.cta.H, onEnforce, onFloor);
2624
2634
  scale.onFillTextIsWhiteDark = onTextIsWhite(apcaY(scale.ctaDark.r, scale.ctaDark.g, scale.ctaDark.b), scale.ctaDark.L, scale.ctaDark.C, scale.ctaDark.H, onEnforce, onFloor);
2625
2635
  return scale;
@@ -2650,7 +2660,7 @@ function generateSubtleSecondary(hex, opts) {
2650
2660
  scale.ctaHoverDark = st(10, "dark", scale.ctaDark, 1);
2651
2661
  scale.ctaPressedDark = st(11, "dark", scale.ctaDark, 2);
2652
2662
  const onEnforce = opts?.contrastProfile !== "apca";
2653
- const onFloor = opts?.contrastProfile === "apca" ? void 0 : 4.5;
2663
+ const onFloor = 4.5;
2654
2664
  scale.onFillTextIsWhite = onTextIsWhite(apcaY(scale.cta.r, scale.cta.g, scale.cta.b), scale.cta.L, scale.cta.C, scale.cta.H, onEnforce, onFloor);
2655
2665
  scale.onFillTextIsWhiteDark = onTextIsWhite(apcaY(scale.ctaDark.r, scale.ctaDark.g, scale.ctaDark.b), scale.ctaDark.L, scale.ctaDark.C, scale.ctaDark.H, onEnforce, onFloor);
2656
2666
  return scale;
@@ -2695,7 +2705,7 @@ var COLOR_WORD = {
2695
2705
  [FAMILY.info]: " (blue)"
2696
2706
  };
2697
2707
  var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
2698
- var WASH = { req: "subtle interaction states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2708
+ var WASH = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2699
2709
  var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
2700
2710
  var OVERLAY = {
2701
2711
  req: "translucent backgrounds that hold their reading on any paper",
@@ -2713,12 +2723,15 @@ var SCALE = {
2713
2723
  "wash-89": WASH,
2714
2724
  "wash-85": WASH,
2715
2725
  "wash-80": WASH,
2726
+ // ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
2727
+ // landing on the mark rows is worth its ic-ON-s noise — the one surviving body carrier)
2716
2728
  "mark-74": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2717
2729
  "lead-53": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2718
2730
  "ink-42": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2719
2731
  // ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
2720
- // words, and a body carrying either floods that token's picker search — the C50 law)
2721
- "ink-30": { req: "strong-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2732
+ // words, and a body carrying either floods that token's picker search — the C50 law;
2733
+ // "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
2734
+ "ink-30": { req: "heavy-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2722
2735
  // the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
2723
2736
  // these bodies on purpose: it stopped being a token label, so it floods nothing,
2724
2737
  // and a designer's "cta" query still lands on these rows.
@@ -2729,17 +2742,17 @@ var SCALE = {
2729
2742
  // ("fill" reworded out 2026-08-18: fill became a label word and is foreign here)
2730
2743
  "stamp/on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}` }
2731
2744
  };
2732
- var ABS = { req: "max contrast on CTAs, aliased global endpoints" };
2733
- var OFFSET = { req: "min APCA visibility", theming: "offsets buttons in themes whose CTAs sit close to the page" };
2745
+ var ABS = { req: "extreme poles for CTAs, aliased global endpoints" };
2746
+ var OFFSET = { req: "min APCA visibility", theming: "offsets CTAs in themes where they sit close to the page" };
2734
2747
  var SHADOW = { req: "drop shadows" };
2735
2748
  var PLANE = (req) => ({ req, theming: "aliased to the gray ramp" });
2736
2749
  var LINK = (state, contrast) => ({
2737
2750
  req: "links" + state,
2738
2751
  contrast,
2739
- theming: "rides the theme\u2019s text action; custom seed re-solves; overridable per theme"
2752
+ theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme"
2740
2753
  });
2741
2754
  var LINK_INVERSE = (state, contrast) => ({
2742
- req: "links on inverted backgrounds" + state,
2755
+ req: "links over inverted backgrounds" + state,
2743
2756
  contrast,
2744
2757
  theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme"
2745
2758
  });
@@ -2750,19 +2763,23 @@ var SYSTEM = {
2750
2763
  // the anchor is engine-resolved and rides the neutral in BOTH plugins now —
2751
2764
  // NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
2752
2765
  "system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
2753
- "system/abs-alt": { req: "identity seed reference", theming: "the theme\u2019s companion input, as given" },
2766
+ // ("companion" reworded 2026-08-28: compani-ON fed the "on" flood)
2767
+ "system/abs-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given" },
2754
2768
  "system/alpha/transparent": { req: "aliased off-states" },
2755
2769
  // ("dimming" reworded 2026-08-18: the surface planes took dim as a label word)
2756
2770
  "system/alpha/abs-black-060": { req: "veils the page behind modals" },
2757
- // ("fills" reworded 2026-08-18: fill became a label word and is foreign here)
2758
- "system/alpha/ink": { req: "soft on-color for quiet CTAs" },
2771
+ // ("fills" reworded 2026-08-18: fill became a label word and is foreign here;
2772
+ // "on-color" reworded 2026-08-28: the bare on fed the "on" flood)
2773
+ "system/alpha/ink": { req: "soft text pole for quiet CTAs" },
2759
2774
  "system/alpha/006": OFFSET,
2760
2775
  "system/alpha/008": OFFSET,
2761
2776
  "system/alpha/016": OFFSET,
2762
2777
  "system/alpha/shadow-04": SHADOW,
2763
2778
  "system/alpha/shadow-08": SHADOW,
2764
2779
  "system/alpha/shadow-12": SHADOW,
2765
- "system/surface/dim": PLANE("recessed elevation plane"),
2780
+ // ("elevation" dropped 2026-08-28: elevati-ON fed the "on" flood; the sibling
2781
+ // planes never carried the word)
2782
+ "system/surface/dim": PLANE("recessed plane"),
2766
2783
  "system/surface/low": PLANE("resting page plane"),
2767
2784
  "system/surface/mid": PLANE("raised plane \u2014 cards, menus"),
2768
2785
  // ("overlays" reworded 2026-08-13: paper-overlay took the word as a label — the C50 law)
@@ -3393,6 +3410,33 @@ var STAMP_LEAF = {
3393
3410
  EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
3394
3411
  ON: STAMP_STATE_LEAVES[STAMP_ON]
3395
3412
  };
3413
+ var SYSTEM_LEAF = {
3414
+ ABS_BLACK: "system/abs-black",
3415
+ ABS_WHITE: "system/abs-white",
3416
+ SURFACE: {
3417
+ DIM: "system/surface/dim",
3418
+ LOW: "system/surface/low",
3419
+ MID: "system/surface/mid",
3420
+ HIGH: "system/surface/high"
3421
+ },
3422
+ ALPHA: {
3423
+ TRANSPARENT: "system/alpha/transparent",
3424
+ SCRIM: "system/alpha/abs-black-060",
3425
+ INK: "system/alpha/ink",
3426
+ OFFSET_006: "system/alpha/006",
3427
+ OFFSET_008: "system/alpha/008",
3428
+ OFFSET_016: "system/alpha/016",
3429
+ SHADOW_04: "system/alpha/shadow-04",
3430
+ SHADOW_08: "system/alpha/shadow-08",
3431
+ SHADOW_12: "system/alpha/shadow-12"
3432
+ }
3433
+ };
3434
+ var SURFACE_PLANE_LAW = {
3435
+ [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_100 },
3436
+ [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
3437
+ [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
3438
+ [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_100, dark: SHARED_NAMES[3] }
3439
+ };
3396
3440
  var TOKEN_ORDER = [
3397
3441
  ...Object.values(SHARED_NAMES),
3398
3442
  STAMP_FILL,
@@ -3438,6 +3482,12 @@ var OFFSET_ALPHAS = { 6: 0.06, 8: 0.08, 16: 0.16 };
3438
3482
  var ctaBorderRung = (prefix) => prefix === CSS_NEUTRAL ? 8 : prefix === CSS_SECONDARY ? 6 : 16;
3439
3483
  var offsetLeafName = (rung) => String(rung).padStart(3, "0");
3440
3484
  var offsetVarName = (rung) => `--alpha-${offsetLeafName(rung)}`;
3485
+ var SHADOW_ALPHAS = {
3486
+ 4: { light: 0.04, dark: 0.32 },
3487
+ 8: { light: 0.08, dark: 0.48 },
3488
+ 12: { light: 0.12, dark: 0.64 }
3489
+ };
3490
+ var SCRIM_ALPHA = 0.6;
3441
3491
  var TRANSPARENT_VAR = "--alpha-transparent";
3442
3492
  var offsetRgba = (rung, mode) => mode === "light" ? `rgba(0, 0, 0, ${OFFSET_ALPHAS[rung]})` : `rgba(255, 255, 255, ${OFFSET_ALPHAS[rung]})`;
3443
3493
  var alphaRootVars = (mode) => [
@@ -3627,7 +3677,7 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3627
3677
  };
3628
3678
  const softOnCta = (mode) => {
3629
3679
  if (!secondary || secondaryStyle === "outline") return [];
3630
- if (secondaryStyle !== "default" && !softOnCtaPasses(secondary, mode)) return [];
3680
+ if (!softOnCtaPasses(secondary, mode)) return [];
3631
3681
  const white = mode === "light" ? secondary.onFillTextIsWhite : secondary.onFillTextIsWhiteDark;
3632
3682
  return [` --${CSS_SECONDARY}-stamp-on: rgba(${white ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});`];
3633
3683
  };
@@ -3820,7 +3870,7 @@ function themeToFigma(r, input) {
3820
3870
  });
3821
3871
  };
3822
3872
  softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
3823
- if (input.secondary && input.secondaryStyle !== "outline" && (input.secondaryStyle === "default" || softOnCtaPasses(input.secondary, mode)))
3873
+ if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
3824
3874
  softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
3825
3875
  const brandGroup = rampGroup(scale[mode], mode === "light" ? scale.onFillTextIsWhite : scale.onFillTextIsWhiteDark, brandExtra(scale, mode, CSS_FAMILY.brandPrimary));
3826
3876
  const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
@@ -3855,6 +3905,28 @@ function themeToFigma(r, input) {
3855
3905
  ctaFamily(sig.scale, mode, sig.name)
3856
3906
  );
3857
3907
  }
3908
+ const pole = (white, alpha) => {
3909
+ const c = white ? 1 : 0;
3910
+ return {
3911
+ $type: "color",
3912
+ $value: { colorSpace: "srgb", components: [c, c, c], alpha, hex: white ? "#ffffff" : "#000000" }
3913
+ };
3914
+ };
3915
+ putLeaf(g2, SYSTEM_LEAF.ABS_BLACK, colorFromHex(false));
3916
+ putLeaf(g2, SYSTEM_LEAF.ABS_WHITE, colorFromHex(true));
3917
+ for (const [path, law] of Object.entries(SURFACE_PLANE_LAW)) {
3918
+ const tok = neutralGroup[law[mode]];
3919
+ if (tok && "$type" in tok) putLeaf(g2, path, tok);
3920
+ }
3921
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.TRANSPARENT, pole(true, 0));
3922
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SCRIM, pole(false, SCRIM_ALPHA));
3923
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.INK, pole(mode === "dark", SOFT_ON_CTA_ALPHA[mode]));
3924
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.OFFSET_006, OFFSET_TOKEN(6, mode));
3925
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.OFFSET_008, OFFSET_TOKEN(8, mode));
3926
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.OFFSET_016, OFFSET_TOKEN(16, mode));
3927
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_04, pole(false, SHADOW_ALPHAS[4][mode]));
3928
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_08, pole(false, SHADOW_ALPHAS[8][mode]));
3929
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_12, pole(false, SHADOW_ALPHAS[12][mode]));
3858
3930
  return g2;
3859
3931
  };
3860
3932
  return { light: build("light"), dark: build("dark") };
@@ -20,9 +20,11 @@ function stateStepL(restL, _mode, k) {
20
20
  return k * STATE_HOVER_K;
21
21
  }
22
22
  var OVERRIDE_LIGHT_BELOW = ARCHETYPES.find((a) => a.name === "rich").min;
23
+ var OVERRIDE_DARK_ABOVE = ARCHETYPES.find((a) => a.name === "light").min;
23
24
  function stateFillL(restL, mode, k) {
24
25
  const natural = mode === "dark" ? 1 : -1;
25
- const preferred = mode === "light" && restL < OVERRIDE_LIGHT_BELOW ? -natural : natural;
26
+ const overridden = mode === "light" ? restL < OVERRIDE_LIGHT_BELOW : restL > OVERRIDE_DARK_ABOVE;
27
+ const preferred = overridden ? -natural : natural;
26
28
  const fits = (d) => {
27
29
  const p = restL + d * stateStepL(restL, mode, 2);
28
30
  return p >= STATE_L_MIN && p <= STATE_L_MAX;
@@ -2549,7 +2551,7 @@ function generateNeutralScale(brandH, level = "default", contrastProfile) {
2549
2551
  scale.ctaHoverDark = nState(10, "dark", scale.ctaDark, 1);
2550
2552
  scale.ctaPressedDark = nState(11, "dark", scale.ctaDark, 2);
2551
2553
  const onEnforce = contrastProfile !== "apca";
2552
- const onFloor = contrastProfile === "apca" ? void 0 : 4.5;
2554
+ const onFloor = 4.5;
2553
2555
  scale.onFillTextIsWhite = onTextIsWhite(apcaY(scale.cta.r, scale.cta.g, scale.cta.b), scale.cta.L, scale.cta.C, scale.cta.H, onEnforce, onFloor);
2554
2556
  scale.onFillTextIsWhiteDark = onTextIsWhite(apcaY(scale.ctaDark.r, scale.ctaDark.g, scale.ctaDark.b), scale.ctaDark.L, scale.ctaDark.C, scale.ctaDark.H, onEnforce, onFloor);
2555
2557
  return scale;
@@ -2580,7 +2582,7 @@ function generateSubtleSecondary(hex, opts) {
2580
2582
  scale.ctaHoverDark = st(10, "dark", scale.ctaDark, 1);
2581
2583
  scale.ctaPressedDark = st(11, "dark", scale.ctaDark, 2);
2582
2584
  const onEnforce = opts?.contrastProfile !== "apca";
2583
- const onFloor = opts?.contrastProfile === "apca" ? void 0 : 4.5;
2585
+ const onFloor = 4.5;
2584
2586
  scale.onFillTextIsWhite = onTextIsWhite(apcaY(scale.cta.r, scale.cta.g, scale.cta.b), scale.cta.L, scale.cta.C, scale.cta.H, onEnforce, onFloor);
2585
2587
  scale.onFillTextIsWhiteDark = onTextIsWhite(apcaY(scale.ctaDark.r, scale.ctaDark.g, scale.ctaDark.b), scale.ctaDark.L, scale.ctaDark.C, scale.ctaDark.H, onEnforce, onFloor);
2586
2588
  return scale;
@@ -2625,7 +2627,7 @@ var COLOR_WORD = {
2625
2627
  [FAMILY.info]: " (blue)"
2626
2628
  };
2627
2629
  var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
2628
- var WASH = { req: "subtle interaction states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2630
+ var WASH = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2629
2631
  var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
2630
2632
  var OVERLAY = {
2631
2633
  req: "translucent backgrounds that hold their reading on any paper",
@@ -2643,12 +2645,15 @@ var SCALE = {
2643
2645
  "wash-89": WASH,
2644
2646
  "wash-85": WASH,
2645
2647
  "wash-80": WASH,
2648
+ // ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
2649
+ // landing on the mark rows is worth its ic-ON-s noise — the one surviving body carrier)
2646
2650
  "mark-74": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2647
2651
  "lead-53": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2648
2652
  "ink-42": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2649
2653
  // ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
2650
- // words, and a body carrying either floods that token's picker search — the C50 law)
2651
- "ink-30": { req: "strong-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2654
+ // words, and a body carrying either floods that token's picker search — the C50 law;
2655
+ // "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
2656
+ "ink-30": { req: "heavy-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2652
2657
  // the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
2653
2658
  // these bodies on purpose: it stopped being a token label, so it floods nothing,
2654
2659
  // and a designer's "cta" query still lands on these rows.
@@ -2659,17 +2664,17 @@ var SCALE = {
2659
2664
  // ("fill" reworded out 2026-08-18: fill became a label word and is foreign here)
2660
2665
  "stamp/on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}` }
2661
2666
  };
2662
- var ABS = { req: "max contrast on CTAs, aliased global endpoints" };
2663
- var OFFSET = { req: "min APCA visibility", theming: "offsets buttons in themes whose CTAs sit close to the page" };
2667
+ var ABS = { req: "extreme poles for CTAs, aliased global endpoints" };
2668
+ var OFFSET = { req: "min APCA visibility", theming: "offsets CTAs in themes where they sit close to the page" };
2664
2669
  var SHADOW = { req: "drop shadows" };
2665
2670
  var PLANE = (req) => ({ req, theming: "aliased to the gray ramp" });
2666
2671
  var LINK = (state, contrast) => ({
2667
2672
  req: "links" + state,
2668
2673
  contrast,
2669
- theming: "rides the theme\u2019s text action; custom seed re-solves; overridable per theme"
2674
+ theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme"
2670
2675
  });
2671
2676
  var LINK_INVERSE = (state, contrast) => ({
2672
- req: "links on inverted backgrounds" + state,
2677
+ req: "links over inverted backgrounds" + state,
2673
2678
  contrast,
2674
2679
  theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme"
2675
2680
  });
@@ -2680,19 +2685,23 @@ var SYSTEM = {
2680
2685
  // the anchor is engine-resolved and rides the neutral in BOTH plugins now —
2681
2686
  // NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
2682
2687
  "system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
2683
- "system/abs-alt": { req: "identity seed reference", theming: "the theme\u2019s companion input, as given" },
2688
+ // ("companion" reworded 2026-08-28: compani-ON fed the "on" flood)
2689
+ "system/abs-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given" },
2684
2690
  "system/alpha/transparent": { req: "aliased off-states" },
2685
2691
  // ("dimming" reworded 2026-08-18: the surface planes took dim as a label word)
2686
2692
  "system/alpha/abs-black-060": { req: "veils the page behind modals" },
2687
- // ("fills" reworded 2026-08-18: fill became a label word and is foreign here)
2688
- "system/alpha/ink": { req: "soft on-color for quiet CTAs" },
2693
+ // ("fills" reworded 2026-08-18: fill became a label word and is foreign here;
2694
+ // "on-color" reworded 2026-08-28: the bare on fed the "on" flood)
2695
+ "system/alpha/ink": { req: "soft text pole for quiet CTAs" },
2689
2696
  "system/alpha/006": OFFSET,
2690
2697
  "system/alpha/008": OFFSET,
2691
2698
  "system/alpha/016": OFFSET,
2692
2699
  "system/alpha/shadow-04": SHADOW,
2693
2700
  "system/alpha/shadow-08": SHADOW,
2694
2701
  "system/alpha/shadow-12": SHADOW,
2695
- "system/surface/dim": PLANE("recessed elevation plane"),
2702
+ // ("elevation" dropped 2026-08-28: elevati-ON fed the "on" flood; the sibling
2703
+ // planes never carried the word)
2704
+ "system/surface/dim": PLANE("recessed plane"),
2696
2705
  "system/surface/low": PLANE("resting page plane"),
2697
2706
  "system/surface/mid": PLANE("raised plane \u2014 cards, menus"),
2698
2707
  // ("overlays" reworded 2026-08-13: paper-overlay took the word as a label — the C50 law)
@@ -3323,6 +3332,33 @@ var STAMP_LEAF = {
3323
3332
  EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
3324
3333
  ON: STAMP_STATE_LEAVES[STAMP_ON]
3325
3334
  };
3335
+ var SYSTEM_LEAF = {
3336
+ ABS_BLACK: "system/abs-black",
3337
+ ABS_WHITE: "system/abs-white",
3338
+ SURFACE: {
3339
+ DIM: "system/surface/dim",
3340
+ LOW: "system/surface/low",
3341
+ MID: "system/surface/mid",
3342
+ HIGH: "system/surface/high"
3343
+ },
3344
+ ALPHA: {
3345
+ TRANSPARENT: "system/alpha/transparent",
3346
+ SCRIM: "system/alpha/abs-black-060",
3347
+ INK: "system/alpha/ink",
3348
+ OFFSET_006: "system/alpha/006",
3349
+ OFFSET_008: "system/alpha/008",
3350
+ OFFSET_016: "system/alpha/016",
3351
+ SHADOW_04: "system/alpha/shadow-04",
3352
+ SHADOW_08: "system/alpha/shadow-08",
3353
+ SHADOW_12: "system/alpha/shadow-12"
3354
+ }
3355
+ };
3356
+ var SURFACE_PLANE_LAW = {
3357
+ [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_100 },
3358
+ [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
3359
+ [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
3360
+ [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_100, dark: SHARED_NAMES[3] }
3361
+ };
3326
3362
  var TOKEN_ORDER = [
3327
3363
  ...Object.values(SHARED_NAMES),
3328
3364
  STAMP_FILL,
@@ -3368,6 +3404,12 @@ var OFFSET_ALPHAS = { 6: 0.06, 8: 0.08, 16: 0.16 };
3368
3404
  var ctaBorderRung = (prefix) => prefix === CSS_NEUTRAL ? 8 : prefix === CSS_SECONDARY ? 6 : 16;
3369
3405
  var offsetLeafName = (rung) => String(rung).padStart(3, "0");
3370
3406
  var offsetVarName = (rung) => `--alpha-${offsetLeafName(rung)}`;
3407
+ var SHADOW_ALPHAS = {
3408
+ 4: { light: 0.04, dark: 0.32 },
3409
+ 8: { light: 0.08, dark: 0.48 },
3410
+ 12: { light: 0.12, dark: 0.64 }
3411
+ };
3412
+ var SCRIM_ALPHA = 0.6;
3371
3413
  var TRANSPARENT_VAR = "--alpha-transparent";
3372
3414
  var offsetRgba = (rung, mode) => mode === "light" ? `rgba(0, 0, 0, ${OFFSET_ALPHAS[rung]})` : `rgba(255, 255, 255, ${OFFSET_ALPHAS[rung]})`;
3373
3415
  var alphaRootVars = (mode) => [
@@ -3557,7 +3599,7 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3557
3599
  };
3558
3600
  const softOnCta = (mode) => {
3559
3601
  if (!secondary || secondaryStyle === "outline") return [];
3560
- if (secondaryStyle !== "default" && !softOnCtaPasses(secondary, mode)) return [];
3602
+ if (!softOnCtaPasses(secondary, mode)) return [];
3561
3603
  const white = mode === "light" ? secondary.onFillTextIsWhite : secondary.onFillTextIsWhiteDark;
3562
3604
  return [` --${CSS_SECONDARY}-stamp-on: rgba(${white ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});`];
3563
3605
  };
@@ -3750,7 +3792,7 @@ function themeToFigma(r, input) {
3750
3792
  });
3751
3793
  };
3752
3794
  softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
3753
- if (input.secondary && input.secondaryStyle !== "outline" && (input.secondaryStyle === "default" || softOnCtaPasses(input.secondary, mode)))
3795
+ if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
3754
3796
  softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
3755
3797
  const brandGroup = rampGroup(scale[mode], mode === "light" ? scale.onFillTextIsWhite : scale.onFillTextIsWhiteDark, brandExtra(scale, mode, CSS_FAMILY.brandPrimary));
3756
3798
  const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
@@ -3785,6 +3827,28 @@ function themeToFigma(r, input) {
3785
3827
  ctaFamily(sig.scale, mode, sig.name)
3786
3828
  );
3787
3829
  }
3830
+ const pole = (white, alpha) => {
3831
+ const c = white ? 1 : 0;
3832
+ return {
3833
+ $type: "color",
3834
+ $value: { colorSpace: "srgb", components: [c, c, c], alpha, hex: white ? "#ffffff" : "#000000" }
3835
+ };
3836
+ };
3837
+ putLeaf(g2, SYSTEM_LEAF.ABS_BLACK, colorFromHex(false));
3838
+ putLeaf(g2, SYSTEM_LEAF.ABS_WHITE, colorFromHex(true));
3839
+ for (const [path, law] of Object.entries(SURFACE_PLANE_LAW)) {
3840
+ const tok = neutralGroup[law[mode]];
3841
+ if (tok && "$type" in tok) putLeaf(g2, path, tok);
3842
+ }
3843
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.TRANSPARENT, pole(true, 0));
3844
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SCRIM, pole(false, SCRIM_ALPHA));
3845
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.INK, pole(mode === "dark", SOFT_ON_CTA_ALPHA[mode]));
3846
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.OFFSET_006, OFFSET_TOKEN(6, mode));
3847
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.OFFSET_008, OFFSET_TOKEN(8, mode));
3848
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.OFFSET_016, OFFSET_TOKEN(16, mode));
3849
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_04, pole(false, SHADOW_ALPHAS[4][mode]));
3850
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_08, pole(false, SHADOW_ALPHAS[8][mode]));
3851
+ putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_12, pole(false, SHADOW_ALPHAS[12][mode]));
3788
3852
  return g2;
3789
3853
  };
3790
3854
  return { light: build("light"), dark: build("dark") };
@@ -3853,6 +3917,7 @@ function resolveDtcgRamp(group) {
3853
3917
  return resolveRamp(seedHex, mode, { stops, roles, ons });
3854
3918
  }
3855
3919
  export {
3920
+ CSS_FAMILY,
3856
3921
  DEFAULT_LINK_HEX,
3857
3922
  EXT_KEY,
3858
3923
  INK_0,
@@ -3862,7 +3927,9 @@ export {
3862
3927
  PAPER_100,
3863
3928
  RESOLVER_ID,
3864
3929
  SCALE_STOP_COUNT,
3930
+ SCRIM_ALPHA,
3865
3931
  SECONDARY_DISTINCT_DELTA_E,
3932
+ SHADOW_ALPHAS,
3866
3933
  SIGNALS2 as SIGNALS,
3867
3934
  SIGNAL_EMIT_NAME,
3868
3935
  SIGNAL_SCALES,
@@ -3874,16 +3941,21 @@ export {
3874
3941
  STAMP_STATE_LEAVES,
3875
3942
  SUBTLE_PASTEL_K,
3876
3943
  SUBTLE_TINT_MULT,
3944
+ SURFACE_PLANE_LAW,
3945
+ SYSTEM_LEAF,
3877
3946
  brandCss,
3878
3947
  checkAllCollisions,
3879
3948
  checkCollision,
3880
3949
  checkHueCollision,
3881
3950
  classifyArchetype,
3951
+ ctaBorderRung,
3952
+ ctaNeedsBorder,
3882
3953
  emitDtcgRamp,
3883
3954
  generateNeutralScale,
3884
3955
  generateScale,
3885
3956
  generateSubtleSecondary,
3886
3957
  neutralCss,
3958
+ pageStopFor,
3887
3959
  parseToken,
3888
3960
  resolveBrand,
3889
3961
  resolveDtcgRamp,
@@ -17,6 +17,22 @@ export declare const ctaBorderRung: (prefix: string) => OffsetRung;
17
17
  export declare const offsetLeafName: (rung: OffsetRung) => string;
18
18
  export declare const offsetVarName: (rung: OffsetRung) => string;
19
19
  export declare const offsetTokenPath: (rung: OffsetRung) => string;
20
+ export declare const SHADOW_ALPHAS: {
21
+ readonly 4: {
22
+ readonly light: 0.04;
23
+ readonly dark: 0.32;
24
+ };
25
+ readonly 8: {
26
+ readonly light: 0.08;
27
+ readonly dark: 0.48;
28
+ };
29
+ readonly 12: {
30
+ readonly light: 0.12;
31
+ readonly dark: 0.64;
32
+ };
33
+ };
34
+ export type ShadowRung = keyof typeof SHADOW_ALPHAS;
35
+ export declare const SCRIM_ALPHA = 0.6;
20
36
  export declare const TRANSPARENT_VAR = "--alpha-transparent";
21
37
  export declare const offsetRgba: (rung: OffsetRung, mode: "light" | "dark") => string;
22
38
  export declare const alphaRootVars: (mode: "light" | "dark") => string[];
@@ -15,6 +15,31 @@ export declare const STAMP_LEAF: {
15
15
  readonly EDGE: string;
16
16
  readonly ON: string;
17
17
  };
18
+ export declare const SYSTEM_LEAF: {
19
+ readonly ABS_BLACK: "system/abs-black";
20
+ readonly ABS_WHITE: "system/abs-white";
21
+ readonly SURFACE: {
22
+ readonly DIM: "system/surface/dim";
23
+ readonly LOW: "system/surface/low";
24
+ readonly MID: "system/surface/mid";
25
+ readonly HIGH: "system/surface/high";
26
+ };
27
+ readonly ALPHA: {
28
+ readonly TRANSPARENT: "system/alpha/transparent";
29
+ readonly SCRIM: "system/alpha/abs-black-060";
30
+ readonly INK: "system/alpha/ink";
31
+ readonly OFFSET_006: "system/alpha/006";
32
+ readonly OFFSET_008: "system/alpha/008";
33
+ readonly OFFSET_016: "system/alpha/016";
34
+ readonly SHADOW_04: "system/alpha/shadow-04";
35
+ readonly SHADOW_08: "system/alpha/shadow-08";
36
+ readonly SHADOW_12: "system/alpha/shadow-12";
37
+ };
38
+ };
39
+ export declare const SURFACE_PLANE_LAW: Record<string, {
40
+ light: string;
41
+ dark: string;
42
+ }>;
18
43
  export declare const CONTRACT_INVARIANT_ROWS: ReadonlySet<string>;
19
44
  export declare const EXT_NON_OVERRIDABLE: (p: string) => boolean;
20
45
  export declare const EXT_OVERRIDABLE_SYSTEM: (p: string) => boolean;
@@ -1,10 +1,11 @@
1
1
  export { generateScale, generateNeutralScale, generateSubtleSecondary, 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 } from './engine/cssRender';
3
+ export { brandCss, neutralCss, signalsCss, stopsToVars, toHex, stopHex, OFFSET_ALPHAS, SHADOW_ALPHAS, SCRIM_ALPHA, type ShadowRung, ctaNeedsBorder, ctaBorderRung, pageStopFor, } from './engine/cssRender';
4
+ export { CSS_FAMILY } from './engine/tokenDescriptions';
4
5
  export { themeToFigma, type FigmaGroup, type FigmaColorToken, type ThemeInput, } from './engine/figmaRender';
5
6
  export { emitDtcgRamp, resolveDtcgRamp, parseToken, EXT_KEY, RESOLVER_ID, type DtcgRampGroup, type DtcgRequirementToken, type DtcgSeedToken, type DtcgColorValue, } from './engine/requirements/dtcg';
6
7
  export { MODE_SPECS, type ModeSpec } from './engine/requirements/spec';
7
- export { stopTokenName, tokenOrder, SCALE_STOP_COUNT, PAPER_100, INK_0, STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED, STAMP_EDGE, STAMP_ON, STAMP_STATE_LEAVES, } from './engine/tokenNames';
8
+ export { stopTokenName, tokenOrder, SCALE_STOP_COUNT, PAPER_100, INK_0, STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED, STAMP_EDGE, STAMP_ON, STAMP_STATE_LEAVES, SYSTEM_LEAF, SURFACE_PLANE_LAW, } from './engine/tokenNames';
8
9
  export { SIGNAL_EMIT_NAME } from './engine/signals';
9
10
  export { classifyArchetype, type Archetype } from './engine/archetypes';
10
11
  export { SIGNALS, type SignalDef } from './engine/signals';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okchroma",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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": {
@@ -18,7 +18,8 @@
18
18
  }
19
19
  },
20
20
  "files": [
21
- "dist-lib"
21
+ "dist-lib",
22
+ "tokens"
22
23
  ],
23
24
  "sideEffects": false,
24
25
  "scripts": {
@@ -0,0 +1,204 @@
1
+ /*
2
+ Semantic token layer — maps role names to primitive tokens.
3
+ This file never changes per brand. Only the primitives (--brand-*, --neutral-*) change.
4
+ */
5
+
6
+ /* ─── Global surface / fg / border ─────────────────────────────────────────── */
7
+
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-100's side of the ramp. Light descends the ramp as elevation rises
14
+ (sunken paper-95 → high paper-100 white); dark ascends it (sunken paper-100
15
+ black → high paper-95). paper-100 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. */
18
+ --surface-dim: var(--neutral-paper-95);
19
+ --surface-low: var(--neutral-paper-97);
20
+ --surface-mid: var(--neutral-paper-99);
21
+ --surface-high: var(--paper-100, #ffffff);
22
+
23
+ --fg-default: var(--neutral-ink-30);
24
+ --fg-subtle: var(--neutral-lead-53);
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 ink 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 mark-74, 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; mark-74 is where the
32
+ requirement actually lives, and it is the one stop this round does not move.
33
+ Decorative borders are --border-subtle (wash-89) and the per-family
34
+ --*-border-default (wash-85). */
35
+ --border-default: var(--neutral-mark-74);
36
+ --border-subtle: var(--neutral-wash-89);
37
+
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. */
43
+ --shadow-04: rgba(0, 0, 0, 0.04);
44
+ --shadow-08: rgba(0, 0, 0, 0.08);
45
+ --shadow-12: rgba(0, 0, 0, 0.12);
46
+ }
47
+
48
+ [data-brand][data-theme="dark"] {
49
+ /* Dark runs the same four stops upward: sunken paper-100 (black) → low paper-99 →
50
+ base paper-97 → high paper-95 — the one-level highest background (C27: every
51
+ family's paper-95 shares one photometric level, so chips sit flush on the high
52
+ plane by construction; a generated candidate for it was tried and retired,
53
+ owner 2026-07-28). Low needs its own override here because the shared block's
54
+ low is paper-97 (the light value); the stops themselves did not move. */
55
+ --surface-dim: var(--paper-100, #000000);
56
+ --surface-low: var(--neutral-paper-99);
57
+ --surface-mid: var(--neutral-paper-97);
58
+ --surface-high: var(--neutral-paper-95);
59
+
60
+ --shadow-04: rgba(0, 0, 0, 0.32);
61
+ --shadow-08: rgba(0, 0, 0, 0.48);
62
+ --shadow-12: rgba(0, 0, 0, 0.64);
63
+ }
64
+
65
+ /* ─── role.brand ────────────────────────────────────────────────────────────── */
66
+
67
+ [data-brand] {
68
+ --brand-fg: var(--brand-ink-30);
69
+ --brand-fg-hover: var(--brand-lead-53);
70
+ --brand-fg-alt: var(--brand-lead-53);
71
+ --brand-fg-alt-hover: var(--brand-ink-30);
72
+ --brand-fg-on-emphasis: var(--brand-stamp-on);
73
+
74
+ --brand-bg-faint: var(--brand-paper-97);
75
+ --brand-bg-subtle: var(--brand-wash-89);
76
+ --brand-bg-subtle-hover: var(--brand-wash-85);
77
+ --brand-bg-emphasis: var(--brand-stamp-fill);
78
+ --brand-bg-emphasis-hover: var(--brand-stamp-fill-hover);
79
+ --brand-bg-emphasis-pressed: var(--brand-stamp-fill-pressed);
80
+
81
+ --brand-border-subtle: var(--brand-wash-92);
82
+ --brand-border-subtle-hover: var(--brand-wash-89);
83
+ --brand-border-default: var(--brand-wash-85);
84
+ --brand-border-default-hover: var(--brand-mark-74);
85
+ --brand-border-emphasis: var(--brand-stamp-fill);
86
+ --brand-border-emphasis-hover: var(--brand-stamp-fill-hover);
87
+
88
+ /* the SYSTEM LINK (one trio per theme — aliases the primary's ink stops unless a custom
89
+ link color de-conflicts it; --link/-hover/-pressed are emitted per brand) */
90
+ --fg-link: var(--link);
91
+ --fg-link-hover: var(--link-hover);
92
+ --fg-link-pressed: var(--link-pressed);
93
+
94
+ /* the INVERSE link trio (owner round 2026-08-19): the same link seed re-solved for
95
+ text on inverted surfaces (an ink-30 fill); always its own emitted values —
96
+ no stop is anchored against that ground, so there is no alias posture */
97
+ --fg-link-inverse: var(--link-inverse);
98
+ --fg-link-inverse-hover: var(--link-inverse-hover);
99
+ --fg-link-inverse-pressed: var(--link-inverse-pressed);
100
+ }
101
+
102
+ /* ─── role.secondary (the brand's secondary ramp; mirrors brand when none) ──── */
103
+
104
+ [data-brand] {
105
+ --brand-alt-fg: var(--brand-alt-ink-30);
106
+ --brand-alt-fg-hover: var(--brand-alt-lead-53);
107
+ --brand-alt-fg-alt: var(--brand-alt-lead-53);
108
+ --brand-alt-fg-alt-hover: var(--brand-alt-ink-30);
109
+ --brand-alt-fg-on-emphasis: var(--brand-alt-stamp-on);
110
+
111
+ --brand-alt-bg-faint: var(--brand-alt-paper-97);
112
+ --brand-alt-bg-subtle: var(--brand-alt-wash-89);
113
+ --brand-alt-bg-subtle-hover: var(--brand-alt-wash-85);
114
+ --brand-alt-bg-emphasis: var(--brand-alt-stamp-fill);
115
+ --brand-alt-bg-emphasis-hover: var(--brand-alt-stamp-fill-hover);
116
+ --brand-alt-bg-emphasis-pressed: var(--brand-alt-stamp-fill-pressed);
117
+
118
+ --brand-alt-border-subtle: var(--brand-alt-wash-92);
119
+ --brand-alt-border-subtle-hover: var(--brand-alt-wash-89);
120
+ --brand-alt-border-default: var(--brand-alt-wash-85);
121
+ --brand-alt-border-default-hover: var(--brand-alt-mark-74);
122
+ --brand-alt-border-emphasis: var(--brand-alt-stamp-fill);
123
+ --brand-alt-border-emphasis-hover: var(--brand-alt-stamp-fill-hover);
124
+ }
125
+
126
+ /* ─── Signal roles ──────────────────────────────────────────────────────────
127
+ Aliases onto engine-generated signal primitives (dist/signals.css).
128
+ Mode switching happens at the primitive layer, so one block covers both.
129
+ Signal families emit by ROLE (owner 2026-07-27: critical/warning/positive/
130
+ info — the re-pointable in-between tier; identity stays engine-internal),
131
+ so the role tokens and the family ramp share a prefix: --critical-fg rides
132
+ --critical-ink-30. The old alert-high/alert-med role prefixes collapsed
133
+ into critical/warning in the same round.
134
+
135
+ -fg-on-emphasis rides --paper-100, NOT a solved on-color: the engine's on-highlight
136
+ token was deleted with the highlight band (owner 2026-07-29). paper-100 is the
137
+ mode-flipping paper extreme — white in light, the tinted deep ground in dark — and
138
+ it clears 4.5 against lead-53 everywhere (agnostic 360-seed sweep, worst 4.96
139
+ light / 8.04 dark). -bg-emphasis / -border-emphasis moved from highlight-9 to
140
+ lead-53 (ink-9 pre-Stage-B) with it; in dark that reads lighter (contrast vs
141
+ paper-95, median 6.01 → 7.72). Stage B (owner 2026-08-07, names only) relabeled
142
+ every leaf in the block below; the stops themselves did not move. */
143
+
144
+ [data-brand] {
145
+ --critical-fg: var(--critical-ink-30);
146
+ --critical-fg-hover: var(--critical-lead-53);
147
+ --critical-fg-alt: var(--critical-lead-53);
148
+ --critical-fg-alt-hover: var(--critical-ink-30);
149
+ --critical-fg-on-emphasis: var(--paper-100);
150
+ --critical-bg-faint: var(--critical-paper-97);
151
+ --critical-bg-subtle: var(--critical-wash-89);
152
+ --critical-bg-subtle-hover: var(--critical-wash-85);
153
+ --critical-bg-emphasis: var(--critical-lead-53);
154
+ --critical-border-subtle: var(--critical-wash-92);
155
+ --critical-border-subtle-hover: var(--critical-wash-89);
156
+ --critical-border-default: var(--critical-wash-85);
157
+ --critical-border-default-hover: var(--critical-mark-74);
158
+ --critical-border-emphasis: var(--critical-lead-53);
159
+
160
+ --warning-fg: var(--warning-ink-30);
161
+ --warning-fg-hover: var(--warning-lead-53);
162
+ --warning-fg-alt: var(--warning-lead-53);
163
+ --warning-fg-alt-hover: var(--warning-ink-30);
164
+ --warning-fg-on-emphasis: var(--paper-100);
165
+ --warning-bg-faint: var(--warning-paper-97);
166
+ --warning-bg-subtle: var(--warning-wash-89);
167
+ --warning-bg-subtle-hover: var(--warning-wash-85);
168
+ --warning-bg-emphasis: var(--warning-lead-53);
169
+ --warning-border-subtle: var(--warning-wash-92);
170
+ --warning-border-subtle-hover: var(--warning-wash-89);
171
+ --warning-border-default: var(--warning-wash-85);
172
+ --warning-border-default-hover: var(--warning-mark-74);
173
+ --warning-border-emphasis: var(--warning-lead-53);
174
+
175
+ --positive-fg: var(--positive-ink-30);
176
+ --positive-fg-hover: var(--positive-lead-53);
177
+ --positive-fg-alt: var(--positive-lead-53);
178
+ --positive-fg-alt-hover: var(--positive-ink-30);
179
+ --positive-fg-on-emphasis: var(--paper-100);
180
+ --positive-bg-faint: var(--positive-paper-97);
181
+ --positive-bg-subtle: var(--positive-wash-89);
182
+ --positive-bg-subtle-hover: var(--positive-wash-85);
183
+ --positive-bg-emphasis: var(--positive-lead-53);
184
+ --positive-border-subtle: var(--positive-wash-92);
185
+ --positive-border-subtle-hover: var(--positive-wash-89);
186
+ --positive-border-default: var(--positive-wash-85);
187
+ --positive-border-default-hover: var(--positive-mark-74);
188
+ --positive-border-emphasis: var(--positive-lead-53);
189
+
190
+ --info-fg: var(--info-ink-30);
191
+ --info-fg-hover: var(--info-lead-53);
192
+ --info-fg-alt: var(--info-lead-53);
193
+ --info-fg-alt-hover: var(--info-ink-30);
194
+ --info-fg-on-emphasis: var(--paper-100);
195
+ --info-bg-faint: var(--info-paper-97);
196
+ --info-bg-subtle: var(--info-wash-89);
197
+ --info-bg-subtle-hover: var(--info-wash-85);
198
+ --info-bg-emphasis: var(--info-lead-53);
199
+ --info-border-subtle: var(--info-wash-92);
200
+ --info-border-subtle-hover: var(--info-wash-89);
201
+ --info-border-default: var(--info-wash-85);
202
+ --info-border-default-hover: var(--info-mark-74);
203
+ --info-border-emphasis: var(--info-lead-53);
204
+ }