@conductor-by-89soone/tokens 0.0.0 → 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.
@@ -26,6 +26,8 @@ var EXIT_CODES = {
26
26
  "TOK-CP-USAGE": 1,
27
27
  "TOK-CP-COLOR": 1,
28
28
  "TOK-CP-ALPHA": 1,
29
+ "TOK-CP-FORBIDDEN": 1,
30
+ "TOK-THEME-KEY": 1,
29
31
  "TOK-LITERAL": 1,
30
32
  "TOK-ARG": 3
31
33
  };
@@ -270,12 +272,96 @@ var badgePairs = [
270
272
  "badge.marker.background",
271
273
  "nonText",
272
274
  "tokens spec 8.2 names the foreground `badge.marker.dot`; that key is a variant slot rather than a token, and it resolves to `status.queued` for the only declared variant."
275
+ ),
276
+ pair(
277
+ "CP-042",
278
+ "status.neutralEnd",
279
+ "badge.marker.background",
280
+ "nonText",
281
+ "CR-035. The second marker variant, measurable again now that the dark end state is `slate.400`. New id: CP-025 is permanently retired (tokens spec 8.2, 8.5)."
273
282
  )
274
283
  ];
275
284
  var disabledPairs = [
276
285
  pair("CP-040", "text.muted", "state.disabled", "body"),
277
286
  pair("CP-041", "button.policyDisabled.text", "state.disabledPolicy", "body")
278
287
  ];
288
+ var datavizPairs = [
289
+ pair("CP-043", "dataviz.series.1", "surface.base", "nonText"),
290
+ pair("CP-044", "dataviz.series.1", "surface.canvas", "nonText"),
291
+ pair("CP-045", "dataviz.series.1", "surface.raised", "nonText"),
292
+ pair("CP-046", "dataviz.series.2", "surface.base", "nonText"),
293
+ pair("CP-047", "dataviz.series.2", "surface.canvas", "nonText"),
294
+ pair("CP-048", "dataviz.series.2", "surface.raised", "nonText"),
295
+ pair("CP-049", "dataviz.series.3", "surface.base", "nonText"),
296
+ pair("CP-050", "dataviz.series.3", "surface.canvas", "nonText"),
297
+ pair("CP-051", "dataviz.series.3", "surface.raised", "nonText"),
298
+ pair("CP-052", "dataviz.series.4", "surface.base", "nonText"),
299
+ pair("CP-053", "dataviz.series.4", "surface.canvas", "nonText"),
300
+ pair("CP-054", "dataviz.series.4", "surface.raised", "nonText"),
301
+ pair("CP-055", "dataviz.series.5", "surface.base", "nonText"),
302
+ pair("CP-056", "dataviz.series.5", "surface.canvas", "nonText"),
303
+ pair("CP-057", "dataviz.series.5", "surface.raised", "nonText"),
304
+ pair("CP-058", "dataviz.series.6", "surface.base", "nonText"),
305
+ pair("CP-059", "dataviz.series.6", "surface.canvas", "nonText"),
306
+ pair("CP-060", "dataviz.series.6", "surface.raised", "nonText"),
307
+ pair("CP-061", "dataviz.series.7", "surface.base", "nonText"),
308
+ pair("CP-062", "dataviz.series.7", "surface.canvas", "nonText"),
309
+ pair("CP-063", "dataviz.series.7", "surface.raised", "nonText"),
310
+ pair("CP-064", "dataviz.series.8", "surface.base", "nonText"),
311
+ pair("CP-065", "dataviz.series.8", "surface.canvas", "nonText"),
312
+ pair("CP-066", "dataviz.series.8", "surface.raised", "nonText"),
313
+ pair("CP-067", "dataviz.series.9", "surface.base", "nonText"),
314
+ pair("CP-068", "dataviz.series.9", "surface.canvas", "nonText"),
315
+ pair("CP-069", "dataviz.series.9", "surface.raised", "nonText"),
316
+ pair("CP-070", "dataviz.series.10", "surface.base", "nonText"),
317
+ pair("CP-071", "dataviz.series.10", "surface.canvas", "nonText"),
318
+ pair("CP-072", "dataviz.series.10", "surface.raised", "nonText"),
319
+ pair("CP-073", "dataviz.series.11", "surface.base", "nonText"),
320
+ pair("CP-074", "dataviz.series.11", "surface.canvas", "nonText"),
321
+ pair("CP-075", "dataviz.series.11", "surface.raised", "nonText"),
322
+ pair("CP-076", "dataviz.series.12", "surface.base", "nonText"),
323
+ pair("CP-077", "dataviz.series.12", "surface.canvas", "nonText"),
324
+ pair("CP-078", "dataviz.series.12", "surface.raised", "nonText"),
325
+ pair("CP-079", "dataviz.series.13", "surface.base", "nonText"),
326
+ pair("CP-080", "dataviz.series.13", "surface.canvas", "nonText"),
327
+ pair("CP-081", "dataviz.series.13", "surface.raised", "nonText"),
328
+ pair("CP-082", "dataviz.series.14", "surface.base", "nonText"),
329
+ pair("CP-083", "dataviz.series.14", "surface.canvas", "nonText"),
330
+ pair("CP-084", "dataviz.series.14", "surface.raised", "nonText"),
331
+ pair("CP-085", "dataviz.series.15", "surface.base", "nonText"),
332
+ pair("CP-086", "dataviz.series.15", "surface.canvas", "nonText"),
333
+ pair("CP-087", "dataviz.series.15", "surface.raised", "nonText"),
334
+ pair("CP-088", "dataviz.series.16", "surface.base", "nonText"),
335
+ pair("CP-089", "dataviz.series.16", "surface.canvas", "nonText"),
336
+ pair("CP-090", "dataviz.series.16", "surface.raised", "nonText"),
337
+ pair("CP-091", "dataviz.series.17", "surface.base", "nonText"),
338
+ pair("CP-092", "dataviz.series.17", "surface.canvas", "nonText"),
339
+ pair("CP-093", "dataviz.series.17", "surface.raised", "nonText"),
340
+ pair("CP-094", "dataviz.series.18", "surface.base", "nonText"),
341
+ pair("CP-095", "dataviz.series.18", "surface.canvas", "nonText"),
342
+ pair("CP-096", "dataviz.series.18", "surface.raised", "nonText"),
343
+ pair("CP-097", "dataviz.series.19", "surface.base", "nonText"),
344
+ pair("CP-098", "dataviz.series.19", "surface.canvas", "nonText"),
345
+ pair("CP-099", "dataviz.series.19", "surface.raised", "nonText"),
346
+ pair("CP-100", "dataviz.series.20", "surface.base", "nonText"),
347
+ pair("CP-101", "dataviz.series.20", "surface.canvas", "nonText"),
348
+ pair("CP-102", "dataviz.series.20", "surface.raised", "nonText"),
349
+ pair("CP-103", "dataviz.sequential.1", "surface.base", "nonText"),
350
+ pair("CP-104", "dataviz.sequential.1", "surface.canvas", "nonText"),
351
+ pair("CP-105", "dataviz.sequential.1", "surface.raised", "nonText"),
352
+ pair("CP-106", "dataviz.sequential.2", "surface.base", "nonText"),
353
+ pair("CP-107", "dataviz.sequential.2", "surface.canvas", "nonText"),
354
+ pair("CP-108", "dataviz.sequential.2", "surface.raised", "nonText"),
355
+ pair("CP-109", "dataviz.sequential.3", "surface.base", "nonText"),
356
+ pair("CP-110", "dataviz.sequential.3", "surface.canvas", "nonText"),
357
+ pair("CP-111", "dataviz.sequential.3", "surface.raised", "nonText"),
358
+ pair("CP-112", "dataviz.sequential.4", "surface.base", "nonText"),
359
+ pair("CP-113", "dataviz.sequential.4", "surface.canvas", "nonText"),
360
+ pair("CP-114", "dataviz.sequential.4", "surface.raised", "nonText"),
361
+ pair("CP-115", "dataviz.sequential.5", "surface.base", "nonText"),
362
+ pair("CP-116", "dataviz.sequential.5", "surface.canvas", "nonText"),
363
+ pair("CP-117", "dataviz.sequential.5", "surface.raised", "nonText")
364
+ ];
279
365
  var contrastPairs = [
280
366
  ...textOnSurface,
281
367
  ...accentPairs,
@@ -284,7 +370,22 @@ var contrastPairs = [
284
370
  ...meterPairs,
285
371
  ...severityPairs,
286
372
  ...badgePairs,
287
- ...disabledPairs
373
+ ...disabledPairs,
374
+ ...datavizPairs
375
+ ];
376
+ var forbiddenPairs = [
377
+ {
378
+ id: "FP-001",
379
+ foreground: "text.faint",
380
+ background: "surface.elevated",
381
+ reason: "`text.faint` is `decorative` and may not be painted on this surface \u2014 2.94:1 in dark, still short of body 4.5:1 in light (FR-THM-005 AC-3)"
382
+ },
383
+ {
384
+ id: "FP-002",
385
+ foreground: "accent",
386
+ background: "surface.elevated",
387
+ reason: "the accent is a body foreground on `surface.base` and `surface.raised` only; on the elevated surface it measures 4.40:1 in dark, below body 4.5:1 (tokens spec 8.4)"
388
+ }
288
389
  ];
289
390
 
290
391
  // src/scales.ts
@@ -347,6 +448,11 @@ var radius = [
347
448
  aliased("radius.xl", "curve.24", "Largest radius."),
348
449
  semantic("radius.pill", "9999px", "Fully rounded badge, toggle, marker and progress geometry.")
349
450
  ];
451
+ var borderWidth = [
452
+ aliased("border.width.hairline", "stroke.1", "\uAE30\uBCF8 1px \uC5E3\uC9C0."),
453
+ aliased("border.width.emphasis", "stroke.2", "\uD45C \uD5E4\uB354 \uADDC\uCE59, \uB9C8\uCEE4 \uB9C1."),
454
+ aliased("border.width.rail", "stroke.3", "\uBC30\uB108 \uC0C1\uD0DC \uB808\uC77C, \uB0B4\uBE44 \uD65C\uC131 \uC778\uB514\uCF00\uC774\uD130.")
455
+ ];
350
456
  var Z_LAYERS = {
351
457
  base: 0,
352
458
  raised: 10,
@@ -389,6 +495,7 @@ var scaleTokens = [
389
495
  ...fontLineHeight,
390
496
  ...space,
391
497
  ...radius,
498
+ ...borderWidth,
392
499
  ...z,
393
500
  ...breakpoint,
394
501
  ...motion
@@ -472,14 +579,14 @@ var badge = [
472
579
  component("badge.marker.border", ref("border.strong"), "Marker-shape edge."),
473
580
  component("badge.marker.dotSize", value("9px"), "Status dot diameter."),
474
581
  component("badge.marker.dotRing", ref("surface.raised"), "Marker dot ring."),
475
- component("badge.marker.dotRingWidth", value("2px"), "Marker dot ring width."),
582
+ component("badge.marker.dotRingWidth", ref("border.width.emphasis"), "Marker dot ring width."),
476
583
  component("badge.severity.text", ref("text.primary"), "Label on a severity fill. 4.67:1 to 9.32:1.", "body")
477
584
  ];
478
585
  var table = [
479
586
  component("table.headerText", ref("text.muted"), "Column header text.", "body"),
480
587
  component("table.headerFontSize", ref("font.size.base"), "Column header size."),
481
588
  component("table.headerBorder", ref("border.default"), "Header rule."),
482
- component("table.headerBorderWidth", value("2px"), "Header rule width."),
589
+ component("table.headerBorderWidth", ref("border.width.emphasis"), "Header rule width."),
483
590
  component(
484
591
  "table.cellBorder",
485
592
  ref("border.default"),
@@ -591,6 +698,9 @@ var overlay = [
591
698
  ];
592
699
  var HEADING_MIN_PX = FONT_SIZE_PX.xl * 1.2;
593
700
  var HEADING_MAX_PX = FONT_SIZE_PX.xl * 1.6;
701
+ var H2_PX = Math.round(FONT_SIZE_PX.xl * 1.3);
702
+ var H3_PX = Math.round(FONT_SIZE_PX.xl * 1.1);
703
+ var HEADING_LINE_HEIGHT_RATIO = 1.3;
594
704
  var page = [
595
705
  component(
596
706
  "page.headingSize",
@@ -602,6 +712,26 @@ var page = [
602
712
  value("1.16"),
603
713
  "The only unitless line height: `clamp()` makes a px value impossible here."
604
714
  ),
715
+ component(
716
+ "page.sectionHeadingSize",
717
+ value(`${H2_PX}px`),
718
+ "`h2`. `font.size.xl` \xD7 1.3."
719
+ ),
720
+ component(
721
+ "page.sectionHeadingLineHeight",
722
+ value(`${roundHalfUp(H2_PX * HEADING_LINE_HEIGHT_RATIO)}px`),
723
+ "Line height paired with `page.sectionHeadingSize`, at the 1.30 heading ratio."
724
+ ),
725
+ component(
726
+ "page.subHeadingSize",
727
+ value(`${H3_PX}px`),
728
+ "`h3`. `font.size.xl` \xD7 1.1."
729
+ ),
730
+ component(
731
+ "page.subHeadingLineHeight",
732
+ value(`${roundHalfUp(H3_PX * HEADING_LINE_HEIGHT_RATIO)}px`),
733
+ "Line height paired with `page.subHeadingSize`, at the 1.30 heading ratio."
734
+ ),
605
735
  component("page.stackGap", ref("space.6"), "Gap between page sections."),
606
736
  component("page.stackGapCompact", ref("space.5"), "Section gap below `breakpoint.sm`."),
607
737
  component("page.contentStackGap", ref("space.5"), "Gap inside a content stack."),
@@ -614,12 +744,21 @@ var page = [
614
744
  component("page.skipLinkZ", ref("z.popover"), "Skip link stacking layer.")
615
745
  ];
616
746
  var shell = [
747
+ component(
748
+ "appShell.background",
749
+ value("radial-gradient(circle at 82% -10%, {surface.tint.1}, transparent 34rem), radial-gradient(circle at 10% 85%, {surface.tint.2}, transparent 30rem), {surface.base}"),
750
+ "Ambient app-shell surface derived from the source application background."
751
+ ),
617
752
  component("appShell.navWidth", ref("page.shellNavWidth"), "AppShell desktop navigation width."),
618
753
  component("appShell.mainMaxWidth", value("1480px"), "AppShell main content maximum width."),
619
754
  component("appShell.overlayBackground", ref("surface.overlay"), "Mobile navigation scrim."),
620
755
  component("skipLink.background", ref("accent.DEFAULT"), "Skip-link fill."),
621
756
  component("skipLink.text", ref("text.inverse"), "Skip-link label."),
622
- component("navList.background", ref("surface.raised"), "Navigation surface."),
757
+ component(
758
+ "navList.background",
759
+ value("linear-gradient(180deg, {surface.glass}, {surface.canvas})"),
760
+ "Glass navigation surface over the app-shell ambient background."
761
+ ),
623
762
  component("navList.border", ref("border.subtle"), "Navigation surface edge."),
624
763
  component("navItem.text", ref("text.muted"), "Navigation item label."),
625
764
  component("navItem.textActive", ref("text.primary"), "Active navigation item label."),
@@ -717,6 +856,18 @@ var surface = [
717
856
  usage: "decorative",
718
857
  value: "rgba(255, 255, 255, 0.05)",
719
858
  description: "Progress and meter track, matching the source 1:1."
859
+ }),
860
+ darkToken({
861
+ key: "surface.tint.1",
862
+ usage: "decorative",
863
+ value: "rgba(109, 124, 255, 0.11)",
864
+ description: "Primary ambient glow from the source application background. Decorative only."
865
+ }),
866
+ darkToken({
867
+ key: "surface.tint.2",
868
+ usage: "decorative",
869
+ value: "rgba(20, 184, 166, 0.055)",
870
+ description: "Secondary ambient glow from the source application background. Decorative only."
720
871
  })
721
872
  ];
722
873
  var text = [
@@ -860,10 +1011,10 @@ var status = [
860
1011
  }),
861
1012
  darkToken({
862
1013
  key: "status.neutralEnd",
863
- usage: "decorative",
864
- value: "#475569",
1014
+ usage: "nonText",
1015
+ value: "#94a3b8",
865
1016
  icon: "circle-slash",
866
- description: "Cancelled or superseded. Contrast-exempt per CR-006 (`srs_final.md` 12.1): 2.04:1 to 2.60:1 reaches non-text 3:1 on no surface. WCAG 1.4.11 exception \u2014 FR-THM-005 AC-7 forces an icon and a label alongside the colour, and the source's surface-coloured ring supplies the marker's shape boundary, so the dot's fill carries no identification on its own. Known cost: the dot reads faintly against a dark background."
1017
+ description: "Cancelled or superseded. `slate.400` at 6.61:1 on `surface.raised` (CP-042). The marker is drawn as a ring rather than a fill, so it stays distinguishable from queued's filled dot by shape as well as colour. CR-035 retires the CR-006 contrast exemption at this value: the old `#475569` measured 2.04:1 to 2.60:1 and could only be classified `decorative`, which meant the end-state dot read faintly against every dark surface."
867
1018
  })
868
1019
  ];
869
1020
  var meter = [
@@ -982,6 +1133,158 @@ var interaction = [
982
1133
  description: "Policy-blocked fill. Contrast-exempt as a fill; checked beneath the label (5.27:1)."
983
1134
  })
984
1135
  ];
1136
+ var dataviz = [
1137
+ darkToken({
1138
+ key: "dataviz.series.1",
1139
+ usage: "nonText",
1140
+ value: "#e88171",
1141
+ description: "Categorical chart series 1 of 20. Fill only; non-text 3:1 on chart surfaces (CP-043~CP-045). Series identity only; carries no magnitude."
1142
+ }),
1143
+ darkToken({
1144
+ key: "dataviz.series.2",
1145
+ usage: "nonText",
1146
+ value: "#1db198",
1147
+ description: "Categorical chart series 2 of 20. Fill only; non-text 3:1 on chart surfaces (CP-046~CP-048). Series identity only; carries no magnitude."
1148
+ }),
1149
+ darkToken({
1150
+ key: "dataviz.series.3",
1151
+ usage: "nonText",
1152
+ value: "#e979ad",
1153
+ description: "Categorical chart series 3 of 20. Fill only; non-text 3:1 on chart surfaces (CP-049~CP-051). Series identity only; carries no magnitude."
1154
+ }),
1155
+ darkToken({
1156
+ key: "dataviz.series.4",
1157
+ usage: "nonText",
1158
+ value: "#1eb641",
1159
+ description: "Categorical chart series 4 of 20. Fill only; non-text 3:1 on chart surfaces (CP-052~CP-054). Series identity only; carries no magnitude."
1160
+ }),
1161
+ darkToken({
1162
+ key: "dataviz.series.5",
1163
+ usage: "nonText",
1164
+ value: "#e073e8",
1165
+ description: "Categorical chart series 5 of 20. Fill only; non-text 3:1 on chart surfaces (CP-055~CP-057). Series identity only; carries no magnitude."
1166
+ }),
1167
+ darkToken({
1168
+ key: "dataviz.series.6",
1169
+ usage: "nonText",
1170
+ value: "#54b21d",
1171
+ description: "Categorical chart series 6 of 20. Fill only; non-text 3:1 on chart surfaces (CP-058~CP-060). Series identity only; carries no magnitude."
1172
+ }),
1173
+ darkToken({
1174
+ key: "dataviz.series.7",
1175
+ usage: "nonText",
1176
+ value: "#ad8dec",
1177
+ description: "Categorical chart series 7 of 20. Fill only; non-text 3:1 on chart surfaces (CP-061~CP-063). Series identity only; carries no magnitude."
1178
+ }),
1179
+ darkToken({
1180
+ key: "dataviz.series.8",
1181
+ usage: "nonText",
1182
+ value: "#9fa41b",
1183
+ description: "Categorical chart series 8 of 20. Fill only; non-text 3:1 on chart surfaces (CP-064~CP-066). Series identity only; carries no magnitude."
1184
+ }),
1185
+ darkToken({
1186
+ key: "dataviz.series.9",
1187
+ usage: "nonText",
1188
+ value: "#7f9bea",
1189
+ description: "Categorical chart series 9 of 20. Fill only; non-text 3:1 on chart surfaces (CP-067~CP-069). Series identity only; carries no magnitude."
1190
+ }),
1191
+ darkToken({
1192
+ key: "dataviz.series.10",
1193
+ usage: "nonText",
1194
+ value: "#e18844",
1195
+ description: "Categorical chart series 10 of 20. Fill only; non-text 3:1 on chart surfaces (CP-070~CP-072). Series identity only; carries no magnitude."
1196
+ }),
1197
+ darkToken({
1198
+ key: "dataviz.series.11",
1199
+ usage: "nonText",
1200
+ value: "#20adc2",
1201
+ description: "Categorical chart series 11 of 20. Fill only; non-text 3:1 on chart surfaces (CP-073~CP-075). Series identity only; carries no magnitude."
1202
+ }),
1203
+ darkToken({
1204
+ key: "dataviz.series.12",
1205
+ usage: "nonText",
1206
+ value: "#ea7d8f",
1207
+ description: "Categorical chart series 12 of 20. Fill only; non-text 3:1 on chart surfaces (CP-076~CP-078). Series identity only; carries no magnitude."
1208
+ }),
1209
+ darkToken({
1210
+ key: "dataviz.series.13",
1211
+ usage: "nonText",
1212
+ value: "#1db46e",
1213
+ description: "Categorical chart series 13 of 20. Fill only; non-text 3:1 on chart surfaces (CP-079~CP-081). Series identity only; carries no magnitude."
1214
+ }),
1215
+ darkToken({
1216
+ key: "dataviz.series.14",
1217
+ usage: "nonText",
1218
+ value: "#e874cd",
1219
+ description: "Categorical chart series 14 of 20. Fill only; non-text 3:1 on chart surfaces (CP-082~CP-084). Series identity only; carries no magnitude."
1220
+ }),
1221
+ darkToken({
1222
+ key: "dataviz.series.15",
1223
+ usage: "nonText",
1224
+ value: "#28b61e",
1225
+ description: "Categorical chart series 15 of 20. Fill only; non-text 3:1 on chart surfaces (CP-085~CP-087). Series identity only; carries no magnitude."
1226
+ }),
1227
+ darkToken({
1228
+ key: "dataviz.series.16",
1229
+ usage: "nonText",
1230
+ value: "#c583eb",
1231
+ description: "Categorical chart series 16 of 20. Fill only; non-text 3:1 on chart surfaces (CP-088~CP-090). Series identity only; carries no magnitude."
1232
+ }),
1233
+ darkToken({
1234
+ key: "dataviz.series.17",
1235
+ usage: "nonText",
1236
+ value: "#7cac1c",
1237
+ description: "Categorical chart series 17 of 20. Fill only; non-text 3:1 on chart surfaces (CP-091~CP-093). Series identity only; carries no magnitude."
1238
+ }),
1239
+ darkToken({
1240
+ key: "dataviz.series.18",
1241
+ usage: "nonText",
1242
+ value: "#9794ee",
1243
+ description: "Categorical chart series 18 of 20. Fill only; non-text 3:1 on chart surfaces (CP-094~CP-096). Series identity only; carries no magnitude."
1244
+ }),
1245
+ darkToken({
1246
+ key: "dataviz.series.19",
1247
+ usage: "nonText",
1248
+ value: "#c39720",
1249
+ description: "Categorical chart series 19 of 20. Fill only; non-text 3:1 on chart surfaces (CP-097~CP-099). Series identity only; carries no magnitude."
1250
+ }),
1251
+ darkToken({
1252
+ key: "dataviz.series.20",
1253
+ usage: "nonText",
1254
+ value: "#52a4e3",
1255
+ description: "Categorical chart series 20 of 20. Fill only; non-text 3:1 on chart surfaces (CP-100~CP-102). Series identity only; carries no magnitude."
1256
+ }),
1257
+ darkToken({
1258
+ key: "dataviz.sequential.1",
1259
+ usage: "nonText",
1260
+ value: "#6960d1",
1261
+ description: "Sequential distribution step 1 of 5 (least prominent), single-hue luminance ramp. Fill only; non-text 3:1 on chart surfaces (CP-103~CP-105)."
1262
+ }),
1263
+ darkToken({
1264
+ key: "dataviz.sequential.2",
1265
+ usage: "nonText",
1266
+ value: "#847dd9",
1267
+ description: "Sequential distribution step 2 of 5 (step), single-hue luminance ramp. Fill only; non-text 3:1 on chart surfaces (CP-106~CP-108)."
1268
+ }),
1269
+ darkToken({
1270
+ key: "dataviz.sequential.3",
1271
+ usage: "nonText",
1272
+ value: "#9e98e1",
1273
+ description: "Sequential distribution step 3 of 5 (step), single-hue luminance ramp. Fill only; non-text 3:1 on chart surfaces (CP-109~CP-111)."
1274
+ }),
1275
+ darkToken({
1276
+ key: "dataviz.sequential.4",
1277
+ usage: "nonText",
1278
+ value: "#b7b3e9",
1279
+ description: "Sequential distribution step 4 of 5 (step), single-hue luminance ramp. Fill only; non-text 3:1 on chart surfaces (CP-112~CP-114)."
1280
+ }),
1281
+ darkToken({
1282
+ key: "dataviz.sequential.5",
1283
+ usage: "nonText",
1284
+ value: "#cecbf0",
1285
+ description: "Sequential distribution step 5 of 5 (most prominent), single-hue luminance ramp. Fill only; non-text 3:1 on chart surfaces (CP-115~CP-117)."
1286
+ })
1287
+ ];
985
1288
  var darkPalette = [
986
1289
  ...surface,
987
1290
  ...text,
@@ -992,7 +1295,8 @@ var darkPalette = [
992
1295
  ...severity,
993
1296
  ...fontFamily,
994
1297
  ...elevation,
995
- ...interaction
1298
+ ...interaction,
1299
+ ...dataviz
996
1300
  ];
997
1301
 
998
1302
  // src/palette.light.ts
@@ -1006,6 +1310,8 @@ var values = {
1006
1310
  "surface.glass": "rgba(251, 252, 253, 0.86)",
1007
1311
  "surface.timeline": "#f1f4f8",
1008
1312
  "surface.track": "rgba(12, 18, 28, 0.08)",
1313
+ "surface.tint.1": "rgba(79, 91, 213, 0.1)",
1314
+ "surface.tint.2": "rgba(4, 120, 87, 0.05)",
1009
1315
  "text.primary": "#0c121c",
1010
1316
  "text.secondary": "#33415a",
1011
1317
  "text.muted": "#4d5a6e",
@@ -1036,12 +1342,58 @@ var values = {
1036
1342
  "state.hover": "rgba(12, 18, 28, 0.05)",
1037
1343
  "state.selected": "rgba(79, 91, 213, 0.12)",
1038
1344
  "state.disabled": "#e2e8f0",
1039
- "state.disabledPolicy": "#fef3c7"
1345
+ "state.disabledPolicy": "#fef3c7",
1346
+ // dataviz — chart series colours, darkened for light surfaces (CR-036).
1347
+ "dataviz.series.1": "#d45946",
1348
+ "dataviz.series.2": "#228f7d",
1349
+ "dataviz.series.3": "#d54e8d",
1350
+ "dataviz.series.4": "#22933d",
1351
+ "dataviz.series.5": "#ca45d3",
1352
+ "dataviz.series.6": "#4a9022",
1353
+ "dataviz.series.7": "#906adc",
1354
+ "dataviz.series.8": "#81851f",
1355
+ "dataviz.series.9": "#5a7cd8",
1356
+ "dataviz.series.10": "#bd6b2c",
1357
+ "dataviz.series.11": "#258c9c",
1358
+ "dataviz.series.12": "#d75369",
1359
+ "dataviz.series.13": "#22915d",
1360
+ "dataviz.series.14": "#d446b2",
1361
+ "dataviz.series.15": "#2a9322",
1362
+ "dataviz.series.16": "#ab5dd9",
1363
+ "dataviz.series.17": "#688b21",
1364
+ "dataviz.series.18": "#7773de",
1365
+ "dataviz.series.19": "#9b7c24",
1366
+ "dataviz.series.20": "#2f85c7",
1367
+ "dataviz.sequential.1": "#7d74dc",
1368
+ "dataviz.sequential.2": "#6e65d8",
1369
+ "dataviz.sequential.3": "#5c52d4",
1370
+ "dataviz.sequential.4": "#4539cd",
1371
+ "dataviz.sequential.5": "#3329a4"
1040
1372
  };
1041
- var lightPalette = darkPalette.map((token) => {
1042
- const value2 = values[token.key];
1043
- return value2 === void 0 ? token : { ...token, value: value2, alias: void 0 };
1044
- });
1373
+ var lightAdditions = [];
1374
+ function derivePalette(base, overrides, additions = []) {
1375
+ const baseKeys = new Set(base.map((token) => token.key));
1376
+ const unmatched = Object.keys(overrides).filter((key) => !baseKeys.has(key));
1377
+ if (unmatched.length > 0) {
1378
+ throw new TokenBuildError("TOK-THEME-KEY", "theme override names a key the base palette does not declare", [
1379
+ ...unmatched.map((key) => `override: ${key}`),
1380
+ "hint: the override would be dropped and the token would keep its dark value, which the key-set contract check cannot see. Fix the spelling, or declare the key in the base palette."
1381
+ ]);
1382
+ }
1383
+ const notExempt = additions.filter((token) => token.themeSpecific !== true);
1384
+ if (notExempt.length > 0) {
1385
+ throw new TokenBuildError("TOK-THEME-KEY", "theme-only token is not marked `themeSpecific`", [
1386
+ ...notExempt.map((token) => `addition: ${token.key}`),
1387
+ "hint: FR-QA-001 exempts a key from the cross-theme contract only when the token declares `themeSpecific: true`, so the exemption stays visible in the report."
1388
+ ]);
1389
+ }
1390
+ const derived = base.map((token) => {
1391
+ const value2 = overrides[token.key];
1392
+ return value2 === void 0 ? token : { ...token, value: value2, alias: void 0 };
1393
+ });
1394
+ return [...derived, ...additions];
1395
+ }
1396
+ var lightPalette = derivePalette(darkPalette, values, lightAdditions);
1045
1397
 
1046
1398
  // src/primitives.ts
1047
1399
  function primitive(key, value2, description) {
@@ -1085,9 +1437,17 @@ var indigo = [
1085
1437
  primitive("indigo.800", "#3f4ac0", "Light `accent.strong`.")
1086
1438
  ];
1087
1439
  var slate = [
1088
- primitive("slate.400", "#94a3b8", "Base colour of all four dark border tokens and the scrollbar."),
1440
+ primitive(
1441
+ "slate.400",
1442
+ "#94a3b8",
1443
+ "Base colour of all four dark border tokens and the scrollbar; dark `status.neutralEnd` (CR-035)."
1444
+ ),
1089
1445
  primitive("slate.500", "#64748b", "Dark `status.queued`; light `border.control`."),
1090
- primitive("slate.600", "#475569", "Dark `status.neutralEnd`."),
1446
+ primitive(
1447
+ "slate.600",
1448
+ "#475569",
1449
+ "The source's `--status-neutral-end`. No consumer since CR-035 raised the dark end-state to `slate.400`; kept as the record of the source value that decision replaced."
1450
+ ),
1091
1451
  primitive("slate.700", "#3f4b5f", "Light `status.neutralEnd`."),
1092
1452
  primitive("slate.750", "#52607a", "Light `status.queued`.")
1093
1453
  ];
@@ -1131,6 +1491,9 @@ var nonColour = [
1131
1491
  primitive("scale.32", "32px", "Spacing step."),
1132
1492
  primitive("scale.40", "40px", "Spacing step."),
1133
1493
  primitive("scale.48", "48px", "Spacing step."),
1494
+ primitive("stroke.1", "1px", "Hairline width."),
1495
+ primitive("stroke.2", "2px", "Emphasis rule width."),
1496
+ primitive("stroke.3", "3px", "Status rail and active indicator width."),
1134
1497
  primitive("curve.6", "6px", "Corner radius step."),
1135
1498
  primitive("curve.9", "9px", "Corner radius step."),
1136
1499
  primitive("curve.12", "12px", "Corner radius step."),
@@ -1391,22 +1754,70 @@ function measure(pair2, theme, index, values2) {
1391
1754
  pass: ratio >= threshold
1392
1755
  };
1393
1756
  }
1757
+ function aliasTerminus(index, reference) {
1758
+ let key = resolveReferenceTarget(index, reference);
1759
+ const seen = /* @__PURE__ */ new Set();
1760
+ while (key !== void 0 && !seen.has(key)) {
1761
+ seen.add(key);
1762
+ const alias = index.get(key)?.alias;
1763
+ if (alias === void 0) return key;
1764
+ key = resolveReferenceTarget(index, alias);
1765
+ }
1766
+ return key;
1767
+ }
1768
+ function assertNotForbidden(pair2, index, forbidden) {
1769
+ const resolvedForeground = aliasTerminus(index, pair2.foreground);
1770
+ const resolvedBackground = aliasTerminus(index, pair2.background);
1771
+ for (const banned of forbidden) {
1772
+ const bannedForeground = aliasTerminus(index, banned.foreground);
1773
+ const bannedBackground = aliasTerminus(index, banned.background);
1774
+ if (resolvedForeground !== bannedForeground || resolvedBackground !== bannedBackground) continue;
1775
+ throw new TokenBuildError("TOK-CP-FORBIDDEN", "contrast pair declares a forbidden combination", [
1776
+ `pair: ${pair2.id} (${pair2.foreground} on ${pair2.background})`,
1777
+ `forbidden: ${banned.id} (${banned.foreground} on ${banned.background})`,
1778
+ `reason: ${banned.reason}`,
1779
+ "hint: the pair resolves to a combination that must not render. Lifting the ban needs a CR."
1780
+ ]);
1781
+ }
1782
+ }
1783
+ function measureForbidden(forbidden, theme, index, values2) {
1784
+ return forbidden.map((banned) => {
1785
+ const asPair = { ...banned, usage: "nonText", themes: [theme] };
1786
+ const foreground = lookup(index, asPair, "foreground");
1787
+ const background = lookup(index, asPair, "background");
1788
+ const backgroundColor = colorOf(asPair, background, values2.get(background.key), "background");
1789
+ const foregroundColor = colorOf(asPair, foreground, values2.get(foreground.key), "foreground");
1790
+ const ratio = contrastRatio(compositeOver(foregroundColor, backgroundColor), backgroundColor);
1791
+ return {
1792
+ id: banned.id,
1793
+ theme,
1794
+ foreground: banned.foreground,
1795
+ background: banned.background,
1796
+ ratio: Number(ratio.toFixed(4)),
1797
+ reason: banned.reason
1798
+ };
1799
+ });
1800
+ }
1394
1801
  function exclusionsFor(tokens) {
1395
1802
  return tokens.filter((token) => token.tier !== "primitive" && token.usage === "decorative").map((token) => ({ key: token.key, usage: "decorative", reason: token.description }));
1396
1803
  }
1397
1804
  function checkContrast(options = {}) {
1398
1805
  const themes = options.themes ?? THEME_SOURCES;
1399
1806
  const pairs = options.pairs ?? contrastPairs;
1807
+ const forbidden = options.forbidden ?? forbiddenPairs;
1400
1808
  assertUniqueIds(pairs);
1401
1809
  const results = [];
1402
1810
  const exclusions = /* @__PURE__ */ new Map();
1811
+ const forbiddenResults = [];
1403
1812
  for (const theme of themes) {
1404
1813
  const tokens = tokensForTheme(theme);
1405
1814
  const index = buildTokenIndex(tokens);
1406
1815
  const { values: values2 } = resolveTokens(index);
1407
1816
  for (const exclusion of exclusionsFor(tokens)) exclusions.set(exclusion.key, exclusion);
1817
+ forbiddenResults.push(...measureForbidden(forbidden, theme.theme, index, values2));
1408
1818
  for (const pair2 of pairs) {
1409
1819
  if (!pair2.themes.includes(theme.theme)) continue;
1820
+ assertNotForbidden(pair2, index, forbidden);
1410
1821
  results.push(measure(pair2, theme.theme, index, values2));
1411
1822
  }
1412
1823
  }
@@ -1416,12 +1827,14 @@ function checkContrast(options = {}) {
1416
1827
  thresholds: CONTRAST_THRESHOLDS,
1417
1828
  results,
1418
1829
  exclusions: [...exclusions.values()],
1830
+ forbidden: forbiddenResults,
1419
1831
  summary: {
1420
1832
  declaredPairs: pairs.length,
1421
1833
  checks: results.length,
1422
1834
  passed: results.length - failed,
1423
1835
  failed,
1424
- excludedTokens: exclusions.size
1836
+ excludedTokens: exclusions.size,
1837
+ forbiddenPairs: forbidden.length
1425
1838
  }
1426
1839
  };
1427
1840
  }
@@ -1455,6 +1868,11 @@ function formatExclusions(report) {
1455
1868
  lines.push(` ${exclusion.key}`);
1456
1869
  lines.push(` reason: ${exclusion.reason}`);
1457
1870
  }
1871
+ lines.push(`[contrast] ${report.summary.forbiddenPairs} combination(s) declared forbidden`);
1872
+ for (const banned of report.forbidden) {
1873
+ lines.push(` ${banned.id} ${banned.foreground} on ${banned.background} (theme=${banned.theme}, ${formatRatio(banned.ratio)}:1)`);
1874
+ lines.push(` reason: ${banned.reason}`);
1875
+ }
1458
1876
  lines.push("[contrast] --report: no files written");
1459
1877
  return lines;
1460
1878
  }
@@ -1466,7 +1884,8 @@ function renderReportJson(report) {
1466
1884
  thresholds: report.thresholds,
1467
1885
  summary: report.summary,
1468
1886
  results: report.results,
1469
- exclusions: report.exclusions
1887
+ exclusions: report.exclusions,
1888
+ forbidden: report.forbidden
1470
1889
  },
1471
1890
  null,
1472
1891
  2