@opendata-ai/openchart-core 8.3.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -443,6 +443,274 @@ function findAccessibleColor(baseColor, bg, targetRatio = 4.5) {
443
443
  return baseColor;
444
444
  }
445
445
 
446
+ // src/styles/token-definitions.ts
447
+ var CSS_TOKEN_ROOT_SELECTORS = [
448
+ ".oc-root",
449
+ ".oc-chart-root",
450
+ ".oc-table-wrapper",
451
+ ".oc-table-root",
452
+ ".oc-graph-wrapper",
453
+ ".oc-graph-root",
454
+ ".oc-sankey-root",
455
+ ".oc-tilemap-root",
456
+ ".oc-barlist-root",
457
+ ".oc-map-root"
458
+ ];
459
+ var CSS_TOKENS = [
460
+ {
461
+ name: "--oc-font-family",
462
+ light: '\n "Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
463
+ },
464
+ {
465
+ name: "--oc-font-mono",
466
+ light: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace'
467
+ },
468
+ {
469
+ section: "Animation easing presets via CSS linear()",
470
+ name: "--oc-ease-smooth",
471
+ light: "linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n )"
472
+ },
473
+ {
474
+ name: "--oc-ease-snappy",
475
+ light: "linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n )",
476
+ consumers: "EASE_VAR_MAP in vanilla renderers"
477
+ },
478
+ { section: "Animation timing defaults", name: "--oc-animation-duration", light: "450ms" },
479
+ { name: "--oc-animation-stagger", light: "30ms" },
480
+ { name: "--oc-annotation-delay", light: "150ms" },
481
+ { section: "Typography scale (editorial design system)", name: "--oc-title-size", light: "26px" },
482
+ {
483
+ name: "--oc-title-weight",
484
+ light: "600",
485
+ comment: "one ladder: 400 regular / 500 medium / 600 semibold / 700 bold"
486
+ },
487
+ { name: "--oc-title-tracking", light: "-0.022em" },
488
+ { name: "--oc-subtitle-size", light: "14px" },
489
+ { name: "--oc-subtitle-weight", light: "400" },
490
+ { name: "--oc-source-size", light: "11px" },
491
+ { name: "--oc-source-weight", light: "400" },
492
+ { name: "--oc-body-size", light: "13px" },
493
+ { name: "--oc-eyebrow-size", light: "11px" },
494
+ { name: "--oc-eyebrow-weight", light: "500" },
495
+ { name: "--oc-eyebrow-tracking", light: "0.08em" },
496
+ { section: "Surfaces (light mode defaults)", name: "--oc-bg", light: "#ffffff", dark: "#09090b" },
497
+ { name: "--oc-card", light: "#ffffff", dark: "#111113" },
498
+ {
499
+ name: "--oc-secondary",
500
+ light: "#f4f4f5",
501
+ dark: "#27272a",
502
+ comment: "zinc-100, raised surface"
503
+ },
504
+ {
505
+ section: `Text levels. Names invert across modes: in light mode "muted" sits at
506
+ * a lighter zinc step than "subtle" because both are picked relative to
507
+ * the active background, not from a fixed lightness ladder. The intent
508
+ * is "muted = first step away from primary text"; "subtle = next step
509
+ * down"; etc.
510
+ *
511
+ * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
512
+ * subtle=zinc-400, faint=zinc-300
513
+ * Dark mode (dark.css): inverts the surface tokens but keeps the same
514
+ * muted -> subtle -> faint progression away from primary.`,
515
+ name: "--oc-text",
516
+ light: "#09090b",
517
+ dark: "#f7f8f8"
518
+ },
519
+ { name: "--oc-text-secondary", light: "#3f3f46", dark: "#d0d6e0" },
520
+ { name: "--oc-text-muted", light: "#71717a", dark: "#a1a1aa" },
521
+ { name: "--oc-text-faint", light: "#d4d4d8", dark: "#52525b" },
522
+ {
523
+ section: `Derived neutral ramp. Named by the share of *text* in a text-toward-
524
+ * background mix: 100 is nearly the background, 800 is nearly the text.
525
+ * These static values are the mix against the default surfaces; mounts
526
+ * restamp them from ResolvedTheme.colors.neutral, which a custom warm or
527
+ * cool theme derives from its own text/background pair.`,
528
+ name: "--oc-gray-100",
529
+ light: "#f0f0f0",
530
+ dark: "#171719"
531
+ },
532
+ { name: "--oc-gray-200", light: "#e1e1e2", dark: "#262627" },
533
+ { name: "--oc-gray-300", light: "#c9c9c9", dark: "#3d3e3f" },
534
+ { name: "--oc-gray-400", light: "#9d9d9d", dark: "#68696a" },
535
+ { name: "--oc-gray-600", light: "#787879", dark: "#8c8c8d" },
536
+ { name: "--oc-gray-800", light: "#474748", dark: "#bcbcbd" },
537
+ {
538
+ section: "Lines",
539
+ name: "--oc-gridline",
540
+ light: "rgba(0, 0, 0, 0.08)",
541
+ dark: "rgba(255, 255, 255, 0.06)"
542
+ },
543
+ {
544
+ name: "--oc-axis",
545
+ light: "rgba(0, 0, 0, 0.14)",
546
+ dark: "rgba(255, 255, 255, 0.14)",
547
+ comment: "hairline = theme.colors.hairline, not the tick-label ink"
548
+ },
549
+ {
550
+ name: "--oc-border",
551
+ light: "rgba(0, 0, 0, 0.08)",
552
+ dark: "rgba(255, 255, 255, 0.1)"
553
+ },
554
+ {
555
+ name: "--oc-border-radius",
556
+ light: "8px",
557
+ comment: "containers and tooltips; marks use --oc-radius-sm"
558
+ },
559
+ { section: "Radius ladder", name: "--oc-radius-sm", light: "2px" },
560
+ { name: "--oc-radius-md", light: "6px" },
561
+ { name: "--oc-radius-lg", light: "var(--oc-border-radius)" },
562
+ { name: "--oc-radius-full", light: "999px" },
563
+ {
564
+ section: "Brand and semantic",
565
+ name: "--oc-accent",
566
+ light: "#06b6d4",
567
+ dark: "#06b6d4"
568
+ },
569
+ {
570
+ name: "--oc-accent-strong",
571
+ light: "#0891b2",
572
+ dark: "#06b6d4",
573
+ comment: "darker cyan for line strokes on light bg",
574
+ consumers: "stamped via adaptForLightLineStroke"
575
+ },
576
+ {
577
+ name: "--oc-positive",
578
+ light: "#15803d",
579
+ dark: "#4ade80",
580
+ comment: "kept equal to DEFAULT_THEME.colors.positive (token-theme-parity test)"
581
+ },
582
+ { name: "--oc-negative", light: "#dc2626", dark: "#f87171" },
583
+ {
584
+ name: "--oc-positive-tint",
585
+ light: "color-mix(in srgb, var(--oc-positive) 10%, var(--oc-bg))",
586
+ comment: "delta chip background"
587
+ },
588
+ {
589
+ name: "--oc-negative-tint",
590
+ light: "color-mix(in srgb, var(--oc-negative) 10%, var(--oc-bg))"
591
+ },
592
+ { name: "--oc-focus", light: "#3b82f6", dark: "#60a5fa" },
593
+ {
594
+ name: "--oc-focus-ring",
595
+ light: "rgba(59, 130, 246, 0.1)",
596
+ comment: "static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode"
597
+ },
598
+ {
599
+ name: "--oc-focus-ring-strong",
600
+ light: "rgba(59, 130, 246, 0.25)"
601
+ },
602
+ {
603
+ name: "--oc-editable-hover",
604
+ light: "rgba(79, 70, 229, 0.35)",
605
+ comment: "edit-mode hover outline, indigo one-off"
606
+ },
607
+ {
608
+ section: "Spacing scale (4px base) \u2014 only --oc-space-2/4 are consumed (JS-stamped)",
609
+ name: "--oc-space-2",
610
+ light: "8px",
611
+ consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
612
+ },
613
+ {
614
+ name: "--oc-space-4",
615
+ light: "16px",
616
+ consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
617
+ },
618
+ {
619
+ section: `Hover language. The hovered element goes to full ink; the rest of its
620
+ * group drops to --oc-hover-dim over --oc-hover-duration. Maps override the
621
+ * dim locally (3000 county paths at 0.3 reads as a blackout, not a hint).`,
622
+ name: "--oc-hover-dim",
623
+ light: "0.3"
624
+ },
625
+ { name: "--oc-hover-duration", light: "140ms" },
626
+ { name: "--oc-map-hover-dim", light: "0.75" },
627
+ {
628
+ section: "Interactive states",
629
+ name: "--oc-hover-bg",
630
+ light: "rgba(0, 0, 0, 0.025)",
631
+ dark: "rgba(255, 255, 255, 0.05)"
632
+ },
633
+ {
634
+ name: "--oc-tooltip-bg",
635
+ light: "rgba(255, 255, 255, 0.88)",
636
+ dark: "rgba(17, 17, 19, 0.92)"
637
+ },
638
+ {
639
+ name: "--oc-tooltip-border",
640
+ light: "rgba(0, 0, 0, 0.08)",
641
+ dark: "rgba(255, 255, 255, 0.08)"
642
+ },
643
+ {
644
+ name: "--oc-tooltip-shadow",
645
+ light: "0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12)",
646
+ dark: "0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4)"
647
+ },
648
+ { name: "--oc-tooltip-text", light: "#09090b", dark: "#f7f8f8" },
649
+ { name: "--oc-legend-text", light: "#3f3f46", dark: "#d0d6e0" }
650
+ ];
651
+ var TOKEN_MAP = new Map(CSS_TOKENS.map((t) => [t.name, t]));
652
+ function cssTokenDefault(name, mode) {
653
+ const token = TOKEN_MAP.get(name);
654
+ if (!token) throw new Error(`Unknown CSS token: ${name}`);
655
+ if (mode === "dark" && token.dark !== void 0) return token.dark;
656
+ return token.light;
657
+ }
658
+
659
+ // src/colors/neutral.ts
660
+ var STEPS = [
661
+ [100, 0.06],
662
+ [200, 0.12],
663
+ [300, 0.22],
664
+ [400, 0.4],
665
+ [600, 0.55],
666
+ [800, 0.75]
667
+ ];
668
+ function parseHex(color) {
669
+ const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim());
670
+ if (!m) return null;
671
+ const hex = m[1].length === 3 ? m[1].split("").map((c) => c + c).join("") : m[1];
672
+ return [
673
+ parseInt(hex.slice(0, 2), 16),
674
+ parseInt(hex.slice(2, 4), 16),
675
+ parseInt(hex.slice(4, 6), 16)
676
+ ];
677
+ }
678
+ function mix(text, bg, t) {
679
+ const c = (i) => Math.round(text[i] * t + bg[i] * (1 - t));
680
+ const hex = (n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, "0");
681
+ return `#${hex(c(0))}${hex(c(1))}${hex(c(2))}`;
682
+ }
683
+ function staticRamp(mode) {
684
+ return {
685
+ 100: cssTokenDefault("--oc-gray-100", mode),
686
+ 200: cssTokenDefault("--oc-gray-200", mode),
687
+ 300: cssTokenDefault("--oc-gray-300", mode),
688
+ 400: cssTokenDefault("--oc-gray-400", mode),
689
+ 600: cssTokenDefault("--oc-gray-600", mode),
690
+ 800: cssTokenDefault("--oc-gray-800", mode),
691
+ secondary: cssTokenDefault("--oc-text-secondary", mode),
692
+ faint: cssTokenDefault("--oc-text-faint", mode),
693
+ border: cssTokenDefault("--oc-border", mode),
694
+ surface: cssTokenDefault("--oc-bg", mode)
695
+ };
696
+ }
697
+ function deriveNeutralRamp(text, bg, isDark = false) {
698
+ const mode = isDark ? "dark" : "light";
699
+ const surface = isOpaqueColor(bg) ? bg : cssTokenDefault("--oc-bg", mode);
700
+ if (!isOpaqueColor(bg)) return staticRamp(mode);
701
+ const t = parseHex(text);
702
+ const b = parseHex(bg);
703
+ if (!t || !b) return { ...staticRamp(mode), surface };
704
+ const ramp = Object.fromEntries(STEPS.map(([step, w]) => [step, mix(t, b, w)]));
705
+ return {
706
+ ...ramp,
707
+ secondary: ramp[800],
708
+ faint: ramp[300],
709
+ border: ramp[100],
710
+ surface
711
+ };
712
+ }
713
+
446
714
  // src/colors/palettes.ts
447
715
  var ACHROMATIC_RAMP = {
448
716
  fg: "#f7f8f8",
@@ -464,23 +732,73 @@ var ACHROMATIC_RAMP = {
464
732
  };
465
733
  var CATEGORICAL_PALETTE = [
466
734
  "#06b6d4",
467
- // cyan, primary accent (sRGB literal, ~205°)
468
- "#ee4a73",
469
- // rose — oklch(65% 0.20 10)
470
- "#00b054",
471
- // emerald — oklch(65% 0.20 155)
472
- "#a46bf5",
473
- // violet — oklch(65% 0.20 300)
474
- "#e07d00",
475
- // amber — oklch(68% 0.19 70)
476
- "#0091ff",
477
- // sky — oklch(65% 0.20 250)
478
- "#f36000",
479
- // orange — oklch(67% 0.20 45)
480
- "#6f7dff",
481
- // indigo — oklch(65% 0.20 275)
482
- "#00afbf"
483
- // teal — oklch(65% 0.20 200)
735
+ // cyan — oklch(0.715 0.126 215) — sRGB literal, primary accent
736
+ "#e29e47",
737
+ // ochre — oklch(0.750 0.130 70)
738
+ "#4170cb",
739
+ // blue — oklch(0.559 0.150 262)
740
+ "#b2417f",
741
+ // rose — oklch(0.550 0.161 350)
742
+ "#3ca368",
743
+ // green — oklch(0.640 0.130 155)
744
+ "#a584dc"
745
+ // violet — oklch(0.679 0.130 300)
746
+ ];
747
+ var CATEGORICAL_FILL_PALETTE = [
748
+ "#51bccc",
749
+ // cyan — oklch(0.739 0.100 209)
750
+ "#dda96b",
751
+ // ochre — oklch(0.770 0.100 70)
752
+ "#587fc8",
753
+ // blue — oklch(0.601 0.120 262)
754
+ "#bf6291",
755
+ // rose — oklch(0.619 0.131 350)
756
+ "#62ab7d",
757
+ // green — oklch(0.681 0.101 155)
758
+ "#ae96da"
759
+ // violet — oklch(0.719 0.100 300)
760
+ ];
761
+ var CATEGORICAL_PALETTE_DARK = [
762
+ "#22d3ee",
763
+ // cyan — oklch(0.797 0.134 212) — sRGB literal
764
+ "#f9be78",
765
+ // ochre — oklch(0.840 0.111 70)
766
+ "#6591e1",
767
+ // blue — oklch(0.660 0.129 262)
768
+ "#d771a4",
769
+ // rose — oklch(0.680 0.140 350)
770
+ "#6ebf8c",
771
+ // green — oklch(0.739 0.109 155)
772
+ "#bca1ed"
773
+ // violet — oklch(0.760 0.110 300)
774
+ ];
775
+ var CATEGORICAL_FILL_PALETTE_DARK = [
776
+ "#72cedc",
777
+ // cyan — oklch(0.800 0.090 208)
778
+ "#f3c898",
779
+ // ochre — oklch(0.859 0.079 70)
780
+ "#7698d6",
781
+ // blue — oklch(0.679 0.100 262)
782
+ "#d281a8",
783
+ // rose — oklch(0.700 0.111 350)
784
+ "#82ba95",
785
+ // green — oklch(0.740 0.079 155)
786
+ "#c2afe7"
787
+ // violet — oklch(0.789 0.081 300)
788
+ ];
789
+ var CATEGORICAL_EXTENDED_PALETTE = [
790
+ "#8e8f19",
791
+ // olive — oklch(0.629 0.130 110)
792
+ "#b086eb",
793
+ // periwinkle— oklch(0.701 0.149 302)
794
+ "#ac3225",
795
+ // brick — oklch(0.501 0.161 30)
796
+ "#00bfc0",
797
+ // teal — oklch(0.729 0.124 195)
798
+ "#a5538c",
799
+ // mulberry — oklch(0.560 0.129 340)
800
+ "#65b1f6"
801
+ // sky — oklch(0.740 0.126 248)
484
802
  ];
485
803
  var SEQUENTIAL_BLUE = {
486
804
  name: "blue",
@@ -972,6 +1290,16 @@ function getLayoutStrategy(breakpoint, heightClass = "normal") {
972
1290
  const base = getWidthStrategy(breakpoint);
973
1291
  return applyHeightConstraints(base, heightClass);
974
1292
  }
1293
+ var HEIGHT_NO_GRID_MAX = 150;
1294
+ var COMPACT_MAX_X_TICKS = 3;
1295
+ function resolveChromeEconomy(width, height) {
1296
+ const economy = {
1297
+ gridlines: height >= HEIGHT_NO_GRID_MAX,
1298
+ axes: height >= HEIGHT_CRAMPED_MAX
1299
+ };
1300
+ if (getBreakpoint(width) === "compact") economy.maxXTicks = COMPACT_MAX_X_TICKS;
1301
+ return economy;
1302
+ }
975
1303
 
976
1304
  // src/responsive/metrics.ts
977
1305
  var AXIS_TITLE_OFFSET_COMPACT = 35;
@@ -983,7 +1311,7 @@ var HPAD_COMPACT_FRACTION = 0.5;
983
1311
  var HPAD_COMPACT_MIN = 4;
984
1312
  var TICK_LABEL_OFFSET = 6;
985
1313
  var AXIS_TITLE_TRAILING_PAD = 4;
986
- var AXIS_TITLE_GAP = 7;
1314
+ var AXIS_TITLE_GAP = 3;
987
1315
  function axisTitleOffset(maxTickLabelWidth, titleFontSize, width, inline = false) {
988
1316
  const halfTitleGlyph = Math.ceil(titleFontSize / 2);
989
1317
  if (inline) {
@@ -1063,18 +1391,22 @@ function truncateRotatedLabel(label, tickAngle, tickFontSize, tickFontWeight, me
1063
1391
  var DEFAULT_THEME = {
1064
1392
  colors: {
1065
1393
  categorical: [...CATEGORICAL_PALETTE],
1394
+ categoricalFill: [...CATEGORICAL_FILL_PALETTE],
1066
1395
  sequential: SEQUENTIAL_PALETTES,
1067
1396
  diverging: DIVERGING_PALETTES,
1068
1397
  background: "transparent",
1069
1398
  text: "#09090b",
1070
- gridline: "rgba(0,0,0,0.1)",
1399
+ gridline: "rgba(0,0,0,0.08)",
1400
+ // Structural hairline: axis lines, separators. Deliberately heavier than
1401
+ // the gridline and lighter than any ink, so structure reads as structure.
1402
+ hairline: "rgba(0,0,0,0.14)",
1071
1403
  // Used for axis lines/ticks AND axis tick label fill. Must clear WCAG AA
1072
1404
  // contrast (4.5:1) on white because tick labels are rendered with this
1073
1405
  // color. Zinc-500 hits ~5.7:1.
1074
1406
  axis: "#71717a",
1075
1407
  annotationFill: "rgba(0,0,0,0.04)",
1076
1408
  annotationText: "#71717a",
1077
- positive: "#16a34a",
1409
+ positive: "#15803d",
1078
1410
  negative: "#dc2626"
1079
1411
  },
1080
1412
  fonts: {
@@ -1086,19 +1418,21 @@ var DEFAULT_THEME = {
1086
1418
  body: 13,
1087
1419
  small: 11,
1088
1420
  axisTick: 11,
1089
- metricLabel: 10,
1421
+ metricLabel: 11,
1090
1422
  metricValue: 22
1091
1423
  },
1424
+ // One ladder everywhere: 400 regular / 500 medium (eyebrow, labels, UI,
1425
+ // values) / 600 semibold (title, series names, annotation lede) / 700 bold.
1092
1426
  weights: {
1093
- normal: 450,
1094
- medium: 550,
1095
- semibold: 590,
1427
+ normal: 400,
1428
+ medium: 500,
1429
+ semibold: 600,
1096
1430
  bold: 700
1097
1431
  }
1098
1432
  },
1099
1433
  spacing: {
1100
1434
  padding: 20,
1101
- chromeGap: 4,
1435
+ chromeGap: 6,
1102
1436
  chromeToChart: 8,
1103
1437
  chartToFooter: 8,
1104
1438
  axisMargin: 6,
@@ -1110,17 +1444,18 @@ var DEFAULT_THEME = {
1110
1444
  // so the gap holds regardless of font size.
1111
1445
  xAxisLabelPadding: 4
1112
1446
  },
1113
- borderRadius: 2,
1447
+ /** Containers and tooltips. Marks carry their own 2px radius constant. */
1448
+ borderRadius: 8,
1114
1449
  chrome: {
1115
1450
  eyebrow: {
1116
1451
  fontSize: 11,
1117
- fontWeight: 510,
1452
+ fontWeight: 500,
1118
1453
  color: "#06b6d4",
1119
1454
  lineHeight: 1.4
1120
1455
  },
1121
1456
  title: {
1122
1457
  fontSize: 26,
1123
- fontWeight: 590,
1458
+ fontWeight: 600,
1124
1459
  color: "#09090b",
1125
1460
  lineHeight: 1.15
1126
1461
  },
@@ -1149,6 +1484,8 @@ var DEFAULT_THEME = {
1149
1484
  lineHeight: 1.4
1150
1485
  }
1151
1486
  },
1487
+ /** No editorial rule by default; the `broadsheet` preset sets one. */
1488
+ rule: null,
1152
1489
  seriesStrategy: "palette"
1153
1490
  };
1154
1491
 
@@ -1215,9 +1552,15 @@ function adaptTheme(theme) {
1215
1552
  const lightChrome = DEFAULT_THEME.chrome;
1216
1553
  const overridden = (current, lightDefault, darkDefault) => current === lightDefault ? darkDefault : current;
1217
1554
  const darkText = pairs?.["colors.text"] ? pairs["colors.text"].dark : overridden(theme.colors.text, light.text, DARK_TEXT);
1218
- const darkGridline = pairs?.["colors.gridline"] ? pairs["colors.gridline"].dark : overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.05)");
1555
+ const darkGridline = pairs?.["colors.gridline"] ? pairs["colors.gridline"].dark : overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.06)");
1556
+ const darkHairline = pairs?.["colors.hairline"] ? pairs["colors.hairline"].dark : overridden(theme.colors.hairline, light.hairline, "rgba(255,255,255,0.14)");
1219
1557
  const darkAxis = pairs?.["colors.axis"] ? pairs["colors.axis"].dark : overridden(theme.colors.axis, light.axis, "#a1a1aa");
1220
- const categorical = theme.colors.categorical;
1558
+ const categorical = overridden(theme.colors.categorical, light.categorical, [
1559
+ ...CATEGORICAL_PALETTE_DARK
1560
+ ]);
1561
+ const categoricalFill = overridden(theme.colors.categoricalFill, light.categoricalFill, [
1562
+ ...CATEGORICAL_FILL_PALETTE_DARK
1563
+ ]);
1221
1564
  return {
1222
1565
  ...theme,
1223
1566
  isDark: true,
@@ -1230,8 +1573,11 @@ function adaptTheme(theme) {
1230
1573
  annotationFill: pairs?.["colors.annotationFill"] ? pairs["colors.annotationFill"].dark : overridden(theme.colors.annotationFill, light.annotationFill, "rgba(255,255,255,0.06)"),
1231
1574
  annotationText: pairs?.["colors.annotationText"] ? pairs["colors.annotationText"].dark : overridden(theme.colors.annotationText, light.annotationText, darkMuted),
1232
1575
  categorical,
1233
- positive: pairs?.["colors.positive"] ? pairs["colors.positive"].dark : theme.colors.positive !== "#16a34a" ? theme.colors.positive : "#34d399",
1234
- negative: pairs?.["colors.negative"] ? pairs["colors.negative"].dark : theme.colors.negative !== "#dc2626" ? theme.colors.negative : "#f87171"
1576
+ categoricalFill,
1577
+ hairline: darkHairline,
1578
+ positive: pairs?.["colors.positive"] ? pairs["colors.positive"].dark : overridden(theme.colors.positive, light.positive, "#4ade80"),
1579
+ negative: pairs?.["colors.negative"] ? pairs["colors.negative"].dark : overridden(theme.colors.negative, light.negative, "#f87171"),
1580
+ neutral: deriveNeutralRamp(darkText, darkBg, true)
1235
1581
  },
1236
1582
  chrome: {
1237
1583
  // Eyebrow keeps its accent tint (cyan in both modes) unless the user
@@ -1261,7 +1607,10 @@ function adaptTheme(theme) {
1261
1607
  ...theme.chrome.footer,
1262
1608
  color: pairs?.["chrome.footer.color"] ? pairs["chrome.footer.color"].dark : overridden(theme.chrome.footer.color, lightChrome.footer.color, darkMuted)
1263
1609
  }
1264
- }
1610
+ },
1611
+ // The editorial rule is a brand device: it keeps its authored color unless
1612
+ // the theme gave an explicit dark half of the pair.
1613
+ rule: theme.rule && pairs?.["rule.color"] ? { ...theme.rule, color: pairs["rule.color"].dark } : theme.rule
1265
1614
  };
1266
1615
  }
1267
1616
  function adaptForLightLineStroke(color) {
@@ -1324,6 +1673,71 @@ var wire = {
1324
1673
  },
1325
1674
  borderRadius: 2
1326
1675
  };
1676
+ var broadsheet = {
1677
+ colors: {
1678
+ categorical: ["#1d5f8a", "#e3120b", "#7fb0d3", "#e0a63b", "#3a8a6e", "#8c8680"],
1679
+ categoricalFill: ["#3b7097", "#ee493a", "#97c2e2", "#efbb64", "#579a81", "#9e9893"],
1680
+ background: { light: "#fffdf9", dark: "#171513" },
1681
+ text: { light: "#1a1714", dark: "#f4f1ea" },
1682
+ gridline: { light: "rgba(26,23,20,0.08)", dark: "rgba(244,241,234,0.07)" },
1683
+ // Heavier than the default 0.14: newsprint rules are drawn to be seen.
1684
+ hairline: { light: "rgba(26,23,20,0.28)", dark: "rgba(244,241,234,0.28)" },
1685
+ axis: { light: "#6b645c", dark: "#a8a099" },
1686
+ annotationFill: { light: "rgba(26,23,20,0.05)", dark: "rgba(244,241,234,0.07)" },
1687
+ annotationText: { light: "#6b645c", dark: "#a8a099" }
1688
+ },
1689
+ fonts: {
1690
+ sizes: { title: 24, subtitle: 14, body: 13, small: 11 }
1691
+ },
1692
+ spacing: {
1693
+ padding: 20,
1694
+ chromeGap: 8,
1695
+ chromeToChart: 16,
1696
+ chartToFooter: 12
1697
+ },
1698
+ chrome: {
1699
+ eyebrow: { color: { light: "#e3120b", dark: "#f4463f" }, fontSize: 11, fontWeight: 700 },
1700
+ title: { fontSize: 24, fontWeight: 700, lineHeight: 1.18 },
1701
+ subtitle: { fontWeight: 400, lineHeight: 1.45 }
1702
+ },
1703
+ /** The masthead device: a short red bar above the eyebrow. */
1704
+ rule: { color: { light: "#e3120b", dark: "#f4463f" }, width: 40, thickness: 3 },
1705
+ borderRadius: 0
1706
+ };
1707
+ var terminal = {
1708
+ colors: {
1709
+ categorical: [...CATEGORICAL_PALETTE_DARK],
1710
+ categoricalFill: [...CATEGORICAL_FILL_PALETTE_DARK],
1711
+ background: { light: "#0b0f14", dark: "#0b0f14" },
1712
+ text: { light: "#e6edf3", dark: "#e6edf3" },
1713
+ gridline: { light: "rgba(230,237,243,0.06)", dark: "rgba(230,237,243,0.06)" },
1714
+ hairline: { light: "rgba(230,237,243,0.14)", dark: "rgba(230,237,243,0.14)" },
1715
+ // 6.25:1 on #0b0f14 — tick labels are drawn in this color too.
1716
+ axis: { light: "#8b949e", dark: "#8b949e" },
1717
+ annotationFill: { light: "rgba(230,237,243,0.06)", dark: "rgba(230,237,243,0.06)" },
1718
+ annotationText: { light: "#8b949e", dark: "#8b949e" },
1719
+ positive: { light: "#4ade80", dark: "#4ade80" },
1720
+ negative: { light: "#f87171", dark: "#f87171" }
1721
+ },
1722
+ fonts: {
1723
+ family: '"Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
1724
+ mono: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace',
1725
+ sizes: { title: 15, subtitle: 12, body: 12, small: 10, axisTick: 10 }
1726
+ },
1727
+ spacing: {
1728
+ padding: 12,
1729
+ chromeGap: 3,
1730
+ chromeToChart: 6,
1731
+ chartToFooter: 6
1732
+ },
1733
+ chrome: {
1734
+ eyebrow: { color: { light: "#22d3ee", dark: "#22d3ee" }, fontSize: 10, fontWeight: 500 },
1735
+ title: { fontSize: 15, fontWeight: 600, lineHeight: 1.3 },
1736
+ subtitle: { fontWeight: 400, lineHeight: 1.4 }
1737
+ },
1738
+ borderRadius: 4,
1739
+ seriesStrategy: "accent-neutral"
1740
+ };
1327
1741
 
1328
1742
  // src/theme/resolve.ts
1329
1743
  function deepMerge(target, source) {
@@ -1350,11 +1764,13 @@ function themeConfigToPartial(config) {
1350
1764
  colors.categorical = config.colors;
1351
1765
  } else {
1352
1766
  if (config.colors.categorical) colors.categorical = config.colors.categorical;
1767
+ if (config.colors.categoricalFill) colors.categoricalFill = config.colors.categoricalFill;
1353
1768
  if (config.colors.sequential) colors.sequential = config.colors.sequential;
1354
1769
  if (config.colors.diverging) colors.diverging = config.colors.diverging;
1355
1770
  if (config.colors.background) colors.background = resolveTokenLight(config.colors.background);
1356
1771
  if (config.colors.text) colors.text = resolveTokenLight(config.colors.text);
1357
1772
  if (config.colors.gridline) colors.gridline = resolveTokenLight(config.colors.gridline);
1773
+ if (config.colors.hairline) colors.hairline = resolveTokenLight(config.colors.hairline);
1358
1774
  if (config.colors.axis) colors.axis = resolveTokenLight(config.colors.axis);
1359
1775
  if (config.colors.annotationFill)
1360
1776
  colors.annotationFill = resolveTokenLight(config.colors.annotationFill);
@@ -1363,6 +1779,9 @@ function themeConfigToPartial(config) {
1363
1779
  if (config.colors.positive) colors.positive = resolveTokenLight(config.colors.positive);
1364
1780
  if (config.colors.negative) colors.negative = resolveTokenLight(config.colors.negative);
1365
1781
  }
1782
+ if (colors.categorical && !colors.categoricalFill) {
1783
+ colors.categoricalFill = colors.categorical;
1784
+ }
1366
1785
  partial.colors = colors;
1367
1786
  }
1368
1787
  if (config.fonts) {
@@ -1408,6 +1827,13 @@ function themeConfigToPartial(config) {
1408
1827
  }
1409
1828
  partial.chrome = chrome;
1410
1829
  }
1830
+ if (config.rule) {
1831
+ partial.rule = {
1832
+ color: resolveTokenLight(config.rule.color),
1833
+ width: config.rule.width,
1834
+ thickness: config.rule.thickness
1835
+ };
1836
+ }
1411
1837
  if (config.seriesStrategy) {
1412
1838
  partial.seriesStrategy = config.seriesStrategy;
1413
1839
  }
@@ -1420,6 +1846,7 @@ function collectTokenPairs(config) {
1420
1846
  "background",
1421
1847
  "text",
1422
1848
  "gridline",
1849
+ "hairline",
1423
1850
  "axis",
1424
1851
  "annotationFill",
1425
1852
  "annotationText",
@@ -1433,6 +1860,9 @@ function collectTokenPairs(config) {
1433
1860
  }
1434
1861
  }
1435
1862
  }
1863
+ if (config.rule?.color && typeof config.rule.color === "object" && "light" in config.rule.color) {
1864
+ pairs["rule.color"] = config.rule.color;
1865
+ }
1436
1866
  if (config.chrome) {
1437
1867
  for (const [element, value] of Object.entries(config.chrome)) {
1438
1868
  if (value && typeof value === "object" && "color" in value && value.color && typeof value.color === "object" && "light" in value.color) {
@@ -1491,9 +1921,9 @@ function adjustOpacity(hex, opacity) {
1491
1921
  const r = parseInt(m[1].slice(0, 2), 16);
1492
1922
  const g = parseInt(m[1].slice(2, 4), 16);
1493
1923
  const b = parseInt(m[1].slice(4, 6), 16);
1494
- const mix = (c) => Math.round(c * opacity + 128 * (1 - opacity));
1924
+ const mix2 = (c) => Math.round(c * opacity + 128 * (1 - opacity));
1495
1925
  const toHex = (n) => n.toString(16).padStart(2, "0");
1496
- return `#${toHex(mix(r))}${toHex(mix(g))}${toHex(mix(b))}`;
1926
+ return `#${toHex(mix2(r))}${toHex(mix2(g))}${toHex(mix2(b))}`;
1497
1927
  }
1498
1928
  function resolveTheme(userTheme, base = DEFAULT_THEME) {
1499
1929
  let merged = userTheme ? deepMerge(base, themeConfigToPartial(userTheme)) : { ...base };
@@ -1523,6 +1953,10 @@ function resolveTheme(userTheme, base = DEFAULT_THEME) {
1523
1953
  }
1524
1954
  return {
1525
1955
  ...merged,
1956
+ colors: {
1957
+ ...merged.colors,
1958
+ neutral: deriveNeutralRamp(merged.colors.text, merged.colors.background, dark)
1959
+ },
1526
1960
  isDark: dark,
1527
1961
  _tokenPairs: userTheme ? collectTokenPairs(userTheme) : void 0
1528
1962
  };
@@ -1626,6 +2060,19 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
1626
2060
  let topY = pad;
1627
2061
  const topElements = {};
1628
2062
  const eyebrowNorm = chromeMode === "compact" ? null : normalizeChromeText(chrome.eyebrow);
2063
+ const titleNormPeek = normalizeChromeText(chrome.title);
2064
+ const subtitleNormPeek = chromeMode === "compact" ? null : normalizeChromeText(chrome.subtitle);
2065
+ let ruleElement;
2066
+ if (theme.rule && (eyebrowNorm || titleNormPeek || subtitleNormPeek)) {
2067
+ ruleElement = {
2068
+ x: pad,
2069
+ y: topY,
2070
+ width: theme.rule.width,
2071
+ thickness: theme.rule.thickness,
2072
+ color: theme.rule.color
2073
+ };
2074
+ topY += theme.rule.thickness + chromeGap;
2075
+ }
1629
2076
  if (eyebrowNorm) {
1630
2077
  const style = buildTextStyle(
1631
2078
  theme.chrome.eyebrow,
@@ -1647,7 +2094,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
1647
2094
  topElements.eyebrow = element;
1648
2095
  topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
1649
2096
  }
1650
- const titleNorm = normalizeChromeText(chrome.title);
2097
+ const titleNorm = titleNormPeek;
1651
2098
  if (titleNorm) {
1652
2099
  const style = buildTextStyle(
1653
2100
  theme.chrome.title,
@@ -1669,7 +2116,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
1669
2116
  topElements.title = element;
1670
2117
  topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
1671
2118
  }
1672
- const subtitleNorm = chromeMode === "compact" ? null : normalizeChromeText(chrome.subtitle);
2119
+ const subtitleNorm = subtitleNormPeek;
1673
2120
  if (subtitleNorm) {
1674
2121
  const style = buildTextStyle(
1675
2122
  theme.chrome.subtitle,
@@ -1705,6 +2152,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
1705
2152
  return {
1706
2153
  topHeight,
1707
2154
  bottomHeight: compactBottom,
2155
+ ...ruleElement ? { rule: ruleElement } : {},
1708
2156
  ...topElements
1709
2157
  };
1710
2158
  }
@@ -1811,6 +2259,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
1811
2259
  return {
1812
2260
  topHeight,
1813
2261
  bottomHeight,
2262
+ ...ruleElement ? { rule: ruleElement } : {},
1814
2263
  ...topElements,
1815
2264
  ...bottomElements,
1816
2265
  ...brandElement ? { brand: brandElement } : {}
@@ -2072,6 +2521,12 @@ var D3_FORMAT_SUFFIX_RE = /^(.*~?[efgsrdxXobcnp%])(.+)$/;
2072
2521
  function buildD3Formatter(formatStr) {
2073
2522
  if (!formatStr) return null;
2074
2523
  if (formatStr === "ordinal") return formatOrdinal;
2524
+ if (formatStr === "compact") {
2525
+ return (v) => {
2526
+ if (!Number.isFinite(v)) return String(v);
2527
+ return Math.abs(v) >= 1e3 ? abbreviateNumber(v) : formatNumber(v);
2528
+ };
2529
+ }
2075
2530
  try {
2076
2531
  const fmt = d3Format(formatStr);
2077
2532
  if (formatStr.includes("s")) {
@@ -2142,6 +2597,12 @@ function isYearContext(ctx) {
2142
2597
  function resolveNumberFormatter(formatStr, ctx) {
2143
2598
  if (!formatStr) return null;
2144
2599
  if (formatStr === "ordinal") return formatOrdinal;
2600
+ if (formatStr === "compact") {
2601
+ return (v) => {
2602
+ if (!Number.isFinite(v)) return String(v);
2603
+ return Math.abs(v) >= 1e3 ? abbreviateNumber(v) : formatNumber(v);
2604
+ };
2605
+ }
2145
2606
  if (formatStr === "percent") {
2146
2607
  const fraction = ctx?.extent ? Math.max(Math.abs(ctx.extent[0]), Math.abs(ctx.extent[1])) <= 1 : true;
2147
2608
  return (v) => formatPercent(v, { fraction });
@@ -2583,168 +3044,6 @@ function deepMergeSpec(target, source) {
2583
3044
  }
2584
3045
  return result;
2585
3046
  }
2586
-
2587
- // src/styles/token-definitions.ts
2588
- var CSS_TOKEN_ROOT_SELECTORS = [
2589
- ".oc-root",
2590
- ".oc-chart-root",
2591
- ".oc-table-wrapper",
2592
- ".oc-table-root",
2593
- ".oc-graph-wrapper",
2594
- ".oc-graph-root",
2595
- ".oc-sankey-root",
2596
- ".oc-tilemap-root",
2597
- ".oc-barlist-root",
2598
- ".oc-map-root"
2599
- ];
2600
- var CSS_TOKENS = [
2601
- {
2602
- name: "--oc-font-family",
2603
- light: '\n "Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
2604
- },
2605
- {
2606
- name: "--oc-font-mono",
2607
- light: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace'
2608
- },
2609
- {
2610
- section: "Animation easing presets via CSS linear()",
2611
- name: "--oc-ease-smooth",
2612
- light: "linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n )"
2613
- },
2614
- {
2615
- name: "--oc-ease-snappy",
2616
- light: "linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n )",
2617
- consumers: "EASE_VAR_MAP in vanilla renderers"
2618
- },
2619
- { section: "Animation timing defaults", name: "--oc-animation-duration", light: "500ms" },
2620
- { name: "--oc-animation-stagger", light: "80ms" },
2621
- { name: "--oc-annotation-delay", light: "200ms" },
2622
- { section: "Typography scale (editorial design system)", name: "--oc-title-size", light: "26px" },
2623
- { name: "--oc-title-weight", light: "590" },
2624
- { name: "--oc-title-tracking", light: "-0.022em" },
2625
- { name: "--oc-subtitle-size", light: "14px" },
2626
- { name: "--oc-subtitle-weight", light: "450" },
2627
- { name: "--oc-source-size", light: "11px" },
2628
- { name: "--oc-source-weight", light: "450" },
2629
- { name: "--oc-body-size", light: "13px" },
2630
- { name: "--oc-eyebrow-size", light: "11px" },
2631
- { name: "--oc-eyebrow-weight", light: "550" },
2632
- { name: "--oc-eyebrow-tracking", light: "0.08em" },
2633
- { section: "Surfaces (light mode defaults)", name: "--oc-bg", light: "#ffffff", dark: "#09090b" },
2634
- { name: "--oc-card", light: "#ffffff", dark: "#111113" },
2635
- {
2636
- name: "--oc-secondary",
2637
- light: "#f4f4f5",
2638
- dark: "#27272a",
2639
- comment: "zinc-100, raised surface"
2640
- },
2641
- {
2642
- section: `Text levels. Names invert across modes: in light mode "muted" sits at
2643
- * a lighter zinc step than "subtle" because both are picked relative to
2644
- * the active background, not from a fixed lightness ladder. The intent
2645
- * is "muted = first step away from primary text"; "subtle = next step
2646
- * down"; etc.
2647
- *
2648
- * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
2649
- * subtle=zinc-400, faint=zinc-300
2650
- * Dark mode (dark.css): inverts the surface tokens but keeps the same
2651
- * muted -> subtle -> faint progression away from primary.`,
2652
- name: "--oc-text",
2653
- light: "#09090b",
2654
- dark: "#f7f8f8"
2655
- },
2656
- { name: "--oc-text-secondary", light: "#3f3f46", dark: "#d0d6e0" },
2657
- { name: "--oc-text-muted", light: "#71717a", dark: "#a1a1aa" },
2658
- { name: "--oc-text-faint", light: "#d4d4d8", dark: "#52525b" },
2659
- {
2660
- section: "Lines",
2661
- name: "--oc-gridline",
2662
- light: "rgba(0, 0, 0, 0.1)",
2663
- dark: "rgba(255, 255, 255, 0.05)"
2664
- },
2665
- {
2666
- name: "--oc-axis",
2667
- light: "rgba(0, 0, 0, 0.1)",
2668
- dark: "rgba(255, 255, 255, 0.1)"
2669
- },
2670
- {
2671
- name: "--oc-border",
2672
- light: "rgba(0, 0, 0, 0.08)",
2673
- dark: "rgba(255, 255, 255, 0.1)"
2674
- },
2675
- { name: "--oc-border-radius", light: "2px" },
2676
- {
2677
- section: "Brand and semantic",
2678
- name: "--oc-accent",
2679
- light: "#06b6d4",
2680
- dark: "#06b6d4"
2681
- },
2682
- {
2683
- name: "--oc-accent-strong",
2684
- light: "#0891b2",
2685
- dark: "#06b6d4",
2686
- comment: "darker cyan for line strokes on light bg",
2687
- consumers: "stamped via adaptForLightLineStroke"
2688
- },
2689
- { name: "--oc-positive", light: "#10b981", dark: "#34d399" },
2690
- { name: "--oc-negative", light: "#e11d48", dark: "#fb7185" },
2691
- { name: "--oc-focus", light: "#3b82f6", dark: "#60a5fa" },
2692
- {
2693
- name: "--oc-focus-ring",
2694
- light: "rgba(59, 130, 246, 0.1)",
2695
- comment: "static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode"
2696
- },
2697
- {
2698
- name: "--oc-focus-ring-strong",
2699
- light: "rgba(59, 130, 246, 0.25)"
2700
- },
2701
- {
2702
- name: "--oc-editable-hover",
2703
- light: "rgba(79, 70, 229, 0.35)",
2704
- comment: "edit-mode hover outline, indigo one-off"
2705
- },
2706
- {
2707
- section: "Spacing scale (4px base) \u2014 only --oc-space-2/4 are consumed (JS-stamped)",
2708
- name: "--oc-space-2",
2709
- light: "8px",
2710
- consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
2711
- },
2712
- {
2713
- name: "--oc-space-4",
2714
- light: "16px",
2715
- consumers: "stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these"
2716
- },
2717
- {
2718
- section: "Interactive states",
2719
- name: "--oc-hover-bg",
2720
- light: "rgba(0, 0, 0, 0.025)",
2721
- dark: "rgba(255, 255, 255, 0.05)"
2722
- },
2723
- {
2724
- name: "--oc-tooltip-bg",
2725
- light: "rgba(255, 255, 255, 0.88)",
2726
- dark: "rgba(17, 17, 19, 0.92)"
2727
- },
2728
- {
2729
- name: "--oc-tooltip-border",
2730
- light: "rgba(0, 0, 0, 0.08)",
2731
- dark: "rgba(255, 255, 255, 0.08)"
2732
- },
2733
- {
2734
- name: "--oc-tooltip-shadow",
2735
- light: "0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12)",
2736
- dark: "0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4)"
2737
- },
2738
- { name: "--oc-tooltip-text", light: "#09090b", dark: "#f7f8f8" },
2739
- { name: "--oc-legend-text", light: "#3f3f46", dark: "#d0d6e0" }
2740
- ];
2741
- var TOKEN_MAP = new Map(CSS_TOKENS.map((t) => [t.name, t]));
2742
- function cssTokenDefault(name, mode) {
2743
- const token = TOKEN_MAP.get(name);
2744
- if (!token) throw new Error(`Unknown CSS token: ${name}`);
2745
- if (mode === "dark" && token.dark !== void 0) return token.dark;
2746
- return token.light;
2747
- }
2748
3047
  export {
2749
3048
  ACHROMATIC_RAMP,
2750
3049
  AXIS_TITLE_GAP,
@@ -2756,7 +3055,12 @@ export {
2756
3055
  BRAND_RESERVE_WIDTH,
2757
3056
  BREAKPOINT_COMPACT_MAX,
2758
3057
  BREAKPOINT_MEDIUM_MAX,
3058
+ CATEGORICAL_EXTENDED_PALETTE,
3059
+ CATEGORICAL_FILL_PALETTE,
3060
+ CATEGORICAL_FILL_PALETTE_DARK,
2759
3061
  CATEGORICAL_PALETTE,
3062
+ CATEGORICAL_PALETTE_DARK,
3063
+ COMPACT_MAX_X_TICKS,
2760
3064
  COMPACT_WIDTH,
2761
3065
  CSS_TOKENS,
2762
3066
  CSS_TOKEN_ROOT_SELECTORS,
@@ -2766,6 +3070,7 @@ export {
2766
3070
  FOOTNOTE_LINE_HEIGHT,
2767
3071
  GRAPH_ENCODING_RULES,
2768
3072
  HEIGHT_CRAMPED_MAX,
3073
+ HEIGHT_NO_GRID_MAX,
2769
3074
  HEIGHT_SHORT_MAX,
2770
3075
  HPAD_COMPACT_FRACTION,
2771
3076
  HPAD_COMPACT_MIN,
@@ -2799,6 +3104,7 @@ export {
2799
3104
  areaChart,
2800
3105
  axisTitleOffset,
2801
3106
  barChart,
3107
+ broadsheet,
2802
3108
  buildCompactStepFormatter,
2803
3109
  buildD3Formatter,
2804
3110
  buildTemporalFormatter,
@@ -2813,6 +3119,7 @@ export {
2813
3119
  dataTable,
2814
3120
  deepMergeSpec,
2815
3121
  defaultNumberFormatter,
3122
+ deriveNeutralRamp,
2816
3123
  detectCollision,
2817
3124
  donutChart,
2818
3125
  dotChart,
@@ -2861,6 +3168,7 @@ export {
2861
3168
  overlapArea,
2862
3169
  pickLabelColor,
2863
3170
  pieChart,
3171
+ resolveChromeEconomy,
2864
3172
  resolveCollisions,
2865
3173
  resolveMarkDef,
2866
3174
  resolveMarkType,
@@ -2870,6 +3178,7 @@ export {
2870
3178
  resolveTheme,
2871
3179
  scatterChart,
2872
3180
  simulateColorBlindness,
3181
+ terminal,
2873
3182
  textAscent,
2874
3183
  tileMap,
2875
3184
  truncateRotatedLabel,