okchroma 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist-lib/index.cjs +230 -228
- package/dist-lib/index.mjs +230 -228
- package/dist-lib/types/engine/alphaPapers.d.ts +1 -1
- package/dist-lib/types/engine/collision.d.ts +3 -3
- package/dist-lib/types/engine/colorEngine.d.ts +3 -3
- package/dist-lib/types/engine/requirements/producers.d.ts +1 -1
- package/dist-lib/types/engine/requirements/spec.d.ts +6 -5
- package/dist-lib/types/engine/resolve.d.ts +1 -1
- package/dist-lib/types/engine/stopTable.d.ts +6 -6
- package/dist-lib/types/engine/tokenNames.d.ts +2 -2
- package/dist-lib/types/index.d.ts +1 -1
- package/package.json +3 -3
- package/tokens/semantic.css +113 -113
package/dist-lib/index.cjs
CHANGED
|
@@ -24,11 +24,11 @@ __export(index_exports, {
|
|
|
24
24
|
DEFAULT_LINK_HEX: () => DEFAULT_LINK_HEX,
|
|
25
25
|
DISABLED_OPACITY: () => DISABLED_OPACITY,
|
|
26
26
|
EXT_KEY: () => EXT_KEY,
|
|
27
|
-
INK_0: () => INK_0,
|
|
28
27
|
MODE_SPECS: () => MODE_SPECS,
|
|
29
28
|
OFFSET_ALPHAS: () => OFFSET_ALPHAS,
|
|
30
29
|
OUTLINE_HOVER_ALPHA: () => OUTLINE_HOVER_ALPHA,
|
|
31
|
-
|
|
30
|
+
PAPER_0: () => PAPER_0,
|
|
31
|
+
PEN_100: () => PEN_100,
|
|
32
32
|
RESOLVER_ID: () => RESOLVER_ID,
|
|
33
33
|
SCALE_STOP_COUNT: () => SCALE_STOP_COUNT,
|
|
34
34
|
SCRIM_ALPHA: () => SCRIM_ALPHA,
|
|
@@ -282,12 +282,12 @@ var SCALE_C_LIGHT = {
|
|
|
282
282
|
6: { base: 0.068, sat: 0.85 },
|
|
283
283
|
7: { base: 0.086, sat: 0.78 },
|
|
284
284
|
8: { base: 0.142, sat: 0.78 },
|
|
285
|
-
9: {
|
|
285
|
+
9: { textMult: 0.95, textMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
|
|
286
286
|
// ink-10 keeps the FIRST-text register (ink-9's row, C49): the retired hover law
|
|
287
287
|
// evaluated ink-9's chroma register at the between L, so the same params reproduce it;
|
|
288
288
|
// the damped strong register below belongs to the top text stop alone.
|
|
289
|
-
10: {
|
|
290
|
-
11: {
|
|
289
|
+
10: { textMult: 0.95, textMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
|
|
290
|
+
11: { textMult: 0.5, textMaxC: 0.08, chromaFloor: chromaFloorBase(11) }
|
|
291
291
|
};
|
|
292
292
|
var SCALE_C_DARK = {
|
|
293
293
|
0: { sat: 0.4 },
|
|
@@ -299,9 +299,9 @@ var SCALE_C_DARK = {
|
|
|
299
299
|
6: { sat: 0.76 },
|
|
300
300
|
7: { sat: 0.8 },
|
|
301
301
|
8: { sat: 0.84 },
|
|
302
|
-
9: {
|
|
303
|
-
10: {
|
|
304
|
-
11: {
|
|
302
|
+
9: { textMult: 0.95, textMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
|
|
303
|
+
10: { textMult: 0.95, textMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
|
|
304
|
+
11: { textMult: 0.62, textMaxC: 0.045, chromaFloor: chromaFloorBase(11) }
|
|
305
305
|
};
|
|
306
306
|
var DARK_CTA_C = {
|
|
307
307
|
brand: {
|
|
@@ -318,10 +318,10 @@ var DARK_CTA_C = {
|
|
|
318
318
|
};
|
|
319
319
|
var STOP_8_NONTEXT_CONTRAST = 3;
|
|
320
320
|
var DARK_BAND_TOP_LIFT = 1.19;
|
|
321
|
-
var
|
|
322
|
-
var
|
|
323
|
-
var
|
|
324
|
-
var
|
|
321
|
+
var PENCIL_9_CONTRAST = 4.5;
|
|
322
|
+
var PEN_10_CONTRAST = 4.5;
|
|
323
|
+
var PEN_11_CONTRAST_FLOOR = 7;
|
|
324
|
+
var PEN_70_GROUND = {
|
|
325
325
|
wcag: {
|
|
326
326
|
light: { L: 0.3334966864733433, C: 0.07399966383476321, H: 104.26776675496032 },
|
|
327
327
|
dark: { L: 0.907688606079988, C: 0.045, H: 293.66431934911327 }
|
|
@@ -397,7 +397,7 @@ var NEUTRAL_TINT_POINTS = {
|
|
|
397
397
|
[0.95, 0.895],
|
|
398
398
|
// paper-3 ─┘
|
|
399
399
|
[0.924, 1],
|
|
400
|
-
// wash-4 ─┐ peak: the mid-
|
|
400
|
+
// wash-4 ─┐ peak: the mid-highlighter band, the neutral's most-branded moment
|
|
401
401
|
[0.892, 1],
|
|
402
402
|
// wash-5 ─┘
|
|
403
403
|
[0.852, 0.842],
|
|
@@ -405,7 +405,7 @@ var NEUTRAL_TINT_POINTS = {
|
|
|
405
405
|
[0.801, 0.653],
|
|
406
406
|
// wash-7 │
|
|
407
407
|
[0.738, 0.505],
|
|
408
|
-
//
|
|
408
|
+
// crayon-26
|
|
409
409
|
[0.6, 0.4],
|
|
410
410
|
// knee (ex-scaffold) the drop-off: hue fades as you climb
|
|
411
411
|
[0.56, 0.347],
|
|
@@ -431,7 +431,7 @@ var NEUTRAL_TINT_POINTS = {
|
|
|
431
431
|
[0.42, 0.653],
|
|
432
432
|
// wash-7
|
|
433
433
|
[0.55, 0.505],
|
|
434
|
-
//
|
|
434
|
+
// crayon-26
|
|
435
435
|
[0.6, 0.4],
|
|
436
436
|
// knee (ex-scaffold)
|
|
437
437
|
[0.64, 0.347],
|
|
@@ -800,26 +800,136 @@ function perceptualDarkC(L, H2, nativeC, keep = KEEP_DARK, gamut = MASTER_GAMUT)
|
|
|
800
800
|
return solveCForApparent(L, H2, grayApparentL(L, gamut) + keep * meanBoost(L, nativeC, gamut), gamut);
|
|
801
801
|
}
|
|
802
802
|
|
|
803
|
+
// src/engine/tokenNames.ts
|
|
804
|
+
var SHARED_NAMES = {
|
|
805
|
+
1: "paper-1",
|
|
806
|
+
2: "paper-3",
|
|
807
|
+
// stop 3 renamed wash-3 → paper-3 (owner 2026-07-24, elevation round): it is a
|
|
808
|
+
// PLANE in both themes (light sink / dark pop), so it belongs to the surface band.
|
|
809
|
+
// NAME ONLY — generation is index-keyed and unchanged; the highlighter-collision machinery
|
|
810
|
+
// (collision.ts HIGHLIGHTER_STOPS) deliberately still spans stops 3–7. (The requirement
|
|
811
|
+
// spec's `group` label followed the band to 'paper' on 2026-08-31 — labels only,
|
|
812
|
+
// the spans never moved; see spec.ts groupOf.)
|
|
813
|
+
// Stage B (owner 2026-08-07, names only): relabeled to the ROOT_L-derived digit,
|
|
814
|
+
// paper-5 — same band, same index.
|
|
815
|
+
3: "paper-5",
|
|
816
|
+
4: "highlighter-8",
|
|
817
|
+
5: "highlighter-11",
|
|
818
|
+
6: "highlighter-15",
|
|
819
|
+
7: "highlighter-20",
|
|
820
|
+
8: "crayon-26",
|
|
821
|
+
// THE PEN BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
|
|
822
|
+
// 2026-07-29 highlight collapse — the old highlight-9 and on-highlight are
|
|
823
|
+
// deleted; this stop carries both jobs, its on-color a paper token), the between
|
|
824
|
+
// text stop (a value the retired text-cta hover state used to generate bespokely,
|
|
825
|
+
// promoted to a normal stop), and the strong text stop. The three read as states
|
|
826
|
+
// ARE the text-style cta (owner 2026-08-12). C49 restored the top
|
|
827
|
+
// two pre-collapse stop INDICES (11, and the off-scale anchor's 12 — emitted as a
|
|
828
|
+
// literal in cssRender, not through this table) with the between stop taking the
|
|
829
|
+
// vacated index 10; the indices (9/10/11) are unchanged since.
|
|
830
|
+
9: "pencil-47",
|
|
831
|
+
10: "pen-58",
|
|
832
|
+
11: "pen-70"
|
|
833
|
+
};
|
|
834
|
+
var SCALE_STOP_COUNT = Object.keys(SHARED_NAMES).length;
|
|
835
|
+
function stopTokenName(stop) {
|
|
836
|
+
const name = SHARED_NAMES[stop];
|
|
837
|
+
if (!name) throw new Error(`stopTokenName: unexpected stop ${stop}`);
|
|
838
|
+
return name;
|
|
839
|
+
}
|
|
840
|
+
var PAPER_0 = "paper-0";
|
|
841
|
+
var PEN_100 = "pen-100";
|
|
842
|
+
var STAMP_FILL = "stamp-fill";
|
|
843
|
+
var STAMP_FILL_HOVER = "stamp-fill-hover";
|
|
844
|
+
var STAMP_FILL_PRESSED = "stamp-fill-pressed";
|
|
845
|
+
var STAMP_EDGE = "stamp-edge";
|
|
846
|
+
var STAMP_ON = "stamp-on";
|
|
847
|
+
var STAMP_STATE_LEAVES = {
|
|
848
|
+
[STAMP_FILL]: "stamp/fill",
|
|
849
|
+
[STAMP_FILL_HOVER]: "stamp/fill-hover",
|
|
850
|
+
[STAMP_FILL_PRESSED]: "stamp/fill-pressed",
|
|
851
|
+
[STAMP_EDGE]: "stamp/edge",
|
|
852
|
+
[STAMP_ON]: "stamp/on"
|
|
853
|
+
};
|
|
854
|
+
var STAMP_LEAF = {
|
|
855
|
+
FILL: STAMP_STATE_LEAVES[STAMP_FILL],
|
|
856
|
+
FILL_HOVER: STAMP_STATE_LEAVES[STAMP_FILL_HOVER],
|
|
857
|
+
FILL_PRESSED: STAMP_STATE_LEAVES[STAMP_FILL_PRESSED],
|
|
858
|
+
EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
|
|
859
|
+
ON: STAMP_STATE_LEAVES[STAMP_ON]
|
|
860
|
+
};
|
|
861
|
+
var SYSTEM_LEAF = {
|
|
862
|
+
ABS_BLACK: "system/abs-black",
|
|
863
|
+
ABS_WHITE: "system/abs-white",
|
|
864
|
+
SURFACE: {
|
|
865
|
+
DIM: "system/surface/dim",
|
|
866
|
+
LOW: "system/surface/low",
|
|
867
|
+
MID: "system/surface/mid",
|
|
868
|
+
HIGH: "system/surface/high"
|
|
869
|
+
},
|
|
870
|
+
ALPHA: {
|
|
871
|
+
TRANSPARENT: "system/alpha/transparent",
|
|
872
|
+
SCRIM: "system/alpha/abs-black-060",
|
|
873
|
+
INK: "system/alpha/ink",
|
|
874
|
+
// the DIRECTIONAL ladders (owner 2026-08-31, renaming the 2026-08-18 bare rungs
|
|
875
|
+
// and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
|
|
876
|
+
// (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
|
|
877
|
+
// INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
|
|
878
|
+
// a "highlighter": that word belongs to the tinted band stops.) The reversal lives in
|
|
879
|
+
// the engine: a consumer on an inverted ground names one token and never picks a
|
|
880
|
+
// pole. Colored fills never ride these; their states stay engine-solved stops.
|
|
881
|
+
// Rungs are two-digit percent, harmonizing with shadow-04/08/12.
|
|
882
|
+
AWAY_FROM_BG_06: "system/alpha/away-from-bg/06",
|
|
883
|
+
AWAY_FROM_BG_08: "system/alpha/away-from-bg/08",
|
|
884
|
+
AWAY_FROM_BG_16: "system/alpha/away-from-bg/16",
|
|
885
|
+
TOWARD_BG_06: "system/alpha/toward-bg/06",
|
|
886
|
+
TOWARD_BG_08: "system/alpha/toward-bg/08",
|
|
887
|
+
TOWARD_BG_16: "system/alpha/toward-bg/16",
|
|
888
|
+
SHADOW_04: "system/alpha/shadow-04",
|
|
889
|
+
SHADOW_08: "system/alpha/shadow-08",
|
|
890
|
+
SHADOW_12: "system/alpha/shadow-12"
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
var SURFACE_PLANE_LAW = {
|
|
894
|
+
[SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_0 },
|
|
895
|
+
[SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
|
|
896
|
+
[SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
|
|
897
|
+
[SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_0, dark: SHARED_NAMES[3] }
|
|
898
|
+
};
|
|
899
|
+
var TOKEN_ORDER = [
|
|
900
|
+
...Object.values(SHARED_NAMES),
|
|
901
|
+
STAMP_FILL,
|
|
902
|
+
STAMP_FILL_HOVER,
|
|
903
|
+
STAMP_FILL_PRESSED,
|
|
904
|
+
STAMP_ON,
|
|
905
|
+
"identity"
|
|
906
|
+
];
|
|
907
|
+
function tokenOrder(name) {
|
|
908
|
+
const i = TOKEN_ORDER.indexOf(name);
|
|
909
|
+
return i === -1 ? TOKEN_ORDER.length : i;
|
|
910
|
+
}
|
|
911
|
+
|
|
803
912
|
// src/engine/requirements/spec.ts
|
|
804
|
-
var
|
|
913
|
+
var textLane = (g2) => g2 === "pencil" || g2 === "pen" || g2 === "lead" || g2 === "ink";
|
|
914
|
+
var groupOf = (stop) => (stop === 0 ? PAPER_0 : stopTokenName(stop)).split("-")[0];
|
|
805
915
|
var PAPER0_DARK_ROOT_L = 0.16;
|
|
806
916
|
var PL_LADDER = { hue: "warm-drift", L: "perceptual", chroma: "ladder" };
|
|
807
917
|
var PL_TEXT = { hue: "warm-drift", L: "perceptual", chroma: "brand" };
|
|
808
918
|
var P_LIFT = { hue: "warm-torsion", L: "perceptual-lift", chroma: "ladder" };
|
|
809
919
|
var P_FIXED = { hue: "warm-torsion", L: "fixed", chroma: "ladder" };
|
|
810
920
|
var P_TEXT = { hue: "warm-torsion", L: "perceptual", chroma: "brand" };
|
|
811
|
-
var S8 = { metric: "wcag", against: "paper-
|
|
812
|
-
var T9 = { metric: "wcag", against: "paper-
|
|
813
|
-
var T10 = { metric: "wcag", against: "
|
|
814
|
-
var T11 = { metric: "wcag", against: "paper-
|
|
921
|
+
var S8 = { metric: "wcag", against: "paper-5", target: STOP_8_NONTEXT_CONTRAST, level: "AA" };
|
|
922
|
+
var T9 = { metric: "wcag", against: "paper-3", target: PENCIL_9_CONTRAST, level: "AA" };
|
|
923
|
+
var T10 = { metric: "wcag", against: "highlighter-20", target: PEN_10_CONTRAST, level: "AA" };
|
|
924
|
+
var T11 = { metric: "wcag", against: "paper-3", target: PEN_11_CONTRAST_FLOOR, level: "AAA" };
|
|
815
925
|
var ONS = { onFill: { metric: "apca-pole", enforce: true, ratioFloor: 4.5, coEnforceLc: 65 } };
|
|
816
926
|
var LIGHT = {
|
|
817
927
|
stops: [
|
|
818
|
-
// paper-
|
|
819
|
-
{ stop: 0, rootL: 1, group:
|
|
820
|
-
// paper/
|
|
928
|
+
// paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
|
|
929
|
+
{ stop: 0, rootL: 1, group: groupOf(0), produce: { hue: "warm-drift", L: "fixed", chroma: "ladder" }, satFraction: SCALE_C_LIGHT[0].sat, baseC: SCALE_C_LIGHT[0].base },
|
|
930
|
+
// paper/highlighter/crayon-26: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
|
|
821
931
|
// falls out of the shape (see above) — no min-separation require. Only stop 8 carries a require: the WCAG
|
|
822
|
-
// 3:1 vs the resolved paper-
|
|
932
|
+
// 3:1 vs the resolved paper-3 (paper-2) (re-solves automatically since it references paper-3 (paper-2)).
|
|
823
933
|
...[1, 2, 3, 4, 5, 6, 7, 8].map((stop) => ({
|
|
824
934
|
stop,
|
|
825
935
|
rootL: ROOT_L_LIGHT[stop],
|
|
@@ -829,12 +939,12 @@ var LIGHT = {
|
|
|
829
939
|
baseC: SCALE_C_LIGHT[stop].base,
|
|
830
940
|
require: stop === 8 ? S8 : void 0
|
|
831
941
|
})),
|
|
832
|
-
//
|
|
833
|
-
// highlight-9 collapse, owner 2026-07-29).
|
|
942
|
+
// pen text: perceptual + contrast-required. pencil-47 (ink-9) is ALSO the emphasis fill (the
|
|
943
|
+
// highlight-9 collapse, owner 2026-07-29). pen-58 (ink-10) is the between text stop (C49) —
|
|
834
944
|
// a normal stop like its neighbors; the three together are the text-register cta.
|
|
835
|
-
{ stop: 9, rootL: ROOT_L_LIGHT[9], group:
|
|
836
|
-
{ stop: 10, rootL: ROOT_L_LIGHT[10], group:
|
|
837
|
-
{ stop: 11, rootL: ROOT_L_LIGHT[11], group:
|
|
945
|
+
{ stop: 9, rootL: ROOT_L_LIGHT[9], group: groupOf(9), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[9].textMult, textMaxC: SCALE_C_LIGHT[9].textMaxC, chromaFloor: SCALE_C_LIGHT[9].chromaFloor, require: T9 },
|
|
946
|
+
{ stop: 10, rootL: ROOT_L_LIGHT[10], group: groupOf(10), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[10].textMult, textMaxC: SCALE_C_LIGHT[10].textMaxC, chromaFloor: SCALE_C_LIGHT[10].chromaFloor, require: T10 },
|
|
947
|
+
{ stop: 11, rootL: ROOT_L_LIGHT[11], group: groupOf(11), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[11].textMult, textMaxC: SCALE_C_LIGHT[11].textMaxC, chromaFloor: SCALE_C_LIGHT[11].chromaFloor, require: T11 }
|
|
838
948
|
],
|
|
839
949
|
roles: [
|
|
840
950
|
{ role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: 0, chromaMult: 1 },
|
|
@@ -845,10 +955,10 @@ var LIGHT = {
|
|
|
845
955
|
};
|
|
846
956
|
var DARK = {
|
|
847
957
|
stops: [
|
|
848
|
-
// paper-
|
|
958
|
+
// paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — one seam BELOW paper-1 (paper-1), deep and brand-tinted, never the
|
|
849
959
|
// absolute void (the old hard-coded #000000 was "too much"). Lift applies like the rest of the scale.
|
|
850
|
-
{ stop: 0, rootL: PAPER0_DARK_ROOT_L, group:
|
|
851
|
-
// paper/
|
|
960
|
+
{ stop: 0, rootL: PAPER0_DARK_ROOT_L, group: groupOf(0), produce: P_LIFT, satFraction: SCALE_C_DARK[0].sat },
|
|
961
|
+
// paper/highlighter 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
|
|
852
962
|
// 3:1 non-text require DECLARED (the Stage-5 flip, owner-approved) — the blue-recede failure is prevented
|
|
853
963
|
// BY RULE, not by patch. The require now genuinely PLACES the stop rather than catching a few low-luminance
|
|
854
964
|
// hues: on the delta-carry path it solves from the sentinel every time, and the old claim that "most hues
|
|
@@ -862,11 +972,11 @@ var DARK = {
|
|
|
862
972
|
satFraction: SCALE_C_DARK[stop].sat,
|
|
863
973
|
require: stop === 8 ? S8 : void 0
|
|
864
974
|
})),
|
|
865
|
-
//
|
|
975
|
+
// pen text: perceptual + the contrast requires DECLARED in dark too (Stage-5 flip): the scaffold already
|
|
866
976
|
// clears them for every hue (the gate proves it), so values don't move — but the guarantee is now a rule.
|
|
867
|
-
{ stop: 9, rootL: ROOT_L_DARK[9], group:
|
|
868
|
-
{ stop: 10, rootL: ROOT_L_DARK[10], group:
|
|
869
|
-
{ stop: 11, rootL: ROOT_L_DARK[11], group:
|
|
977
|
+
{ stop: 9, rootL: ROOT_L_DARK[9], group: groupOf(9), produce: P_TEXT, chromaMult: SCALE_C_DARK[9].textMult, textMaxC: SCALE_C_DARK[9].textMaxC, chromaFloor: SCALE_C_DARK[9].chromaFloor, require: T9 },
|
|
978
|
+
{ stop: 10, rootL: ROOT_L_DARK[10], group: groupOf(10), produce: P_TEXT, chromaMult: SCALE_C_DARK[10].textMult, textMaxC: SCALE_C_DARK[10].textMaxC, chromaFloor: SCALE_C_DARK[10].chromaFloor, require: T10 },
|
|
979
|
+
{ stop: 11, rootL: ROOT_L_DARK[11], group: groupOf(11), produce: P_TEXT, chromaMult: SCALE_C_DARK[11].textMult, textMaxC: SCALE_C_DARK[11].textMaxC, chromaFloor: SCALE_C_DARK[11].chromaFloor, require: T11 }
|
|
870
980
|
],
|
|
871
981
|
roles: [
|
|
872
982
|
{ role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: DARK_CTA_MIN_L, chromaMult: 1 },
|
|
@@ -877,7 +987,7 @@ var DARK = {
|
|
|
877
987
|
};
|
|
878
988
|
var MODE_SPECS = { light: LIGHT, dark: DARK };
|
|
879
989
|
|
|
880
|
-
//
|
|
990
|
+
// node_modules/helmlab/dist/index.js
|
|
881
991
|
function o0(n) {
|
|
882
992
|
let t = new Float64Array(9);
|
|
883
993
|
for (let e = 0; e < 3; e++) for (let s = 0; s < 3; s++) t[e * 3 + s] = n[e][s];
|
|
@@ -1793,7 +1903,7 @@ function p2Diff(a, b) {
|
|
|
1793
1903
|
var DEFAULT_APCA_LC_MAP = { 3: 30, 4.5: 75, 6.5: 85, 7: 90 };
|
|
1794
1904
|
function toApca(req, lcMap) {
|
|
1795
1905
|
if (req.metric !== "wcag") return req;
|
|
1796
|
-
if (req.against === "
|
|
1906
|
+
if (req.against === "highlighter-20") return { metric: "apca", against: "paper-3", targetLc: 85 };
|
|
1797
1907
|
const targetLc = lcMap[req.target];
|
|
1798
1908
|
if (targetLc === void 0) throw new Error(`apca profile: no Lc mapping for wcag target ${req.target}`);
|
|
1799
1909
|
return { metric: "apca", against: req.against, targetLc };
|
|
@@ -1807,7 +1917,7 @@ function withProfile(spec, profile, lcMap = DEFAULT_APCA_LC_MAP) {
|
|
|
1807
1917
|
// (The stop-9 CARVE-OUT is gone with the stop, owner 2026-07-29. It existed because
|
|
1808
1918
|
// highlight-9's require was a fill-vs-its-own-plane bar with no honest slot in a map
|
|
1809
1919
|
// of TEXT bars — 3 → 30 non-text, 4.5 → 75 body, 7 → 90 — so the apca lane kept its
|
|
1810
|
-
// own hl-9 placement instead. Today's stop 9 is
|
|
1920
|
+
// own hl-9 placement instead. Today's stop 9 is a PEN stop: 4.5 is a text bar, the
|
|
1811
1921
|
// 75 slot is its correct translation, and it maps like every other require.)
|
|
1812
1922
|
stops: spec.stops.map((s) => s.require ? { ...s, require: toApca(s.require, lcMap) } : s),
|
|
1813
1923
|
ons: {
|
|
@@ -1969,16 +2079,16 @@ function separationClampLight(ctx, placed, chromaAt, ref, target) {
|
|
|
1969
2079
|
return { L: pass, C: ceilingAt(pass), H: ctx.lightHueAt(pass) };
|
|
1970
2080
|
}
|
|
1971
2081
|
function placeLightText(ctx, rootL, cMult, maxLFor, deepen, maxC = Infinity) {
|
|
1972
|
-
const
|
|
1973
|
-
const anchorL = perceptualRungL(rootL, clampChromaToGamut(rootL,
|
|
2082
|
+
const textC = Math.min(cMult * ctx.brandC, maxC);
|
|
2083
|
+
const anchorL = perceptualRungL(rootL, clampChromaToGamut(rootL, textC, ctx.lightHueAt(rootL)), ctx.lightHueAt(rootL));
|
|
1974
2084
|
let H2 = ctx.lightHueAt(anchorL);
|
|
1975
|
-
let C2 = clampChromaToGamut(anchorL,
|
|
2085
|
+
let C2 = clampChromaToGamut(anchorL, textC, H2);
|
|
1976
2086
|
let L = Math.min(anchorL, maxLFor(C2, H2));
|
|
1977
2087
|
H2 = ctx.lightHueAt(L);
|
|
1978
|
-
C2 = clampChromaToGamut(L,
|
|
2088
|
+
C2 = clampChromaToGamut(L, textC, H2);
|
|
1979
2089
|
L = Math.min(anchorL, maxLFor(C2, H2));
|
|
1980
2090
|
L = Math.min(L - deepen, maxLFor(C2, ctx.lightHueAt(L - deepen)));
|
|
1981
|
-
return { L, C: ctx.cAt("light", L,
|
|
2091
|
+
return { L, C: ctx.cAt("light", L, textC), H: ctx.lightHueAt(L) };
|
|
1982
2092
|
}
|
|
1983
2093
|
function onFillIsWhiteLight(ctx, enforce, ratioFloor) {
|
|
1984
2094
|
const fill9ApcaY = apcaYAt(ctx.scaleL, clampChromaToGamut(ctx.scaleL, ctx.cAt("light", ctx.scaleL, ctx.brandC), ctx.brandH), ctx.brandH);
|
|
@@ -1992,7 +2102,7 @@ function buildDarkContext(ctx, specFloorL = DARK_CTA_MIN_L) {
|
|
|
1992
2102
|
return { dark9L, darkC9, darkHueAtL };
|
|
1993
2103
|
}
|
|
1994
2104
|
var darkScaleChromaAt = (ctx, dctx, stopIndex, multiplier) => (L) => ctx.cAt("dark", L, ctx.opts?.darkChromaCurve ? ctx.opts.darkChromaCurve(L, dctx.darkHueAtL(L), ctx.brandC, dctx.darkC9) : applyChromaFloor(ctx.subtleC, multiplier, chromaFloorBase(stopIndex), ctx.darkFloorStrength));
|
|
1995
|
-
var
|
|
2105
|
+
var darkTextChromaAt = (ctx, dctx, floorBase, multiplier, maxC = Infinity) => (L) => ctx.cAt("dark", L, Math.min(applyChromaFloor(ctx.brandC, multiplier, floorBase, ctx.darkFloorStrength), maxC));
|
|
1996
2106
|
function placeDark(dctx, rootL, chromaAt, lift = false) {
|
|
1997
2107
|
let L = perceptualRungL(rootL, chromaAt(rootL), dctx.darkHueAtL(rootL));
|
|
1998
2108
|
if (lift) L = Math.max(L, rootL);
|
|
@@ -2253,7 +2363,7 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2253
2363
|
const seed = { hex, ...o };
|
|
2254
2364
|
const ctx = buildContext(hex, opts);
|
|
2255
2365
|
spec ?? (spec = MODE_SPECS[mode]);
|
|
2256
|
-
if (ctx.opts?.
|
|
2366
|
+
if (ctx.opts?.textGround) {
|
|
2257
2367
|
spec = {
|
|
2258
2368
|
...spec,
|
|
2259
2369
|
stops: spec.stops.map((s) => s.stop === 10 && s.require?.metric === "wcag" ? { ...s, require: { ...s.require, target: 6.5 } } : s)
|
|
@@ -2275,12 +2385,12 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2275
2385
|
if (!ref) throw new Error(`stop ${forWhom}: require against stop ${stopNum} but it is not resolved yet`);
|
|
2276
2386
|
return ref;
|
|
2277
2387
|
};
|
|
2278
|
-
const AGAINST_STOP = { "paper-
|
|
2388
|
+
const AGAINST_STOP = { "paper-1": 1, "paper-3": 2, "paper-5": 3, "highlighter-20": 7 };
|
|
2279
2389
|
const declaredAnchor = (req) => req.metric === "min-separation" ? 1 : AGAINST_STOP[req.against] ?? 2;
|
|
2280
2390
|
const wcagAnchorStop = (req, stop) => stop >= 9 && declaredAnchor(req) <= 3 ? 3 : declaredAnchor(req);
|
|
2281
|
-
const
|
|
2282
|
-
const groundOf = (req, stop) =>
|
|
2283
|
-
const apcaGroundOf = (req, stop) =>
|
|
2391
|
+
const textGround = ctx.opts?.textGround?.[mode];
|
|
2392
|
+
const groundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(wcagAnchorStop(req, stop), stop);
|
|
2393
|
+
const apcaGroundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(declaredAnchor(req), stop);
|
|
2284
2394
|
const NEUTRAL_P3_WORST_SHIP_Y = { light: 0.845015, dark: 0.014247 };
|
|
2285
2395
|
const NEUTRAL_W80_WORST_SHIP_Y = { light: 0.506433, dark: 0.074262 };
|
|
2286
2396
|
const maxLForOf = (req, forWhom, withMargin) => {
|
|
@@ -2298,14 +2408,14 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2298
2408
|
}
|
|
2299
2409
|
throw new Error(`stop ${forWhom}: ${req.metric} is not a contrast require`);
|
|
2300
2410
|
};
|
|
2301
|
-
const
|
|
2411
|
+
const TEXT_DEEPEN = 0;
|
|
2302
2412
|
for (const sp of spec.stops) {
|
|
2303
2413
|
let placed;
|
|
2304
2414
|
let clamped = false, unresolvable;
|
|
2305
2415
|
if (mode === "light") {
|
|
2306
|
-
if (sp.group
|
|
2307
|
-
if (!sp.require) throw new Error(`light
|
|
2308
|
-
placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false),
|
|
2416
|
+
if (textLane(sp.group)) {
|
|
2417
|
+
if (!sp.require) throw new Error(`light pen stop ${sp.stop} must declare a contrast require`);
|
|
2418
|
+
placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), TEXT_DEEPEN, sp.textMaxC);
|
|
2309
2419
|
clamped = true;
|
|
2310
2420
|
} else if (sp.produce.L === "fixed") {
|
|
2311
2421
|
const chromaAt = lightScaleChromaAt(ctx, sp.baseC ?? 0, sp.satFraction ?? 1);
|
|
@@ -2317,7 +2427,7 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2317
2427
|
placed = placeLightScale(ctx, sp.rootL, chromaAt, contrastReq ? maxLForOf(contrastReq, sp.stop, true) : void 0, targetChromaAt);
|
|
2318
2428
|
clamped = !!contrastReq;
|
|
2319
2429
|
if (sp.require?.metric === "min-separation") {
|
|
2320
|
-
const refStop = sp.require.against === "paper-
|
|
2430
|
+
const refStop = sp.require.against === "paper-1" ? 1 : sp.stop - 1;
|
|
2321
2431
|
const ref = stops.find((s) => s.stop === refStop);
|
|
2322
2432
|
if (!ref) throw new Error(`stop ${sp.stop}: min-separation against stop ${refStop} but it is not resolved yet`);
|
|
2323
2433
|
const before = placed.L;
|
|
@@ -2327,8 +2437,8 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2327
2437
|
}
|
|
2328
2438
|
} else {
|
|
2329
2439
|
const d = dctx;
|
|
2330
|
-
const
|
|
2331
|
-
const chromaAt = sp.group
|
|
2440
|
+
const textTwin = textLane(sp.group) && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
|
|
2441
|
+
const chromaAt = textLane(sp.group) ? textTwin ? ((_L) => textTwin.C) : darkTextChromaAt(ctx, d, sp.chromaFloor ?? chromaFloorBase(sp.stop), sp.chromaMult ?? 1, sp.textMaxC) : darkScaleChromaAt(ctx, d, Math.max(0, sp.stop - 1), sp.satFraction ?? 1);
|
|
2332
2442
|
let spineH = null;
|
|
2333
2443
|
const dl = ctx.opts?.deltaLightStops;
|
|
2334
2444
|
const ls = dl && sp.stop >= 1 && sp.stop <= 8 ? dl.find((s) => s.stop === sp.stop) : void 0;
|
|
@@ -2348,7 +2458,7 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2348
2458
|
}
|
|
2349
2459
|
if (ctx.opts.deltaHKPlace) L = perceptualRungL(sp.rootL, ls.C, ls.H);
|
|
2350
2460
|
if (ctx.opts.deltaLiftFloor) L = Math.max(L, sp.rootL);
|
|
2351
|
-
if (ctx.opts.deltaChromaEq && sp.group
|
|
2461
|
+
if (ctx.opts.deltaChromaEq && !textLane(sp.group)) C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
|
|
2352
2462
|
placed = { L, C: C2, H: spineH ?? ls.H };
|
|
2353
2463
|
} else if (sp.produce.L === "fixed") {
|
|
2354
2464
|
placed = ls ? placeDarkDelta(d, sp.rootL, chromaAt, ls) : { L: sp.rootL, C: chromaAt(sp.rootL), H: d.darkHueAtL(sp.rootL) };
|
|
@@ -2384,7 +2494,7 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2384
2494
|
if (sp.require?.metric === "wcag") {
|
|
2385
2495
|
const anchor = groundOf(sp.require, sp.stop);
|
|
2386
2496
|
const anchorShipY = shippedY(anchor.L, anchor.C, anchor.H);
|
|
2387
|
-
const paperBound = sp.stop >= 8 && !(
|
|
2497
|
+
const paperBound = sp.stop >= 8 && !(textGround && sp.stop >= 9);
|
|
2388
2498
|
const crossBoundY = wcagAnchorStop(sp.require, sp.stop) === 7 ? NEUTRAL_W80_WORST_SHIP_Y[mode] : NEUTRAL_P3_WORST_SHIP_Y[mode];
|
|
2389
2499
|
const shipRatio = (L) => {
|
|
2390
2500
|
const y2 = shippedY(L, placed.C, placed.H);
|
|
@@ -2412,7 +2522,7 @@ function resolveRamp(hex, mode, spec, opts) {
|
|
|
2412
2522
|
const got = Math.abs(apcaLc(apcaYAt(placed.L, clampChromaToGamut(placed.L, placed.C, placed.H), placed.H), refA));
|
|
2413
2523
|
if (got < sp.require.targetLc - APCA_TOL_LC) unresolvable = `stop ${sp.stop}: |Lc| ${got.toFixed(1)} < required ${sp.require.targetLc}`;
|
|
2414
2524
|
} else if (sp.require?.metric === "min-separation") {
|
|
2415
|
-
const ref = stops.find((s) => s.stop === (sp.require.against === "paper-
|
|
2525
|
+
const ref = stops.find((s) => s.stop === (sp.require.against === "paper-1" ? 1 : sp.stop - 1));
|
|
2416
2526
|
const rad2 = (h) => h * Math.PI / 180;
|
|
2417
2527
|
const gC = clampChromaToGamut(placed.L, placed.C, placed.H);
|
|
2418
2528
|
const got = Math.sqrt((placed.L - ref.L) ** 2 + (gC * Math.cos(rad2(placed.H)) - ref.C * Math.cos(rad2(ref.H))) ** 2 + (gC * Math.sin(rad2(placed.H)) - ref.C * Math.sin(rad2(ref.H))) ** 2);
|
|
@@ -2568,7 +2678,7 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
|
|
|
2568
2678
|
const dark = darkRamp.stops.filter((s) => s.stop >= 1).map(toStop).sort((a, b) => a.stop - b.stop);
|
|
2569
2679
|
const p0Light = lightRamp.stops.find((s) => s.stop === 0);
|
|
2570
2680
|
const p0Dark = darkRamp.stops.find((s) => s.stop === 0);
|
|
2571
|
-
const
|
|
2681
|
+
const penPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
|
|
2572
2682
|
const cta = makeStop(9, lightRamp.roles.cta.L, lightRamp.roles.cta.C, lightRamp.roles.cta.H);
|
|
2573
2683
|
const ctaDark = makeStop(9, darkRamp.roles.cta.L, darkRamp.roles.cta.C, darkRamp.roles.cta.H);
|
|
2574
2684
|
const roleStop = (r, stop) => makeStop(stop, r.L, r.C, r.H);
|
|
@@ -2592,8 +2702,8 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
|
|
|
2592
2702
|
identityHex: hex.toUpperCase(),
|
|
2593
2703
|
paper0: p0Light ? makeStop(0, p0Light.L, p0Light.C, p0Light.H) : void 0,
|
|
2594
2704
|
paper0Dark: p0Dark ? makeStop(0, p0Dark.L, p0Dark.C, p0Dark.H) : void 0,
|
|
2595
|
-
|
|
2596
|
-
|
|
2705
|
+
pen100: penPole(light, 0),
|
|
2706
|
+
pen100Dark: penPole(dark, 1)
|
|
2597
2707
|
};
|
|
2598
2708
|
}
|
|
2599
2709
|
function generateNeutralScale(brandH, level = "default", contrastProfile) {
|
|
@@ -2665,8 +2775,7 @@ function generateSubtleSecondary(hex, opts) {
|
|
|
2665
2775
|
|
|
2666
2776
|
// src/engine/tokenDescriptions.ts
|
|
2667
2777
|
var AA_LARGE = "AA large text and UI elements";
|
|
2668
|
-
var AA_BODY = "AA standard body text
|
|
2669
|
-
var AAA_BODY = "AAA standard body text";
|
|
2778
|
+
var AA_BODY = "AA standard body text";
|
|
2670
2779
|
var FAMILY = {
|
|
2671
2780
|
neutral: "neutral",
|
|
2672
2781
|
brandPrimary: "brand",
|
|
@@ -2702,7 +2811,7 @@ var COLOR_WORD = {
|
|
|
2702
2811
|
[FAMILY.info]: " (blue)"
|
|
2703
2812
|
};
|
|
2704
2813
|
var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
|
|
2705
|
-
var
|
|
2814
|
+
var HIGHLIGHTER = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
|
|
2706
2815
|
var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
|
|
2707
2816
|
var OVERLAY = {
|
|
2708
2817
|
req: "translucent backgrounds that hold their reading on any paper",
|
|
@@ -2710,25 +2819,25 @@ var OVERLAY = {
|
|
|
2710
2819
|
collides: true
|
|
2711
2820
|
};
|
|
2712
2821
|
var SCALE = {
|
|
2713
|
-
"paper-
|
|
2822
|
+
"paper-1": PAPER,
|
|
2714
2823
|
"paper-99-overlay": OVERLAY,
|
|
2715
|
-
"paper-
|
|
2824
|
+
"paper-3": PAPER,
|
|
2716
2825
|
"paper-97-overlay": OVERLAY,
|
|
2717
|
-
"paper-
|
|
2826
|
+
"paper-5": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
|
|
2718
2827
|
"paper-95-overlay": OVERLAY,
|
|
2719
|
-
"
|
|
2720
|
-
"
|
|
2721
|
-
"
|
|
2722
|
-
"
|
|
2828
|
+
"highlighter-8": HIGHLIGHTER,
|
|
2829
|
+
"highlighter-11": HIGHLIGHTER,
|
|
2830
|
+
"highlighter-15": HIGHLIGHTER,
|
|
2831
|
+
"highlighter-20": HIGHLIGHTER,
|
|
2723
2832
|
// ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
|
|
2724
|
-
// landing on the
|
|
2725
|
-
"
|
|
2726
|
-
"
|
|
2727
|
-
"
|
|
2833
|
+
// landing on the crayon rows is worth its ic-ON-s noise — the one surviving body carrier)
|
|
2834
|
+
"crayon-26": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
|
|
2835
|
+
"pencil-47": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
|
|
2836
|
+
"pen-58": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
|
|
2728
2837
|
// ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
|
|
2729
2838
|
// words, and a body carrying either floods that token's picker search — the C50 law;
|
|
2730
2839
|
// "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
|
|
2731
|
-
"
|
|
2840
|
+
"pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
|
|
2732
2841
|
// the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
|
|
2733
2842
|
// these bodies on purpose: it stopped being a token label, so it floods nothing,
|
|
2734
2843
|
// and a designer's "cta" query still lands on these rows.
|
|
@@ -2757,7 +2866,7 @@ var LINK_INVERSE = (state, contrast) => ({
|
|
|
2757
2866
|
var SYSTEM = {
|
|
2758
2867
|
"system/abs-black": ABS,
|
|
2759
2868
|
"system/abs-white": ABS,
|
|
2760
|
-
// (the community system/
|
|
2869
|
+
// (the community system/pen-100 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
|
|
2761
2870
|
// the anchor is engine-resolved and rides the neutral in BOTH plugins now —
|
|
2762
2871
|
// NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
|
|
2763
2872
|
"system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
|
|
@@ -2773,7 +2882,7 @@ var SYSTEM = {
|
|
|
2773
2882
|
"system/alpha/away-from-bg/08": OFFSET,
|
|
2774
2883
|
"system/alpha/away-from-bg/16": OFFSET,
|
|
2775
2884
|
// the inverse ladder: the offset rungs with the pole flipped per mode, for
|
|
2776
|
-
//
|
|
2885
|
+
// highlighters and edges over inverted backgrounds ("interaction" avoided in the
|
|
2777
2886
|
// bodies: acti-ON would feed the "on" flood)
|
|
2778
2887
|
"system/alpha/toward-bg/06": OFFSET_INVERSE,
|
|
2779
2888
|
"system/alpha/toward-bg/08": OFFSET_INVERSE,
|
|
@@ -2790,10 +2899,10 @@ var SYSTEM = {
|
|
|
2790
2899
|
"system/surface/high": PLANE("topmost plane \u2014 modals, dialogs"),
|
|
2791
2900
|
"system/link/default/enabled": LINK("", AA_BODY),
|
|
2792
2901
|
"system/link/default/hover": LINK(" pointer-over", AA_BODY),
|
|
2793
|
-
"system/link/default/pressed": LINK(" pressed",
|
|
2902
|
+
"system/link/default/pressed": LINK(" pressed", AA_BODY),
|
|
2794
2903
|
"system/link/inverse/enabled": LINK_INVERSE("", AA_BODY),
|
|
2795
2904
|
"system/link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
|
|
2796
|
-
"system/link/inverse/pressed": LINK_INVERSE(" pressed",
|
|
2905
|
+
"system/link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
|
|
2797
2906
|
};
|
|
2798
2907
|
var PREFIXES = [
|
|
2799
2908
|
// community spellings FIRST: the 2026-08-21 family rename made the ext word a
|
|
@@ -2840,15 +2949,15 @@ function checkCollision(brand, signalScale, signalDef, mode) {
|
|
|
2840
2949
|
collides: hueGate && deltaE <= threshold
|
|
2841
2950
|
};
|
|
2842
2951
|
}
|
|
2843
|
-
var
|
|
2952
|
+
var HUE_COLLISION_HIGHLIGHTER_DEG = 15;
|
|
2844
2953
|
var HUE_COLLISION_MIN_V = 0.5;
|
|
2845
2954
|
var SECONDARY_NOTE_MIN_V = HUE_NOISE_C / VIVID_C;
|
|
2846
|
-
var
|
|
2847
|
-
function
|
|
2955
|
+
var HIGHLIGHTER_STOPS = [3, 4, 5, 6, 7];
|
|
2956
|
+
function highlighterProximity(brand, sig, mode) {
|
|
2848
2957
|
const b = mode === "light" ? brand.light : brand.dark;
|
|
2849
2958
|
const s = mode === "light" ? sig.light : sig.dark;
|
|
2850
2959
|
let dH = Infinity, dE = Infinity;
|
|
2851
|
-
for (const k of
|
|
2960
|
+
for (const k of HIGHLIGHTER_STOPS) {
|
|
2852
2961
|
const bs = b.find((x2) => x2.stop === k), ss = s.find((x2) => x2.stop === k);
|
|
2853
2962
|
if (!bs || !ss) continue;
|
|
2854
2963
|
dH = Math.min(dH, hueDistance(bs.H, ss.H));
|
|
@@ -2857,15 +2966,15 @@ function washProximity(brand, sig, mode) {
|
|
|
2857
2966
|
return { dH, dE };
|
|
2858
2967
|
}
|
|
2859
2968
|
function checkHueCollision(brand, signalScale, signalDef, opts) {
|
|
2860
|
-
const l =
|
|
2861
|
-
const d =
|
|
2969
|
+
const l = highlighterProximity(brand, signalScale, "light");
|
|
2970
|
+
const d = highlighterProximity(brand, signalScale, "dark");
|
|
2862
2971
|
const vividness = Math.min(1, brand.brandC / VIVID_C);
|
|
2863
2972
|
return {
|
|
2864
2973
|
signal: signalDef.name,
|
|
2865
|
-
|
|
2866
|
-
|
|
2974
|
+
dHueHighlighter: { light: l.dH, dark: d.dH },
|
|
2975
|
+
highlighterDeltaE: { light: l.dE, dark: d.dE },
|
|
2867
2976
|
vividness,
|
|
2868
|
-
collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <=
|
|
2977
|
+
collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_HIGHLIGHTER_DEG
|
|
2869
2978
|
};
|
|
2870
2979
|
}
|
|
2871
2980
|
var YELLOW_SPLIT_H = 96;
|
|
@@ -3058,7 +3167,7 @@ function resolveBrand(hex, name, opts) {
|
|
|
3058
3167
|
// (buildSignalScales/signalShift) — neutral alone stays outside; exact has enforce off.
|
|
3059
3168
|
apcaClearance: opts?.apcaClearance ?? true,
|
|
3060
3169
|
darkCtaFlatApp: opts?.darkCtaFlatApp,
|
|
3061
|
-
|
|
3170
|
+
textGround: opts?.textGround
|
|
3062
3171
|
};
|
|
3063
3172
|
let scale = generateScale(hex, name, void 0, floor);
|
|
3064
3173
|
let pending = [];
|
|
@@ -3141,7 +3250,7 @@ function resolveLinkTrio(linkHex, contrastProfile) {
|
|
|
3141
3250
|
const s = resolveBrand(linkHex, "link", { skipCollisionRules: true, contrastProfile }).scale;
|
|
3142
3251
|
const at = (arr, n) => {
|
|
3143
3252
|
const st = arr.find((x2) => x2.stop === n);
|
|
3144
|
-
if (!st) throw new Error(`resolveLinkTrio: the link resolve has no
|
|
3253
|
+
if (!st) throw new Error(`resolveLinkTrio: the link resolve has no pen stop ${n}`);
|
|
3145
3254
|
return st;
|
|
3146
3255
|
};
|
|
3147
3256
|
return {
|
|
@@ -3154,16 +3263,16 @@ function resolveLinkTrio(linkHex, contrastProfile) {
|
|
|
3154
3263
|
};
|
|
3155
3264
|
}
|
|
3156
3265
|
function resolveLinkInverseTrio(seedHex, contrastProfile) {
|
|
3157
|
-
const ground =
|
|
3266
|
+
const ground = PEN_70_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
|
|
3158
3267
|
const s = resolveBrand(seedHex, "link-inverse", {
|
|
3159
3268
|
skipCollisionRules: true,
|
|
3160
3269
|
contrastProfile,
|
|
3161
3270
|
// the cross — ramp keyed, ground mode-swapped
|
|
3162
|
-
|
|
3271
|
+
textGround: { light: ground.dark, dark: ground.light }
|
|
3163
3272
|
}).scale;
|
|
3164
3273
|
const at = (arr, n) => {
|
|
3165
3274
|
const st = arr.find((x2) => x2.stop === n);
|
|
3166
|
-
if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no
|
|
3275
|
+
if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no pen stop ${n}`);
|
|
3167
3276
|
return st;
|
|
3168
3277
|
};
|
|
3169
3278
|
return {
|
|
@@ -3177,12 +3286,12 @@ function resolveLinkInverseTrio(seedHex, contrastProfile) {
|
|
|
3177
3286
|
};
|
|
3178
3287
|
}
|
|
3179
3288
|
function escapeCtaFamily(nScale, mode, contrastProfile) {
|
|
3180
|
-
const
|
|
3181
|
-
if (!
|
|
3182
|
-
const mk = (stop, L) => makeStop(stop, L,
|
|
3183
|
-
const cta = mk(9,
|
|
3184
|
-
const ctaHover = mk(10, stateFillL(
|
|
3185
|
-
const ctaPressed = mk(11, stateFillL(
|
|
3289
|
+
const strongText = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
|
|
3290
|
+
if (!strongText) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
|
|
3291
|
+
const mk = (stop, L) => makeStop(stop, L, strongText.C, strongText.H);
|
|
3292
|
+
const cta = mk(9, strongText.L);
|
|
3293
|
+
const ctaHover = mk(10, stateFillL(strongText.L, mode, 1));
|
|
3294
|
+
const ctaPressed = mk(11, stateFillL(strongText.L, mode, 2));
|
|
3186
3295
|
const onEnforce = contrastProfile !== "apca";
|
|
3187
3296
|
const onFloor = contrastProfile === "apca" ? void 0 : 4.5;
|
|
3188
3297
|
const onFillIsWhite = onTextIsWhite(apcaY(cta.r, cta.g, cta.b), cta.L, cta.C, cta.H, onEnforce, onFloor);
|
|
@@ -3258,7 +3367,7 @@ function resolveTheme(input) {
|
|
|
3258
3367
|
for (const def of SIGNALS2) {
|
|
3259
3368
|
if (adoptedForSecondary.has(def.name)) continue;
|
|
3260
3369
|
const h = checkHueCollision(scale2, effectiveOf(def.name), def, { minV: SECONDARY_NOTE_MIN_V });
|
|
3261
|
-
if (h.collides) out.push(wording(def.name, Math.min(h.
|
|
3370
|
+
if (h.collides) out.push(wording(def.name, Math.min(h.highlighterDeltaE.light, h.highlighterDeltaE.dark)));
|
|
3262
3371
|
}
|
|
3263
3372
|
return out;
|
|
3264
3373
|
};
|
|
@@ -3308,7 +3417,7 @@ function resolveTheme(input) {
|
|
|
3308
3417
|
derived: true,
|
|
3309
3418
|
notes: [
|
|
3310
3419
|
`secondary derived from the brand color (default model, seed ${liftedHex})`,
|
|
3311
|
-
...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (
|
|
3420
|
+
...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
|
|
3312
3421
|
],
|
|
3313
3422
|
distinctness: ctaDistinctness(primary.scale, scale2)
|
|
3314
3423
|
},
|
|
@@ -3333,7 +3442,7 @@ function resolveTheme(input) {
|
|
|
3333
3442
|
derived: false,
|
|
3334
3443
|
notes: [
|
|
3335
3444
|
`secondary keeps your color through the ramp; the cta is a tint of it (tint seed ${tintedHex})`,
|
|
3336
|
-
...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (
|
|
3445
|
+
...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
|
|
3337
3446
|
],
|
|
3338
3447
|
distinctness: distinctness2
|
|
3339
3448
|
},
|
|
@@ -3345,7 +3454,7 @@ function resolveTheme(input) {
|
|
|
3345
3454
|
const scale = rSec.scale;
|
|
3346
3455
|
const level = "standard";
|
|
3347
3456
|
mergeSecondarySignals(scale, input.secondaryHex);
|
|
3348
|
-
const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (
|
|
3457
|
+
const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (highlighter \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
|
|
3349
3458
|
const distinctness = ctaDistinctness(primary.scale, scale);
|
|
3350
3459
|
if (distinctness.close)
|
|
3351
3460
|
notes.push(`secondary reads close to the primary (\u0394E ${Math.min(distinctness.light, distinctness.dark).toFixed(2)}) \u2014 consider a more distinct color`);
|
|
@@ -3358,113 +3467,6 @@ function resolveTheme(input) {
|
|
|
3358
3467
|
};
|
|
3359
3468
|
}
|
|
3360
3469
|
|
|
3361
|
-
// src/engine/tokenNames.ts
|
|
3362
|
-
var SHARED_NAMES = {
|
|
3363
|
-
1: "paper-99",
|
|
3364
|
-
2: "paper-97",
|
|
3365
|
-
// stop 3 renamed wash-3 → paper-3 (owner 2026-07-24, elevation round): it is a
|
|
3366
|
-
// PLANE in both themes (light sink / dark pop), so it belongs to the surface band.
|
|
3367
|
-
// NAME ONLY — generation is index-keyed and unchanged; the internal wash machinery
|
|
3368
|
-
// (reqtoken groupOf, collision WASH_STOPS) deliberately still spans stops 3–7.
|
|
3369
|
-
// Stage B (owner 2026-08-07, names only): relabeled to the ROOT_L-derived digit,
|
|
3370
|
-
// paper-95 — same band, same index.
|
|
3371
|
-
3: "paper-95",
|
|
3372
|
-
4: "wash-92",
|
|
3373
|
-
5: "wash-89",
|
|
3374
|
-
6: "wash-85",
|
|
3375
|
-
7: "wash-80",
|
|
3376
|
-
8: "wax-74",
|
|
3377
|
-
// THE INK BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
|
|
3378
|
-
// 2026-07-29 highlight collapse — the old highlight-9 and on-highlight are
|
|
3379
|
-
// deleted; this stop carries both jobs, its on-color a paper token), the between
|
|
3380
|
-
// text stop (a value the retired text-cta hover state used to generate bespokely,
|
|
3381
|
-
// promoted to a normal stop), and the strong text stop. The three read as states
|
|
3382
|
-
// ARE the text-style cta (owner 2026-08-12). C49 restored the top
|
|
3383
|
-
// two pre-collapse stop INDICES (11, and the off-scale anchor's 12 — emitted as a
|
|
3384
|
-
// literal in cssRender, not through this table) with the between stop taking the
|
|
3385
|
-
// vacated index 10; the indices (9/10/11) are unchanged since.
|
|
3386
|
-
9: "lead-53",
|
|
3387
|
-
10: "ink-42",
|
|
3388
|
-
11: "ink-30"
|
|
3389
|
-
};
|
|
3390
|
-
var SCALE_STOP_COUNT = Object.keys(SHARED_NAMES).length;
|
|
3391
|
-
function stopTokenName(stop) {
|
|
3392
|
-
const name = SHARED_NAMES[stop];
|
|
3393
|
-
if (!name) throw new Error(`stopTokenName: unexpected stop ${stop}`);
|
|
3394
|
-
return name;
|
|
3395
|
-
}
|
|
3396
|
-
var PAPER_100 = "paper-100";
|
|
3397
|
-
var INK_0 = "ink-0";
|
|
3398
|
-
var STAMP_FILL = "stamp-fill";
|
|
3399
|
-
var STAMP_FILL_HOVER = "stamp-fill-hover";
|
|
3400
|
-
var STAMP_FILL_PRESSED = "stamp-fill-pressed";
|
|
3401
|
-
var STAMP_EDGE = "stamp-edge";
|
|
3402
|
-
var STAMP_ON = "stamp-on";
|
|
3403
|
-
var STAMP_STATE_LEAVES = {
|
|
3404
|
-
[STAMP_FILL]: "stamp/fill",
|
|
3405
|
-
[STAMP_FILL_HOVER]: "stamp/fill-hover",
|
|
3406
|
-
[STAMP_FILL_PRESSED]: "stamp/fill-pressed",
|
|
3407
|
-
[STAMP_EDGE]: "stamp/edge",
|
|
3408
|
-
[STAMP_ON]: "stamp/on"
|
|
3409
|
-
};
|
|
3410
|
-
var STAMP_LEAF = {
|
|
3411
|
-
FILL: STAMP_STATE_LEAVES[STAMP_FILL],
|
|
3412
|
-
FILL_HOVER: STAMP_STATE_LEAVES[STAMP_FILL_HOVER],
|
|
3413
|
-
FILL_PRESSED: STAMP_STATE_LEAVES[STAMP_FILL_PRESSED],
|
|
3414
|
-
EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
|
|
3415
|
-
ON: STAMP_STATE_LEAVES[STAMP_ON]
|
|
3416
|
-
};
|
|
3417
|
-
var SYSTEM_LEAF = {
|
|
3418
|
-
ABS_BLACK: "system/abs-black",
|
|
3419
|
-
ABS_WHITE: "system/abs-white",
|
|
3420
|
-
SURFACE: {
|
|
3421
|
-
DIM: "system/surface/dim",
|
|
3422
|
-
LOW: "system/surface/low",
|
|
3423
|
-
MID: "system/surface/mid",
|
|
3424
|
-
HIGH: "system/surface/high"
|
|
3425
|
-
},
|
|
3426
|
-
ALPHA: {
|
|
3427
|
-
TRANSPARENT: "system/alpha/transparent",
|
|
3428
|
-
SCRIM: "system/alpha/abs-black-060",
|
|
3429
|
-
INK: "system/alpha/ink",
|
|
3430
|
-
// the DIRECTIONAL ladders (owner 2026-08-31, renaming the 2026-08-18 bare rungs
|
|
3431
|
-
// and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
|
|
3432
|
-
// (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
|
|
3433
|
-
// INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
|
|
3434
|
-
// a "wash": that word belongs to the tinted band stops.) The reversal lives in
|
|
3435
|
-
// the engine: a consumer on an inverted ground names one token and never picks a
|
|
3436
|
-
// pole. Colored fills never ride these; their states stay engine-solved stops.
|
|
3437
|
-
// Rungs are two-digit percent, harmonizing with shadow-04/08/12.
|
|
3438
|
-
AWAY_FROM_BG_06: "system/alpha/away-from-bg/06",
|
|
3439
|
-
AWAY_FROM_BG_08: "system/alpha/away-from-bg/08",
|
|
3440
|
-
AWAY_FROM_BG_16: "system/alpha/away-from-bg/16",
|
|
3441
|
-
TOWARD_BG_06: "system/alpha/toward-bg/06",
|
|
3442
|
-
TOWARD_BG_08: "system/alpha/toward-bg/08",
|
|
3443
|
-
TOWARD_BG_16: "system/alpha/toward-bg/16",
|
|
3444
|
-
SHADOW_04: "system/alpha/shadow-04",
|
|
3445
|
-
SHADOW_08: "system/alpha/shadow-08",
|
|
3446
|
-
SHADOW_12: "system/alpha/shadow-12"
|
|
3447
|
-
}
|
|
3448
|
-
};
|
|
3449
|
-
var SURFACE_PLANE_LAW = {
|
|
3450
|
-
[SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_100 },
|
|
3451
|
-
[SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
|
|
3452
|
-
[SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
|
|
3453
|
-
[SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_100, dark: SHARED_NAMES[3] }
|
|
3454
|
-
};
|
|
3455
|
-
var TOKEN_ORDER = [
|
|
3456
|
-
...Object.values(SHARED_NAMES),
|
|
3457
|
-
STAMP_FILL,
|
|
3458
|
-
STAMP_FILL_HOVER,
|
|
3459
|
-
STAMP_FILL_PRESSED,
|
|
3460
|
-
STAMP_ON,
|
|
3461
|
-
"identity"
|
|
3462
|
-
];
|
|
3463
|
-
function tokenOrder(name) {
|
|
3464
|
-
const i = TOKEN_ORDER.indexOf(name);
|
|
3465
|
-
return i === -1 ? TOKEN_ORDER.length : i;
|
|
3466
|
-
}
|
|
3467
|
-
|
|
3468
3470
|
// src/engine/cssRender.ts
|
|
3469
3471
|
var { brandPrimary: CSS_BRAND, brandSecondary: CSS_SECONDARY, neutral: CSS_NEUTRAL } = CSS_FAMILY;
|
|
3470
3472
|
function toHex2(r, g2, b) {
|
|
@@ -3511,7 +3513,7 @@ var alphaRootVars = (mode) => [
|
|
|
3511
3513
|
...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
|
|
3512
3514
|
// the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
|
|
3513
3515
|
// same rungs, pole flipped per mode — state layers for INVERTED grounds (never
|
|
3514
|
-
// called a "
|
|
3516
|
+
// called a "highlighter" — that word is the tinted band's). One spelling with the Figma
|
|
3515
3517
|
// row: --alpha-toward-bg-06 ↔ system/alpha/toward-bg/06.
|
|
3516
3518
|
...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` --alpha-toward-bg-${offsetLeafName(r)}: ${offsetRgba(r, mode === "light" ? "dark" : "light")};`)
|
|
3517
3519
|
];
|
|
@@ -3538,7 +3540,7 @@ function brandKindBody(prefix, s, mode, page) {
|
|
|
3538
3540
|
` --${prefix}-stamp-fill-pressed: ${stopHex(f.ctaPressed)};`,
|
|
3539
3541
|
` --${prefix}-stamp-edge: var(${border ? offsetVarName(ctaBorderRung(prefix)) : TRANSPARENT_VAR});`,
|
|
3540
3542
|
// the SOFT on-cta (owner 2026-08-04: "neutral cta on's should also be the alpha"): the
|
|
3541
|
-
// neutral's cta is the scale-fed
|
|
3543
|
+
// neutral's cta is the scale-fed HIGHLIGHTER-level fill, the system's other quiet cta, so its
|
|
3542
3544
|
// button text takes the pole AT ALPHA like the default-model secondary's — composited
|
|
3543
3545
|
// over whatever state the fill is in, so hover/pressed carry their own legibility. Same
|
|
3544
3546
|
// register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
|
|
@@ -3568,16 +3570,16 @@ function neutralCss(selector, brandH, level = "default", contrastProfile, ctaBor
|
|
|
3568
3570
|
const p3Dark = brandKindP3Body(CSS_NEUTRAL, s, "dark");
|
|
3569
3571
|
return [
|
|
3570
3572
|
`${selector} {`,
|
|
3571
|
-
` --${
|
|
3572
|
-
` --${
|
|
3573
|
+
` --${PAPER_0}: ${p02(s.paper0, "#ffffff")};`,
|
|
3574
|
+
` --${PEN_100}: ${p02(s.pen100, "#000000")};`,
|
|
3573
3575
|
// the neutral IS the page, so it is judged against its own paper stop
|
|
3574
3576
|
// the neutral's overlays never read the visibility bar (the exempt family), so
|
|
3575
3577
|
// the brandless chrome context passes 0
|
|
3576
3578
|
...brandKindBody(CSS_NEUTRAL, s, "light", nPage("light")),
|
|
3577
3579
|
`}`,
|
|
3578
3580
|
`${selector}[data-theme="dark"] {`,
|
|
3579
|
-
` --${
|
|
3580
|
-
` --${
|
|
3581
|
+
` --${PAPER_0}: ${p02(s.paper0Dark, "#000000")};`,
|
|
3582
|
+
` --${PEN_100}: ${p02(s.pen100Dark, "#ffffff")};`,
|
|
3581
3583
|
...brandKindBody(CSS_NEUTRAL, s, "dark", nPage("dark")),
|
|
3582
3584
|
`}`,
|
|
3583
3585
|
...p3Light.length || p3Dark.length ? [
|
|
@@ -3666,8 +3668,8 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
|
|
|
3666
3668
|
const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
|
|
3667
3669
|
const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
|
|
3668
3670
|
const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
|
|
3669
|
-
const lightAnchors = [` --${
|
|
3670
|
-
const darkAnchors = [` --${
|
|
3671
|
+
const lightAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${PEN_100}: ${p0hex(nScale.pen100, "#000000")};`];
|
|
3672
|
+
const darkAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${PEN_100}: ${p0hex(nScale.pen100Dark, "#ffffff")};`];
|
|
3671
3673
|
const linkTrio = linkHex ? resolveLinkTrio(linkHex, contrastProfile) : null;
|
|
3672
3674
|
const link = (mode) => linkTrio ? mode === "light" ? [` --link: ${stopHex(linkTrio.link)};`, ` --link-hover: ${stopHex(linkTrio.linkHover)};`, ` --link-pressed: ${stopHex(linkTrio.linkPressed)};`] : [` --link: ${stopHex(linkTrio.linkDark)};`, ` --link-hover: ${stopHex(linkTrio.linkHoverDark)};`, ` --link-pressed: ${stopHex(linkTrio.linkPressedDark)};`] : [
|
|
3673
3675
|
` --link: var(--${CSS_BRAND}-${stopTokenName(9)});`,
|
|
@@ -3855,17 +3857,17 @@ function themeToFigma(r, input) {
|
|
|
3855
3857
|
const build = (mode) => {
|
|
3856
3858
|
const p02 = mode === "light" ? nScale.paper0 : nScale.paper0Dark;
|
|
3857
3859
|
const ramp = rampGroup(nScale[mode], mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark, neutralExtra(mode));
|
|
3858
|
-
const i02 = mode === "light" ? nScale.
|
|
3859
|
-
const
|
|
3860
|
+
const i02 = mode === "light" ? nScale.pen100 : nScale.pen100Dark;
|
|
3861
|
+
const splicePen100 = (g3) => {
|
|
3860
3862
|
if (!i02) return g3;
|
|
3861
3863
|
const out = {};
|
|
3862
3864
|
for (const [k, v2] of Object.entries(g3)) {
|
|
3863
3865
|
out[k] = v2;
|
|
3864
|
-
if (k === stopTokenName(11)) out[
|
|
3866
|
+
if (k === stopTokenName(11)) out[PEN_100] = colorFromStop(i02);
|
|
3865
3867
|
}
|
|
3866
3868
|
return out;
|
|
3867
3869
|
};
|
|
3868
|
-
const neutralGroup =
|
|
3870
|
+
const neutralGroup = splicePen100(p02 ? { [PAPER_0]: colorFromStop(p02), ...ramp } : ramp);
|
|
3869
3871
|
const secondaryGroup = rampGroup(secondary[mode], mode === "light" ? secondaryOnFillLight : secondaryOnFillDark, brandExtra(secondary, mode, CSS_FAMILY.brandSecondary));
|
|
3870
3872
|
if (input.secondaryStyle === "outline" && input.secondary) {
|
|
3871
3873
|
const s8 = secondary[mode].find((s) => s.stop === 8);
|
|
@@ -3903,12 +3905,12 @@ function themeToFigma(r, input) {
|
|
|
3903
3905
|
putLeaf(g3, STAMP_ON, colorFromHex(esc.onFillIsWhite));
|
|
3904
3906
|
}
|
|
3905
3907
|
}
|
|
3906
|
-
const
|
|
3908
|
+
const scaleTextAt = (n) => {
|
|
3907
3909
|
const s = scale[mode].find((x2) => x2.stop === n);
|
|
3908
|
-
if (!s) throw new Error(`themeToFigma link: the brand scale has no
|
|
3910
|
+
if (!s) throw new Error(`themeToFigma link: the brand scale has no pen stop ${n}`);
|
|
3909
3911
|
return s;
|
|
3910
3912
|
};
|
|
3911
|
-
const linkGroup = lt ? mode === "light" ? { "link": colorFromStop(lt.link), "link-hover": colorFromStop(lt.linkHover), "link-pressed": colorFromStop(lt.linkPressed) } : { "link": colorFromStop(lt.linkDark), "link-hover": colorFromStop(lt.linkHoverDark), "link-pressed": colorFromStop(lt.linkPressedDark) } : { "link": colorFromStop(
|
|
3913
|
+
const linkGroup = lt ? mode === "light" ? { "link": colorFromStop(lt.link), "link-hover": colorFromStop(lt.linkHover), "link-pressed": colorFromStop(lt.linkPressed) } : { "link": colorFromStop(lt.linkDark), "link-hover": colorFromStop(lt.linkHoverDark), "link-pressed": colorFromStop(lt.linkPressedDark) } : { "link": colorFromStop(scaleTextAt(9)), "link-hover": colorFromStop(scaleTextAt(10)), "link-pressed": colorFromStop(scaleTextAt(11)) };
|
|
3912
3914
|
const linkInverseGroup = mode === "light" ? { "link": colorFromStop(invLt.link), "link-hover": colorFromStop(invLt.linkHover), "link-pressed": colorFromStop(invLt.linkPressed) } : { "link": colorFromStop(invLt.linkDark), "link-hover": colorFromStop(invLt.linkHoverDark), "link-pressed": colorFromStop(invLt.linkPressedDark) };
|
|
3913
3915
|
const g2 = {
|
|
3914
3916
|
brand: brandGroup,
|