@opendata-ai/openchart-core 8.3.1 → 8.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5117 -4636
- package/dist/index.js +519 -205
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
- package/schema/chart.schema.json +793 -22
- package/schema/table.schema.json +171 -9
- package/schema/vizspec.schema.json +1738 -81
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
|
|
468
|
-
"#
|
|
469
|
-
//
|
|
470
|
-
"#
|
|
471
|
-
//
|
|
472
|
-
"#
|
|
473
|
-
//
|
|
474
|
-
"#
|
|
475
|
-
//
|
|
476
|
-
"#
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
"#
|
|
481
|
-
//
|
|
482
|
-
"#
|
|
483
|
-
//
|
|
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",
|
|
@@ -913,9 +1231,11 @@ function getBreakpoint(width) {
|
|
|
913
1231
|
if (width <= BREAKPOINT_MEDIUM_MAX) return "medium";
|
|
914
1232
|
return "full";
|
|
915
1233
|
}
|
|
1234
|
+
var HEIGHT_MINIMAL_MAX = 100;
|
|
916
1235
|
var HEIGHT_CRAMPED_MAX = 200;
|
|
917
1236
|
var HEIGHT_SHORT_MAX = 350;
|
|
918
1237
|
function getHeightClass(height) {
|
|
1238
|
+
if (height < HEIGHT_MINIMAL_MAX) return "minimal";
|
|
919
1239
|
if (height < HEIGHT_CRAMPED_MAX) return "cramped";
|
|
920
1240
|
if (height <= HEIGHT_SHORT_MAX) return "short";
|
|
921
1241
|
return "normal";
|
|
@@ -953,10 +1273,10 @@ function getWidthStrategy(breakpoint) {
|
|
|
953
1273
|
}
|
|
954
1274
|
function applyHeightConstraints(strategy, heightClass) {
|
|
955
1275
|
if (heightClass === "normal") return strategy;
|
|
956
|
-
if (heightClass === "cramped") {
|
|
1276
|
+
if (heightClass === "minimal" || heightClass === "cramped") {
|
|
957
1277
|
return {
|
|
958
1278
|
...strategy,
|
|
959
|
-
chromeMode: "hidden",
|
|
1279
|
+
chromeMode: heightClass === "minimal" ? "hidden" : "compact",
|
|
960
1280
|
legendMaxHeight: 0,
|
|
961
1281
|
labelMode: "none",
|
|
962
1282
|
annotationPosition: "tooltip-only"
|
|
@@ -972,6 +1292,16 @@ function getLayoutStrategy(breakpoint, heightClass = "normal") {
|
|
|
972
1292
|
const base = getWidthStrategy(breakpoint);
|
|
973
1293
|
return applyHeightConstraints(base, heightClass);
|
|
974
1294
|
}
|
|
1295
|
+
var HEIGHT_NO_GRID_MAX = 150;
|
|
1296
|
+
var COMPACT_MAX_X_TICKS = 3;
|
|
1297
|
+
function resolveChromeEconomy(width, height) {
|
|
1298
|
+
const economy = {
|
|
1299
|
+
gridlines: height >= HEIGHT_NO_GRID_MAX,
|
|
1300
|
+
axes: height >= HEIGHT_CRAMPED_MAX
|
|
1301
|
+
};
|
|
1302
|
+
if (getBreakpoint(width) === "compact") economy.maxXTicks = COMPACT_MAX_X_TICKS;
|
|
1303
|
+
return economy;
|
|
1304
|
+
}
|
|
975
1305
|
|
|
976
1306
|
// src/responsive/metrics.ts
|
|
977
1307
|
var AXIS_TITLE_OFFSET_COMPACT = 35;
|
|
@@ -983,7 +1313,7 @@ var HPAD_COMPACT_FRACTION = 0.5;
|
|
|
983
1313
|
var HPAD_COMPACT_MIN = 4;
|
|
984
1314
|
var TICK_LABEL_OFFSET = 6;
|
|
985
1315
|
var AXIS_TITLE_TRAILING_PAD = 4;
|
|
986
|
-
var AXIS_TITLE_GAP =
|
|
1316
|
+
var AXIS_TITLE_GAP = 3;
|
|
987
1317
|
function axisTitleOffset(maxTickLabelWidth, titleFontSize, width, inline = false) {
|
|
988
1318
|
const halfTitleGlyph = Math.ceil(titleFontSize / 2);
|
|
989
1319
|
if (inline) {
|
|
@@ -1063,18 +1393,22 @@ function truncateRotatedLabel(label, tickAngle, tickFontSize, tickFontWeight, me
|
|
|
1063
1393
|
var DEFAULT_THEME = {
|
|
1064
1394
|
colors: {
|
|
1065
1395
|
categorical: [...CATEGORICAL_PALETTE],
|
|
1396
|
+
categoricalFill: [...CATEGORICAL_FILL_PALETTE],
|
|
1066
1397
|
sequential: SEQUENTIAL_PALETTES,
|
|
1067
1398
|
diverging: DIVERGING_PALETTES,
|
|
1068
1399
|
background: "transparent",
|
|
1069
1400
|
text: "#09090b",
|
|
1070
|
-
gridline: "rgba(0,0,0,0.
|
|
1401
|
+
gridline: "rgba(0,0,0,0.08)",
|
|
1402
|
+
// Structural hairline: axis lines, separators. Deliberately heavier than
|
|
1403
|
+
// the gridline and lighter than any ink, so structure reads as structure.
|
|
1404
|
+
hairline: "rgba(0,0,0,0.14)",
|
|
1071
1405
|
// Used for axis lines/ticks AND axis tick label fill. Must clear WCAG AA
|
|
1072
1406
|
// contrast (4.5:1) on white because tick labels are rendered with this
|
|
1073
1407
|
// color. Zinc-500 hits ~5.7:1.
|
|
1074
1408
|
axis: "#71717a",
|
|
1075
1409
|
annotationFill: "rgba(0,0,0,0.04)",
|
|
1076
1410
|
annotationText: "#71717a",
|
|
1077
|
-
positive: "#
|
|
1411
|
+
positive: "#15803d",
|
|
1078
1412
|
negative: "#dc2626"
|
|
1079
1413
|
},
|
|
1080
1414
|
fonts: {
|
|
@@ -1086,19 +1420,21 @@ var DEFAULT_THEME = {
|
|
|
1086
1420
|
body: 13,
|
|
1087
1421
|
small: 11,
|
|
1088
1422
|
axisTick: 11,
|
|
1089
|
-
metricLabel:
|
|
1423
|
+
metricLabel: 11,
|
|
1090
1424
|
metricValue: 22
|
|
1091
1425
|
},
|
|
1426
|
+
// One ladder everywhere: 400 regular / 500 medium (eyebrow, labels, UI,
|
|
1427
|
+
// values) / 600 semibold (title, series names, annotation lede) / 700 bold.
|
|
1092
1428
|
weights: {
|
|
1093
|
-
normal:
|
|
1094
|
-
medium:
|
|
1095
|
-
semibold:
|
|
1429
|
+
normal: 400,
|
|
1430
|
+
medium: 500,
|
|
1431
|
+
semibold: 600,
|
|
1096
1432
|
bold: 700
|
|
1097
1433
|
}
|
|
1098
1434
|
},
|
|
1099
1435
|
spacing: {
|
|
1100
1436
|
padding: 20,
|
|
1101
|
-
chromeGap:
|
|
1437
|
+
chromeGap: 6,
|
|
1102
1438
|
chromeToChart: 8,
|
|
1103
1439
|
chartToFooter: 8,
|
|
1104
1440
|
axisMargin: 6,
|
|
@@ -1110,17 +1446,18 @@ var DEFAULT_THEME = {
|
|
|
1110
1446
|
// so the gap holds regardless of font size.
|
|
1111
1447
|
xAxisLabelPadding: 4
|
|
1112
1448
|
},
|
|
1113
|
-
|
|
1449
|
+
/** Containers and tooltips. Marks carry their own 2px radius constant. */
|
|
1450
|
+
borderRadius: 8,
|
|
1114
1451
|
chrome: {
|
|
1115
1452
|
eyebrow: {
|
|
1116
1453
|
fontSize: 11,
|
|
1117
|
-
fontWeight:
|
|
1454
|
+
fontWeight: 500,
|
|
1118
1455
|
color: "#06b6d4",
|
|
1119
1456
|
lineHeight: 1.4
|
|
1120
1457
|
},
|
|
1121
1458
|
title: {
|
|
1122
1459
|
fontSize: 26,
|
|
1123
|
-
fontWeight:
|
|
1460
|
+
fontWeight: 600,
|
|
1124
1461
|
color: "#09090b",
|
|
1125
1462
|
lineHeight: 1.15
|
|
1126
1463
|
},
|
|
@@ -1149,6 +1486,8 @@ var DEFAULT_THEME = {
|
|
|
1149
1486
|
lineHeight: 1.4
|
|
1150
1487
|
}
|
|
1151
1488
|
},
|
|
1489
|
+
/** No editorial rule by default; the `broadsheet` preset sets one. */
|
|
1490
|
+
rule: null,
|
|
1152
1491
|
seriesStrategy: "palette"
|
|
1153
1492
|
};
|
|
1154
1493
|
|
|
@@ -1215,9 +1554,15 @@ function adaptTheme(theme) {
|
|
|
1215
1554
|
const lightChrome = DEFAULT_THEME.chrome;
|
|
1216
1555
|
const overridden = (current, lightDefault, darkDefault) => current === lightDefault ? darkDefault : current;
|
|
1217
1556
|
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.
|
|
1557
|
+
const darkGridline = pairs?.["colors.gridline"] ? pairs["colors.gridline"].dark : overridden(theme.colors.gridline, light.gridline, "rgba(255,255,255,0.06)");
|
|
1558
|
+
const darkHairline = pairs?.["colors.hairline"] ? pairs["colors.hairline"].dark : overridden(theme.colors.hairline, light.hairline, "rgba(255,255,255,0.14)");
|
|
1219
1559
|
const darkAxis = pairs?.["colors.axis"] ? pairs["colors.axis"].dark : overridden(theme.colors.axis, light.axis, "#a1a1aa");
|
|
1220
|
-
const categorical = theme.colors.categorical
|
|
1560
|
+
const categorical = overridden(theme.colors.categorical, light.categorical, [
|
|
1561
|
+
...CATEGORICAL_PALETTE_DARK
|
|
1562
|
+
]);
|
|
1563
|
+
const categoricalFill = overridden(theme.colors.categoricalFill, light.categoricalFill, [
|
|
1564
|
+
...CATEGORICAL_FILL_PALETTE_DARK
|
|
1565
|
+
]);
|
|
1221
1566
|
return {
|
|
1222
1567
|
...theme,
|
|
1223
1568
|
isDark: true,
|
|
@@ -1230,8 +1575,11 @@ function adaptTheme(theme) {
|
|
|
1230
1575
|
annotationFill: pairs?.["colors.annotationFill"] ? pairs["colors.annotationFill"].dark : overridden(theme.colors.annotationFill, light.annotationFill, "rgba(255,255,255,0.06)"),
|
|
1231
1576
|
annotationText: pairs?.["colors.annotationText"] ? pairs["colors.annotationText"].dark : overridden(theme.colors.annotationText, light.annotationText, darkMuted),
|
|
1232
1577
|
categorical,
|
|
1233
|
-
|
|
1234
|
-
|
|
1578
|
+
categoricalFill,
|
|
1579
|
+
hairline: darkHairline,
|
|
1580
|
+
positive: pairs?.["colors.positive"] ? pairs["colors.positive"].dark : overridden(theme.colors.positive, light.positive, "#4ade80"),
|
|
1581
|
+
negative: pairs?.["colors.negative"] ? pairs["colors.negative"].dark : overridden(theme.colors.negative, light.negative, "#f87171"),
|
|
1582
|
+
neutral: deriveNeutralRamp(darkText, darkBg, true)
|
|
1235
1583
|
},
|
|
1236
1584
|
chrome: {
|
|
1237
1585
|
// Eyebrow keeps its accent tint (cyan in both modes) unless the user
|
|
@@ -1261,7 +1609,10 @@ function adaptTheme(theme) {
|
|
|
1261
1609
|
...theme.chrome.footer,
|
|
1262
1610
|
color: pairs?.["chrome.footer.color"] ? pairs["chrome.footer.color"].dark : overridden(theme.chrome.footer.color, lightChrome.footer.color, darkMuted)
|
|
1263
1611
|
}
|
|
1264
|
-
}
|
|
1612
|
+
},
|
|
1613
|
+
// The editorial rule is a brand device: it keeps its authored color unless
|
|
1614
|
+
// the theme gave an explicit dark half of the pair.
|
|
1615
|
+
rule: theme.rule && pairs?.["rule.color"] ? { ...theme.rule, color: pairs["rule.color"].dark } : theme.rule
|
|
1265
1616
|
};
|
|
1266
1617
|
}
|
|
1267
1618
|
function adaptForLightLineStroke(color) {
|
|
@@ -1324,6 +1675,71 @@ var wire = {
|
|
|
1324
1675
|
},
|
|
1325
1676
|
borderRadius: 2
|
|
1326
1677
|
};
|
|
1678
|
+
var broadsheet = {
|
|
1679
|
+
colors: {
|
|
1680
|
+
categorical: ["#1d5f8a", "#e3120b", "#7fb0d3", "#e0a63b", "#3a8a6e", "#8c8680"],
|
|
1681
|
+
categoricalFill: ["#3b7097", "#ee493a", "#97c2e2", "#efbb64", "#579a81", "#9e9893"],
|
|
1682
|
+
background: { light: "#fffdf9", dark: "#171513" },
|
|
1683
|
+
text: { light: "#1a1714", dark: "#f4f1ea" },
|
|
1684
|
+
gridline: { light: "rgba(26,23,20,0.08)", dark: "rgba(244,241,234,0.07)" },
|
|
1685
|
+
// Heavier than the default 0.14: newsprint rules are drawn to be seen.
|
|
1686
|
+
hairline: { light: "rgba(26,23,20,0.28)", dark: "rgba(244,241,234,0.28)" },
|
|
1687
|
+
axis: { light: "#6b645c", dark: "#a8a099" },
|
|
1688
|
+
annotationFill: { light: "rgba(26,23,20,0.05)", dark: "rgba(244,241,234,0.07)" },
|
|
1689
|
+
annotationText: { light: "#6b645c", dark: "#a8a099" }
|
|
1690
|
+
},
|
|
1691
|
+
fonts: {
|
|
1692
|
+
sizes: { title: 24, subtitle: 14, body: 13, small: 11 }
|
|
1693
|
+
},
|
|
1694
|
+
spacing: {
|
|
1695
|
+
padding: 20,
|
|
1696
|
+
chromeGap: 8,
|
|
1697
|
+
chromeToChart: 16,
|
|
1698
|
+
chartToFooter: 12
|
|
1699
|
+
},
|
|
1700
|
+
chrome: {
|
|
1701
|
+
eyebrow: { color: { light: "#e3120b", dark: "#f4463f" }, fontSize: 11, fontWeight: 700 },
|
|
1702
|
+
title: { fontSize: 24, fontWeight: 700, lineHeight: 1.18 },
|
|
1703
|
+
subtitle: { fontWeight: 400, lineHeight: 1.45 }
|
|
1704
|
+
},
|
|
1705
|
+
/** The masthead device: a short red bar above the eyebrow. */
|
|
1706
|
+
rule: { color: { light: "#e3120b", dark: "#f4463f" }, width: 40, thickness: 3 },
|
|
1707
|
+
borderRadius: 0
|
|
1708
|
+
};
|
|
1709
|
+
var terminal = {
|
|
1710
|
+
colors: {
|
|
1711
|
+
categorical: [...CATEGORICAL_PALETTE_DARK],
|
|
1712
|
+
categoricalFill: [...CATEGORICAL_FILL_PALETTE_DARK],
|
|
1713
|
+
background: { light: "#0b0f14", dark: "#0b0f14" },
|
|
1714
|
+
text: { light: "#e6edf3", dark: "#e6edf3" },
|
|
1715
|
+
gridline: { light: "rgba(230,237,243,0.06)", dark: "rgba(230,237,243,0.06)" },
|
|
1716
|
+
hairline: { light: "rgba(230,237,243,0.14)", dark: "rgba(230,237,243,0.14)" },
|
|
1717
|
+
// 6.25:1 on #0b0f14 — tick labels are drawn in this color too.
|
|
1718
|
+
axis: { light: "#8b949e", dark: "#8b949e" },
|
|
1719
|
+
annotationFill: { light: "rgba(230,237,243,0.06)", dark: "rgba(230,237,243,0.06)" },
|
|
1720
|
+
annotationText: { light: "#8b949e", dark: "#8b949e" },
|
|
1721
|
+
positive: { light: "#4ade80", dark: "#4ade80" },
|
|
1722
|
+
negative: { light: "#f87171", dark: "#f87171" }
|
|
1723
|
+
},
|
|
1724
|
+
fonts: {
|
|
1725
|
+
family: '"Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1726
|
+
mono: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace',
|
|
1727
|
+
sizes: { title: 15, subtitle: 12, body: 12, small: 10, axisTick: 10 }
|
|
1728
|
+
},
|
|
1729
|
+
spacing: {
|
|
1730
|
+
padding: 12,
|
|
1731
|
+
chromeGap: 3,
|
|
1732
|
+
chromeToChart: 6,
|
|
1733
|
+
chartToFooter: 6
|
|
1734
|
+
},
|
|
1735
|
+
chrome: {
|
|
1736
|
+
eyebrow: { color: { light: "#22d3ee", dark: "#22d3ee" }, fontSize: 10, fontWeight: 500 },
|
|
1737
|
+
title: { fontSize: 15, fontWeight: 600, lineHeight: 1.3 },
|
|
1738
|
+
subtitle: { fontWeight: 400, lineHeight: 1.4 }
|
|
1739
|
+
},
|
|
1740
|
+
borderRadius: 4,
|
|
1741
|
+
seriesStrategy: "accent-neutral"
|
|
1742
|
+
};
|
|
1327
1743
|
|
|
1328
1744
|
// src/theme/resolve.ts
|
|
1329
1745
|
function deepMerge(target, source) {
|
|
@@ -1350,11 +1766,13 @@ function themeConfigToPartial(config) {
|
|
|
1350
1766
|
colors.categorical = config.colors;
|
|
1351
1767
|
} else {
|
|
1352
1768
|
if (config.colors.categorical) colors.categorical = config.colors.categorical;
|
|
1769
|
+
if (config.colors.categoricalFill) colors.categoricalFill = config.colors.categoricalFill;
|
|
1353
1770
|
if (config.colors.sequential) colors.sequential = config.colors.sequential;
|
|
1354
1771
|
if (config.colors.diverging) colors.diverging = config.colors.diverging;
|
|
1355
1772
|
if (config.colors.background) colors.background = resolveTokenLight(config.colors.background);
|
|
1356
1773
|
if (config.colors.text) colors.text = resolveTokenLight(config.colors.text);
|
|
1357
1774
|
if (config.colors.gridline) colors.gridline = resolveTokenLight(config.colors.gridline);
|
|
1775
|
+
if (config.colors.hairline) colors.hairline = resolveTokenLight(config.colors.hairline);
|
|
1358
1776
|
if (config.colors.axis) colors.axis = resolveTokenLight(config.colors.axis);
|
|
1359
1777
|
if (config.colors.annotationFill)
|
|
1360
1778
|
colors.annotationFill = resolveTokenLight(config.colors.annotationFill);
|
|
@@ -1363,6 +1781,9 @@ function themeConfigToPartial(config) {
|
|
|
1363
1781
|
if (config.colors.positive) colors.positive = resolveTokenLight(config.colors.positive);
|
|
1364
1782
|
if (config.colors.negative) colors.negative = resolveTokenLight(config.colors.negative);
|
|
1365
1783
|
}
|
|
1784
|
+
if (colors.categorical && !colors.categoricalFill) {
|
|
1785
|
+
colors.categoricalFill = colors.categorical;
|
|
1786
|
+
}
|
|
1366
1787
|
partial.colors = colors;
|
|
1367
1788
|
}
|
|
1368
1789
|
if (config.fonts) {
|
|
@@ -1408,6 +1829,13 @@ function themeConfigToPartial(config) {
|
|
|
1408
1829
|
}
|
|
1409
1830
|
partial.chrome = chrome;
|
|
1410
1831
|
}
|
|
1832
|
+
if (config.rule) {
|
|
1833
|
+
partial.rule = {
|
|
1834
|
+
color: resolveTokenLight(config.rule.color),
|
|
1835
|
+
width: config.rule.width,
|
|
1836
|
+
thickness: config.rule.thickness
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1411
1839
|
if (config.seriesStrategy) {
|
|
1412
1840
|
partial.seriesStrategy = config.seriesStrategy;
|
|
1413
1841
|
}
|
|
@@ -1420,6 +1848,7 @@ function collectTokenPairs(config) {
|
|
|
1420
1848
|
"background",
|
|
1421
1849
|
"text",
|
|
1422
1850
|
"gridline",
|
|
1851
|
+
"hairline",
|
|
1423
1852
|
"axis",
|
|
1424
1853
|
"annotationFill",
|
|
1425
1854
|
"annotationText",
|
|
@@ -1433,6 +1862,9 @@ function collectTokenPairs(config) {
|
|
|
1433
1862
|
}
|
|
1434
1863
|
}
|
|
1435
1864
|
}
|
|
1865
|
+
if (config.rule?.color && typeof config.rule.color === "object" && "light" in config.rule.color) {
|
|
1866
|
+
pairs["rule.color"] = config.rule.color;
|
|
1867
|
+
}
|
|
1436
1868
|
if (config.chrome) {
|
|
1437
1869
|
for (const [element, value] of Object.entries(config.chrome)) {
|
|
1438
1870
|
if (value && typeof value === "object" && "color" in value && value.color && typeof value.color === "object" && "light" in value.color) {
|
|
@@ -1491,9 +1923,9 @@ function adjustOpacity(hex, opacity) {
|
|
|
1491
1923
|
const r = parseInt(m[1].slice(0, 2), 16);
|
|
1492
1924
|
const g = parseInt(m[1].slice(2, 4), 16);
|
|
1493
1925
|
const b = parseInt(m[1].slice(4, 6), 16);
|
|
1494
|
-
const
|
|
1926
|
+
const mix2 = (c) => Math.round(c * opacity + 128 * (1 - opacity));
|
|
1495
1927
|
const toHex = (n) => n.toString(16).padStart(2, "0");
|
|
1496
|
-
return `#${toHex(
|
|
1928
|
+
return `#${toHex(mix2(r))}${toHex(mix2(g))}${toHex(mix2(b))}`;
|
|
1497
1929
|
}
|
|
1498
1930
|
function resolveTheme(userTheme, base = DEFAULT_THEME) {
|
|
1499
1931
|
let merged = userTheme ? deepMerge(base, themeConfigToPartial(userTheme)) : { ...base };
|
|
@@ -1523,6 +1955,10 @@ function resolveTheme(userTheme, base = DEFAULT_THEME) {
|
|
|
1523
1955
|
}
|
|
1524
1956
|
return {
|
|
1525
1957
|
...merged,
|
|
1958
|
+
colors: {
|
|
1959
|
+
...merged.colors,
|
|
1960
|
+
neutral: deriveNeutralRamp(merged.colors.text, merged.colors.background, dark)
|
|
1961
|
+
},
|
|
1526
1962
|
isDark: dark,
|
|
1527
1963
|
_tokenPairs: userTheme ? collectTokenPairs(userTheme) : void 0
|
|
1528
1964
|
};
|
|
@@ -1540,10 +1976,11 @@ function scaleFontSize(baseFontSize, width) {
|
|
|
1540
1976
|
const t = (width - 250) / 250;
|
|
1541
1977
|
return Math.max(Math.round(baseFontSize * (0.72 + t * 0.28)), 10);
|
|
1542
1978
|
}
|
|
1543
|
-
|
|
1979
|
+
var CRAMPED_TITLE_SCALE = 0.65;
|
|
1980
|
+
function buildTextStyle(defaults, fontFamily, textColor, width, overrides, extraScale = 1) {
|
|
1544
1981
|
const hasExplicitSize = overrides?.fontSize !== void 0;
|
|
1545
1982
|
const baseFontSize = overrides?.fontSize ?? defaults.fontSize;
|
|
1546
|
-
const fontSize = hasExplicitSize ? baseFontSize : scaleFontSize(baseFontSize, width);
|
|
1983
|
+
const fontSize = hasExplicitSize ? baseFontSize : Math.max(Math.round(scaleFontSize(baseFontSize, width) * extraScale), 10);
|
|
1547
1984
|
return {
|
|
1548
1985
|
fontFamily: overrides?.fontFamily ?? fontFamily,
|
|
1549
1986
|
fontSize,
|
|
@@ -1603,7 +2040,7 @@ function footnoteBandHeight(count, theme) {
|
|
|
1603
2040
|
if (count <= 0) return 0;
|
|
1604
2041
|
return count * theme.fonts.sizes.small * FOOTNOTE_LINE_HEIGHT + FOOTNOTE_BAND_GAP;
|
|
1605
2042
|
}
|
|
1606
|
-
function computeChrome(chrome, theme, width, measureText, chromeMode = "full", padding, watermark = true, bottomLegendHeight = 0) {
|
|
2043
|
+
function computeChrome(chrome, theme, width, measureText, chromeMode = "full", padding, watermark = true, bottomLegendHeight = 0, crampedTitle = false) {
|
|
1607
2044
|
if (!chrome || chromeMode === "hidden") {
|
|
1608
2045
|
if (watermark && width >= BRAND_MIN_WIDTH) {
|
|
1609
2046
|
const hiddenPad = padding ?? theme.spacing.padding;
|
|
@@ -1626,6 +2063,19 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1626
2063
|
let topY = pad;
|
|
1627
2064
|
const topElements = {};
|
|
1628
2065
|
const eyebrowNorm = chromeMode === "compact" ? null : normalizeChromeText(chrome.eyebrow);
|
|
2066
|
+
const titleNormPeek = normalizeChromeText(chrome.title);
|
|
2067
|
+
const subtitleNormPeek = chromeMode === "compact" ? null : normalizeChromeText(chrome.subtitle);
|
|
2068
|
+
let ruleElement;
|
|
2069
|
+
if (theme.rule && (eyebrowNorm || titleNormPeek || subtitleNormPeek)) {
|
|
2070
|
+
ruleElement = {
|
|
2071
|
+
x: pad,
|
|
2072
|
+
y: topY,
|
|
2073
|
+
width: theme.rule.width,
|
|
2074
|
+
thickness: theme.rule.thickness,
|
|
2075
|
+
color: theme.rule.color
|
|
2076
|
+
};
|
|
2077
|
+
topY += theme.rule.thickness + chromeGap;
|
|
2078
|
+
}
|
|
1629
2079
|
if (eyebrowNorm) {
|
|
1630
2080
|
const style = buildTextStyle(
|
|
1631
2081
|
theme.chrome.eyebrow,
|
|
@@ -1647,14 +2097,15 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1647
2097
|
topElements.eyebrow = element;
|
|
1648
2098
|
topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
|
|
1649
2099
|
}
|
|
1650
|
-
const titleNorm =
|
|
2100
|
+
const titleNorm = titleNormPeek;
|
|
1651
2101
|
if (titleNorm) {
|
|
1652
2102
|
const style = buildTextStyle(
|
|
1653
2103
|
theme.chrome.title,
|
|
1654
2104
|
fontFamily,
|
|
1655
2105
|
theme.chrome.title.color,
|
|
1656
2106
|
width,
|
|
1657
|
-
titleNorm.style
|
|
2107
|
+
titleNorm.style,
|
|
2108
|
+
crampedTitle ? CRAMPED_TITLE_SCALE : 1
|
|
1658
2109
|
);
|
|
1659
2110
|
const maxLines = titleNorm.style?.maxLines;
|
|
1660
2111
|
const lineCount = estimateLineCount(titleNorm.text, style, maxWidth, measureText, maxLines);
|
|
@@ -1669,7 +2120,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1669
2120
|
topElements.title = element;
|
|
1670
2121
|
topY += estimateTextHeight(style.fontSize, lineCount, style.lineHeight) + chromeGap;
|
|
1671
2122
|
}
|
|
1672
|
-
const subtitleNorm =
|
|
2123
|
+
const subtitleNorm = subtitleNormPeek;
|
|
1673
2124
|
if (subtitleNorm) {
|
|
1674
2125
|
const style = buildTextStyle(
|
|
1675
2126
|
theme.chrome.subtitle,
|
|
@@ -1705,6 +2156,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1705
2156
|
return {
|
|
1706
2157
|
topHeight,
|
|
1707
2158
|
bottomHeight: compactBottom,
|
|
2159
|
+
...ruleElement ? { rule: ruleElement } : {},
|
|
1708
2160
|
...topElements
|
|
1709
2161
|
};
|
|
1710
2162
|
}
|
|
@@ -1811,6 +2263,7 @@ function computeChrome(chrome, theme, width, measureText, chromeMode = "full", p
|
|
|
1811
2263
|
return {
|
|
1812
2264
|
topHeight,
|
|
1813
2265
|
bottomHeight,
|
|
2266
|
+
...ruleElement ? { rule: ruleElement } : {},
|
|
1814
2267
|
...topElements,
|
|
1815
2268
|
...bottomElements,
|
|
1816
2269
|
...brandElement ? { brand: brandElement } : {}
|
|
@@ -2072,6 +2525,12 @@ var D3_FORMAT_SUFFIX_RE = /^(.*~?[efgsrdxXobcnp%])(.+)$/;
|
|
|
2072
2525
|
function buildD3Formatter(formatStr) {
|
|
2073
2526
|
if (!formatStr) return null;
|
|
2074
2527
|
if (formatStr === "ordinal") return formatOrdinal;
|
|
2528
|
+
if (formatStr === "compact") {
|
|
2529
|
+
return (v) => {
|
|
2530
|
+
if (!Number.isFinite(v)) return String(v);
|
|
2531
|
+
return Math.abs(v) >= 1e3 ? abbreviateNumber(v) : formatNumber(v);
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2075
2534
|
try {
|
|
2076
2535
|
const fmt = d3Format(formatStr);
|
|
2077
2536
|
if (formatStr.includes("s")) {
|
|
@@ -2142,6 +2601,12 @@ function isYearContext(ctx) {
|
|
|
2142
2601
|
function resolveNumberFormatter(formatStr, ctx) {
|
|
2143
2602
|
if (!formatStr) return null;
|
|
2144
2603
|
if (formatStr === "ordinal") return formatOrdinal;
|
|
2604
|
+
if (formatStr === "compact") {
|
|
2605
|
+
return (v) => {
|
|
2606
|
+
if (!Number.isFinite(v)) return String(v);
|
|
2607
|
+
return Math.abs(v) >= 1e3 ? abbreviateNumber(v) : formatNumber(v);
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2145
2610
|
if (formatStr === "percent") {
|
|
2146
2611
|
const fraction = ctx?.extent ? Math.max(Math.abs(ctx.extent[0]), Math.abs(ctx.extent[1])) <= 1 : true;
|
|
2147
2612
|
return (v) => formatPercent(v, { fraction });
|
|
@@ -2583,168 +3048,6 @@ function deepMergeSpec(target, source) {
|
|
|
2583
3048
|
}
|
|
2584
3049
|
return result;
|
|
2585
3050
|
}
|
|
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
3051
|
export {
|
|
2749
3052
|
ACHROMATIC_RAMP,
|
|
2750
3053
|
AXIS_TITLE_GAP,
|
|
@@ -2756,7 +3059,12 @@ export {
|
|
|
2756
3059
|
BRAND_RESERVE_WIDTH,
|
|
2757
3060
|
BREAKPOINT_COMPACT_MAX,
|
|
2758
3061
|
BREAKPOINT_MEDIUM_MAX,
|
|
3062
|
+
CATEGORICAL_EXTENDED_PALETTE,
|
|
3063
|
+
CATEGORICAL_FILL_PALETTE,
|
|
3064
|
+
CATEGORICAL_FILL_PALETTE_DARK,
|
|
2759
3065
|
CATEGORICAL_PALETTE,
|
|
3066
|
+
CATEGORICAL_PALETTE_DARK,
|
|
3067
|
+
COMPACT_MAX_X_TICKS,
|
|
2760
3068
|
COMPACT_WIDTH,
|
|
2761
3069
|
CSS_TOKENS,
|
|
2762
3070
|
CSS_TOKEN_ROOT_SELECTORS,
|
|
@@ -2766,6 +3074,8 @@ export {
|
|
|
2766
3074
|
FOOTNOTE_LINE_HEIGHT,
|
|
2767
3075
|
GRAPH_ENCODING_RULES,
|
|
2768
3076
|
HEIGHT_CRAMPED_MAX,
|
|
3077
|
+
HEIGHT_MINIMAL_MAX,
|
|
3078
|
+
HEIGHT_NO_GRID_MAX,
|
|
2769
3079
|
HEIGHT_SHORT_MAX,
|
|
2770
3080
|
HPAD_COMPACT_FRACTION,
|
|
2771
3081
|
HPAD_COMPACT_MIN,
|
|
@@ -2799,6 +3109,7 @@ export {
|
|
|
2799
3109
|
areaChart,
|
|
2800
3110
|
axisTitleOffset,
|
|
2801
3111
|
barChart,
|
|
3112
|
+
broadsheet,
|
|
2802
3113
|
buildCompactStepFormatter,
|
|
2803
3114
|
buildD3Formatter,
|
|
2804
3115
|
buildTemporalFormatter,
|
|
@@ -2813,6 +3124,7 @@ export {
|
|
|
2813
3124
|
dataTable,
|
|
2814
3125
|
deepMergeSpec,
|
|
2815
3126
|
defaultNumberFormatter,
|
|
3127
|
+
deriveNeutralRamp,
|
|
2816
3128
|
detectCollision,
|
|
2817
3129
|
donutChart,
|
|
2818
3130
|
dotChart,
|
|
@@ -2861,6 +3173,7 @@ export {
|
|
|
2861
3173
|
overlapArea,
|
|
2862
3174
|
pickLabelColor,
|
|
2863
3175
|
pieChart,
|
|
3176
|
+
resolveChromeEconomy,
|
|
2864
3177
|
resolveCollisions,
|
|
2865
3178
|
resolveMarkDef,
|
|
2866
3179
|
resolveMarkType,
|
|
@@ -2870,6 +3183,7 @@ export {
|
|
|
2870
3183
|
resolveTheme,
|
|
2871
3184
|
scatterChart,
|
|
2872
3185
|
simulateColorBlindness,
|
|
3186
|
+
terminal,
|
|
2873
3187
|
textAscent,
|
|
2874
3188
|
tileMap,
|
|
2875
3189
|
truncateRotatedLabel,
|