okchroma 0.1.0 → 0.1.1
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-lib/index.cjs +29 -16
- package/dist-lib/index.mjs +29 -16
- package/dist-lib/types/index.d.ts +2 -1
- package/package.json +3 -2
- package/tokens/semantic.css +204 -0
package/dist-lib/index.cjs
CHANGED
|
@@ -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,
|
|
@@ -46,11 +47,14 @@ __export(index_exports, {
|
|
|
46
47
|
checkCollision: () => checkCollision,
|
|
47
48
|
checkHueCollision: () => checkHueCollision,
|
|
48
49
|
classifyArchetype: () => classifyArchetype,
|
|
50
|
+
ctaBorderRung: () => ctaBorderRung,
|
|
51
|
+
ctaNeedsBorder: () => ctaNeedsBorder,
|
|
49
52
|
emitDtcgRamp: () => emitDtcgRamp,
|
|
50
53
|
generateNeutralScale: () => generateNeutralScale,
|
|
51
54
|
generateScale: () => generateScale,
|
|
52
55
|
generateSubtleSecondary: () => generateSubtleSecondary,
|
|
53
56
|
neutralCss: () => neutralCss,
|
|
57
|
+
pageStopFor: () => pageStopFor,
|
|
54
58
|
parseToken: () => parseToken,
|
|
55
59
|
resolveBrand: () => resolveBrand,
|
|
56
60
|
resolveDtcgRamp: () => resolveDtcgRamp,
|
|
@@ -90,9 +94,11 @@ function stateStepL(restL, _mode, k) {
|
|
|
90
94
|
return k * STATE_HOVER_K;
|
|
91
95
|
}
|
|
92
96
|
var OVERRIDE_LIGHT_BELOW = ARCHETYPES.find((a) => a.name === "rich").min;
|
|
97
|
+
var OVERRIDE_DARK_ABOVE = ARCHETYPES.find((a) => a.name === "light").min;
|
|
93
98
|
function stateFillL(restL, mode, k) {
|
|
94
99
|
const natural = mode === "dark" ? 1 : -1;
|
|
95
|
-
const
|
|
100
|
+
const overridden = mode === "light" ? restL < OVERRIDE_LIGHT_BELOW : restL > OVERRIDE_DARK_ABOVE;
|
|
101
|
+
const preferred = overridden ? -natural : natural;
|
|
96
102
|
const fits = (d) => {
|
|
97
103
|
const p = restL + d * stateStepL(restL, mode, 2);
|
|
98
104
|
return p >= STATE_L_MIN && p <= STATE_L_MAX;
|
|
@@ -2619,7 +2625,7 @@ function generateNeutralScale(brandH, level = "default", contrastProfile) {
|
|
|
2619
2625
|
scale.ctaHoverDark = nState(10, "dark", scale.ctaDark, 1);
|
|
2620
2626
|
scale.ctaPressedDark = nState(11, "dark", scale.ctaDark, 2);
|
|
2621
2627
|
const onEnforce = contrastProfile !== "apca";
|
|
2622
|
-
const onFloor =
|
|
2628
|
+
const onFloor = 4.5;
|
|
2623
2629
|
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
2630
|
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
2631
|
return scale;
|
|
@@ -2650,7 +2656,7 @@ function generateSubtleSecondary(hex, opts) {
|
|
|
2650
2656
|
scale.ctaHoverDark = st(10, "dark", scale.ctaDark, 1);
|
|
2651
2657
|
scale.ctaPressedDark = st(11, "dark", scale.ctaDark, 2);
|
|
2652
2658
|
const onEnforce = opts?.contrastProfile !== "apca";
|
|
2653
|
-
const onFloor =
|
|
2659
|
+
const onFloor = 4.5;
|
|
2654
2660
|
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
2661
|
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
2662
|
return scale;
|
|
@@ -2695,7 +2701,7 @@ var COLOR_WORD = {
|
|
|
2695
2701
|
[FAMILY.info]: " (blue)"
|
|
2696
2702
|
};
|
|
2697
2703
|
var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
|
|
2698
|
-
var WASH = { req: "subtle
|
|
2704
|
+
var WASH = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
|
|
2699
2705
|
var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
|
|
2700
2706
|
var OVERLAY = {
|
|
2701
2707
|
req: "translucent backgrounds that hold their reading on any paper",
|
|
@@ -2713,12 +2719,15 @@ var SCALE = {
|
|
|
2713
2719
|
"wash-89": WASH,
|
|
2714
2720
|
"wash-85": WASH,
|
|
2715
2721
|
"wash-80": WASH,
|
|
2722
|
+
// ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
|
|
2723
|
+
// landing on the mark rows is worth its ic-ON-s noise — the one surviving body carrier)
|
|
2716
2724
|
"mark-74": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
|
|
2717
2725
|
"lead-53": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
|
|
2718
2726
|
"ink-42": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
|
|
2719
2727
|
// ("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
|
-
"
|
|
2728
|
+
// words, and a body carrying either floods that token's picker search — the C50 law;
|
|
2729
|
+
// "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
|
|
2730
|
+
"ink-30": { req: "heavy-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
|
|
2722
2731
|
// the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
|
|
2723
2732
|
// these bodies on purpose: it stopped being a token label, so it floods nothing,
|
|
2724
2733
|
// and a designer's "cta" query still lands on these rows.
|
|
@@ -2729,17 +2738,17 @@ var SCALE = {
|
|
|
2729
2738
|
// ("fill" reworded out 2026-08-18: fill became a label word and is foreign here)
|
|
2730
2739
|
"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
2740
|
};
|
|
2732
|
-
var ABS = { req: "
|
|
2733
|
-
var OFFSET = { req: "min APCA visibility", theming: "offsets
|
|
2741
|
+
var ABS = { req: "extreme poles for CTAs, aliased global endpoints" };
|
|
2742
|
+
var OFFSET = { req: "min APCA visibility", theming: "offsets CTAs in themes where they sit close to the page" };
|
|
2734
2743
|
var SHADOW = { req: "drop shadows" };
|
|
2735
2744
|
var PLANE = (req) => ({ req, theming: "aliased to the gray ramp" });
|
|
2736
2745
|
var LINK = (state, contrast) => ({
|
|
2737
2746
|
req: "links" + state,
|
|
2738
2747
|
contrast,
|
|
2739
|
-
theming: "rides the theme\u2019s
|
|
2748
|
+
theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme"
|
|
2740
2749
|
});
|
|
2741
2750
|
var LINK_INVERSE = (state, contrast) => ({
|
|
2742
|
-
req: "links
|
|
2751
|
+
req: "links over inverted backgrounds" + state,
|
|
2743
2752
|
contrast,
|
|
2744
2753
|
theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme"
|
|
2745
2754
|
});
|
|
@@ -2750,19 +2759,23 @@ var SYSTEM = {
|
|
|
2750
2759
|
// the anchor is engine-resolved and rides the neutral in BOTH plugins now —
|
|
2751
2760
|
// NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
|
|
2752
2761
|
"system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
|
|
2753
|
-
"
|
|
2762
|
+
// ("companion" reworded 2026-08-28: compani-ON fed the "on" flood)
|
|
2763
|
+
"system/abs-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given" },
|
|
2754
2764
|
"system/alpha/transparent": { req: "aliased off-states" },
|
|
2755
2765
|
// ("dimming" reworded 2026-08-18: the surface planes took dim as a label word)
|
|
2756
2766
|
"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
|
-
"
|
|
2767
|
+
// ("fills" reworded 2026-08-18: fill became a label word and is foreign here;
|
|
2768
|
+
// "on-color" reworded 2026-08-28: the bare on fed the "on" flood)
|
|
2769
|
+
"system/alpha/ink": { req: "soft text pole for quiet CTAs" },
|
|
2759
2770
|
"system/alpha/006": OFFSET,
|
|
2760
2771
|
"system/alpha/008": OFFSET,
|
|
2761
2772
|
"system/alpha/016": OFFSET,
|
|
2762
2773
|
"system/alpha/shadow-04": SHADOW,
|
|
2763
2774
|
"system/alpha/shadow-08": SHADOW,
|
|
2764
2775
|
"system/alpha/shadow-12": SHADOW,
|
|
2765
|
-
"
|
|
2776
|
+
// ("elevation" dropped 2026-08-28: elevati-ON fed the "on" flood; the sibling
|
|
2777
|
+
// planes never carried the word)
|
|
2778
|
+
"system/surface/dim": PLANE("recessed plane"),
|
|
2766
2779
|
"system/surface/low": PLANE("resting page plane"),
|
|
2767
2780
|
"system/surface/mid": PLANE("raised plane \u2014 cards, menus"),
|
|
2768
2781
|
// ("overlays" reworded 2026-08-13: paper-overlay took the word as a label — the C50 law)
|
|
@@ -3627,7 +3640,7 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
|
|
|
3627
3640
|
};
|
|
3628
3641
|
const softOnCta = (mode) => {
|
|
3629
3642
|
if (!secondary || secondaryStyle === "outline") return [];
|
|
3630
|
-
if (
|
|
3643
|
+
if (!softOnCtaPasses(secondary, mode)) return [];
|
|
3631
3644
|
const white = mode === "light" ? secondary.onFillTextIsWhite : secondary.onFillTextIsWhiteDark;
|
|
3632
3645
|
return [` --${CSS_SECONDARY}-stamp-on: rgba(${white ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});`];
|
|
3633
3646
|
};
|
|
@@ -3820,7 +3833,7 @@ function themeToFigma(r, input) {
|
|
|
3820
3833
|
});
|
|
3821
3834
|
};
|
|
3822
3835
|
softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
|
|
3823
|
-
if (input.secondary && input.secondaryStyle !== "outline" &&
|
|
3836
|
+
if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
|
|
3824
3837
|
softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
|
|
3825
3838
|
const brandGroup = rampGroup(scale[mode], mode === "light" ? scale.onFillTextIsWhite : scale.onFillTextIsWhiteDark, brandExtra(scale, mode, CSS_FAMILY.brandPrimary));
|
|
3826
3839
|
const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
|
package/dist-lib/index.mjs
CHANGED
|
@@ -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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
"
|
|
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: "
|
|
2663
|
-
var OFFSET = { req: "min APCA visibility", theming: "offsets
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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)
|
|
@@ -3557,7 +3566,7 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
|
|
|
3557
3566
|
};
|
|
3558
3567
|
const softOnCta = (mode) => {
|
|
3559
3568
|
if (!secondary || secondaryStyle === "outline") return [];
|
|
3560
|
-
if (
|
|
3569
|
+
if (!softOnCtaPasses(secondary, mode)) return [];
|
|
3561
3570
|
const white = mode === "light" ? secondary.onFillTextIsWhite : secondary.onFillTextIsWhiteDark;
|
|
3562
3571
|
return [` --${CSS_SECONDARY}-stamp-on: rgba(${white ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});`];
|
|
3563
3572
|
};
|
|
@@ -3750,7 +3759,7 @@ function themeToFigma(r, input) {
|
|
|
3750
3759
|
});
|
|
3751
3760
|
};
|
|
3752
3761
|
softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
|
|
3753
|
-
if (input.secondary && input.secondaryStyle !== "outline" &&
|
|
3762
|
+
if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
|
|
3754
3763
|
softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
|
|
3755
3764
|
const brandGroup = rampGroup(scale[mode], mode === "light" ? scale.onFillTextIsWhite : scale.onFillTextIsWhiteDark, brandExtra(scale, mode, CSS_FAMILY.brandPrimary));
|
|
3756
3765
|
const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
|
|
@@ -3853,6 +3862,7 @@ function resolveDtcgRamp(group) {
|
|
|
3853
3862
|
return resolveRamp(seedHex, mode, { stops, roles, ons });
|
|
3854
3863
|
}
|
|
3855
3864
|
export {
|
|
3865
|
+
CSS_FAMILY,
|
|
3856
3866
|
DEFAULT_LINK_HEX,
|
|
3857
3867
|
EXT_KEY,
|
|
3858
3868
|
INK_0,
|
|
@@ -3879,11 +3889,14 @@ export {
|
|
|
3879
3889
|
checkCollision,
|
|
3880
3890
|
checkHueCollision,
|
|
3881
3891
|
classifyArchetype,
|
|
3892
|
+
ctaBorderRung,
|
|
3893
|
+
ctaNeedsBorder,
|
|
3882
3894
|
emitDtcgRamp,
|
|
3883
3895
|
generateNeutralScale,
|
|
3884
3896
|
generateScale,
|
|
3885
3897
|
generateSubtleSecondary,
|
|
3886
3898
|
neutralCss,
|
|
3899
|
+
pageStopFor,
|
|
3887
3900
|
parseToken,
|
|
3888
3901
|
resolveBrand,
|
|
3889
3902
|
resolveDtcgRamp,
|
|
@@ -1,6 +1,7 @@
|
|
|
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, 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';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okchroma",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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
|
+
}
|