chart-factory 0.1.2

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.
@@ -0,0 +1,792 @@
1
+ /**
2
+ * Chart Factory - Shared Design Tokens
3
+ *
4
+ * These CSS custom properties define the visual foundation for all D3 components.
5
+ * Override these in your own stylesheet to customize the appearance.
6
+ *
7
+ * Usage:
8
+ * @import 'chart-factory/core/tokens.css';
9
+ *
10
+ * :root {
11
+ * --font-family: 'Your Font', sans-serif;
12
+ * --color-text-primary: #222;
13
+ * }
14
+ */
15
+
16
+ :root {
17
+ /* ===========================
18
+ SPACING
19
+ =========================== */
20
+ --space-0: 0;
21
+ --space-1: 4px;
22
+ --space-2: 8px;
23
+ --space-3: 12px;
24
+ --space-4: 15px;
25
+ --space-5: 20px;
26
+ --space-6: 25px;
27
+ --space-7: 35px;
28
+ --space-8: 40px;
29
+
30
+ /* ===========================
31
+ TYPOGRAPHY
32
+ =========================== */
33
+ /* Default: System font stack */
34
+ --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
35
+ /* Alternative: Open Sans (uncomment and add Google Font import to use)
36
+ * Add to <head>: <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap" rel="stylesheet">
37
+ * --font-family: 'Open Sans', sans-serif;
38
+ */
39
+
40
+ /* Font Sizes */
41
+ --font-size-xs: 9px;
42
+ --font-size-sm: 10px;
43
+ --font-size-base: 11px;
44
+ --font-size-md: 12px;
45
+ --font-size-lg: 13px;
46
+ --font-size-xl: 14px;
47
+ --font-size-title: 28px;
48
+ --font-size-subtitle: 13px;
49
+ /* Chart-shell text hierarchy (decided from live mockups 2026-07-18):
50
+ card tier 15/11 (ratio 1.36), featured tier 20/14 (ratio 1.43).
51
+ Title size tracks reading context, not chart size. */
52
+ --shell-title-size: 15px;
53
+ --shell-subtitle-size: var(--font-size-base);
54
+ --shell-featured-title-size: 20px;
55
+ --shell-featured-subtitle-size: 14px;
56
+
57
+ /* Font Weights */
58
+ --font-weight-normal: 400;
59
+ --font-weight-semibold: 600;
60
+ --font-weight-bold: 700;
61
+
62
+ /* Letter Spacing */
63
+ --letter-spacing-tight: -0.3px;
64
+ --letter-spacing-wide: 0.8px;
65
+ --letter-spacing-wider: 1px;
66
+
67
+ /* Line Height */
68
+ --line-height-normal: 1.6;
69
+
70
+ /* ===========================
71
+ COLORS
72
+ =========================== */
73
+
74
+ /* Text Colors */
75
+ --color-black: #000;
76
+ --color-text-primary: #333;
77
+ --color-text-muted: #666;
78
+ --color-text-subtle: #767676;
79
+ --color-text-faint: #999;
80
+ --color-text-disabled: #aaa;
81
+
82
+ /* Background Colors */
83
+ --color-bg-body: #f8f8f8;
84
+ --color-bg-container: white;
85
+ --color-bg-hover: #fafafa;
86
+ --color-bg-highlight: #f0f0f0;
87
+
88
+ /* Border Colors */
89
+ --color-border-header: #000;
90
+ --color-border-row: #f0f0f0;
91
+ --color-border-light: #e5e5e5; /* light axis/grid strokes */
92
+ --color-border-footer: #e8e8e8;
93
+ --color-border-tooltip: #ddd;
94
+
95
+ /* Shadows */
96
+ --shadow-container: 0 1px 3px rgba(0, 0, 0, 0.08);
97
+ --shadow-tooltip: 0 2px 8px rgba(0, 0, 0, 0.15);
98
+
99
+ /* ===========================
100
+ ANIMATIONS
101
+ =========================== */
102
+ --animation-duration-fast: 200ms;
103
+ --animation-duration-normal: 500ms;
104
+ --animation-duration-slow: 1000ms;
105
+ --animation-stagger-delay: 50ms;
106
+
107
+ /* ===========================
108
+ TOOLTIPS
109
+ =========================== */
110
+ --tooltip-z-index: 9999; /* above typical host-page modals/sticky chrome */
111
+ --tooltip-font-size: 12px; /* tooltip body (independent of chart axis text) */
112
+ --tooltip-title-font-size: 15px; /* ~1.25x body for clear title dominance */
113
+ --tooltip-row-gap: 4px; /* vertical rhythm between shell rows */
114
+ --tooltip-padding: 10px 13px;
115
+ --tooltip-border-radius: 6px;
116
+ --tooltip-min-width: 130px;
117
+ --tooltip-chart-height: 50px;
118
+ --tooltip-bar-gap: var(--space-1);
119
+ --tooltip-bar-min-width: var(--space-2);
120
+ --tooltip-bar-border-radius: 2px;
121
+ --tooltip-label-offset-y: 18px;
122
+ --tooltip-value-offset-y: 18px;
123
+
124
+ /* ===========================
125
+ CHART STORY (story: config — narrative summary module under the chart)
126
+ =========================== */
127
+ --story-font-size: var(--font-size-lg); /* body text of all three variants */
128
+ --story-line-height: 1.6;
129
+ /* Accent drives the panel tint/border, the bold lead-in label, and band
130
+ <b>/<strong> emphasis. The per-chart `accent` option overrides it via an
131
+ inline custom property on the module. */
132
+ --story-accent: var(--chart-primary);
133
+ /* Accent share in the panel wash / hairline (color-mix percentages) */
134
+ --story-panel-bg-alpha: 7%;
135
+ --story-panel-border-alpha: 25%;
136
+ /* Band variant: full-width dark lede strip */
137
+ --story-band-bg: #1f2937;
138
+ --story-band-text: #e5e7eb;
139
+ --story-band-label: #9ca3af; /* overline label tint on the band */
140
+
141
+ /* ===========================
142
+ RESPONSIVE
143
+ =========================== */
144
+ --container-padding: var(--space-8); /* .d3-container inner padding — the one knob for card breathing room */
145
+ --container-padding-mobile: var(--space-6) var(--space-5);
146
+ --title-font-size-mobile: 22px;
147
+
148
+ /* ===========================
149
+ CHART SHARED
150
+ =========================== */
151
+
152
+ /* Primary chart color — single-series default (line/area/bar/scatter,
153
+ forecast/actual lines, series-3). Brand blue, kept distinct from the Set2
154
+ categorical palette used for multi-series charts. */
155
+ --chart-primary: #3b82f6;
156
+ --chart-primary-light: rgba(59, 130, 246, 0.3);
157
+ --chart-primary-faint: rgba(59, 130, 246, 0.1);
158
+
159
+ /* Secondary chart colors */
160
+ --chart-secondary: #22c55e; /* line palette slot 2 (getLineColors) */
161
+ --chart-tertiary: #f97316; /* line palette slot 3 (getLineColors) */
162
+ --chart-green: #22c55e;
163
+ --chart-lime: #84cc16; /* mid-tier threshold color (progress bars) */
164
+ --chart-orange: #f97316;
165
+ --chart-purple: #a855f7;
166
+ --chart-purple-light: rgba(168, 85, 247, 0.4);
167
+ --chart-purple-faint: rgba(168, 85, 247, 0.05);
168
+ --chart-red: #ef4444;
169
+
170
+ /* Multi-series palette (for YoY, comparisons) */
171
+ --chart-series-1: #94a3b8;
172
+ --chart-series-2: #64748b;
173
+ --chart-series-3: var(--chart-primary);
174
+
175
+ /* Semantic chart colors */
176
+ --chart-trend-up: var(--chart-green);
177
+ --chart-trend-down: var(--chart-red);
178
+ --chart-anomaly: var(--chart-red);
179
+ --chart-forecast: var(--chart-primary);
180
+ --chart-actual: var(--chart-primary);
181
+ --chart-budget: var(--chart-green);
182
+
183
+ /* Neutral/gray for cumulative/baseline lines */
184
+ --chart-neutral: #6b7280;
185
+ --chart-neutral-light: rgba(107, 114, 128, 0.4);
186
+ --chart-neutral-faint: rgba(107, 114, 128, 0.05);
187
+
188
+ /* Axes */
189
+ --axis-color: var(--color-text-muted);
190
+ --axis-font-size: var(--font-size-xs);
191
+ --axis-label-font-size: var(--font-size-sm);
192
+ --axis-tick-size: 5px;
193
+ --axis-tick-padding: 3px;
194
+
195
+ /* Grid */
196
+ --grid-color: var(--color-border-row);
197
+ --grid-opacity: 0.5;
198
+ --grid-stroke-opacity: 0.5;
199
+
200
+ /* Hover states */
201
+ --hover-stroke-color: var(--color-text-primary);
202
+ --hover-stroke-width: 1;
203
+ --hover-dash-pattern: 3, 3;
204
+ --link-dot-radius: 3.5; /* snapped dot size on linked-crosshair (linkGroup) charts */
205
+ --brush-selection-opacity: 0.08; /* fill opacity of the brush selection window */
206
+ --brush-veil-opacity: 0.65; /* opacity of the outside-selection veil on linked charts */
207
+
208
+ /* Lines */
209
+ --line-stroke-width: 2;
210
+ --line-stroke-width-thin: 1;
211
+ --line-stroke-width-hover: 3;
212
+ --line-opacity: 1;
213
+ --line-opacity-muted: 0.3;
214
+ --line-dot-stroke-width: 2;
215
+ --line-transition-duration: 150;
216
+
217
+ /* Areas */
218
+ --area-opacity: 0.15;
219
+ --area-opacity-light: 0.1;
220
+ --area-gradient-start: 0.3;
221
+ --area-gradient-end: 0.02;
222
+
223
+ /* Points */
224
+ --point-radius: 4px;
225
+ --point-radius-small: 3px;
226
+ --point-radius-hover: 6px;
227
+ --point-stroke-width: 2px;
228
+
229
+ /* Confidence bands */
230
+ --band-opacity: 0.15;
231
+ --band-border-opacity: 0.3;
232
+
233
+ /* Anomaly indicators */
234
+ --anomaly-pulse-radius: 4;
235
+ --anomaly-dot-radius: 3;
236
+
237
+ /* Legend */
238
+ --legend-swatch-size: 12px;
239
+ --legend-line-width: 24px;
240
+ --legend-line-height: 2px;
241
+ --legend-gap: var(--space-4);
242
+ --legend-font-size: var(--font-size-sm);
243
+ /* Interactive legend: opacity of non-highlighted series when one is
244
+ hovered/isolated (chosen treatment: dim others, highlighted unchanged). */
245
+ --legend-dim-opacity: 0.18;
246
+
247
+ /* Trend indicators (legacy aliases) */
248
+ --trend-up-color: var(--chart-trend-up);
249
+ --trend-down-color: var(--chart-trend-down);
250
+ --trend-neutral-color: var(--color-text-muted);
251
+
252
+ /* Animation */
253
+ --chart-animation-duration: 2s;
254
+ --chart-pulse-min-opacity: 0;
255
+ --chart-pulse-max-opacity: 0.6;
256
+
257
+ /* Annotations: fade-in (ms) when an annotation waits for its anchor
258
+ mark's entrance animation before appearing */
259
+ --annotation-fade-duration: 200;
260
+
261
+ /* Event annotations (type: 'event'): tick + label band below the x-axis */
262
+ --annotation-event-band-offset: 22; /* px from plot bottom to the event ticks */
263
+ --annotation-event-tick-height: 8; /* event tick length */
264
+ --annotation-event-font-size: 10px; /* event label size */
265
+ --annotation-event-guide-color: #d1d5db; /* dotted in-plot guide (guide: true) */
266
+
267
+ /* Point/callout annotations: editorial two-line callouts + leader lines */
268
+ --annotation-title-size: 13px; /* titled callout value line (`title`) */
269
+ --annotation-leader-threshold: 14; /* px dot-to-label gap before an auto leader draws */
270
+
271
+ /* ===========================
272
+ SLOPE CHART SPECIFIC
273
+ =========================== */
274
+
275
+ /* Slope line widths */
276
+ --slope-line-width: var(--line-stroke-width);
277
+ --slope-line-width-hover: 3;
278
+
279
+ /* Slope point sizing */
280
+ --slope-point-radius: var(--point-radius);
281
+ --slope-point-radius-hover: 7px;
282
+ --slope-gap-radius: 6px;
283
+
284
+ /* Slope interaction states */
285
+ --slope-dimmed-opacity: 0.2;
286
+ --slope-transition-duration: 0.2s;
287
+
288
+ /* Slope animation */
289
+ --slope-animation-duration: 800ms;
290
+ --slope-stagger-delay: 80ms;
291
+ --slope-fade-duration: 300ms;
292
+
293
+ /* Slope highlight mode */
294
+ --slope-highlight-color: var(--chart-orange);
295
+ --slope-muted-color: #94a3b8;
296
+
297
+ /* Slope label dodge: minimum vertical gap between stacked side labels */
298
+ --slope-label-min-gap: 15;
299
+ /* Two-line right labels (createIndexed) need roughly double the gap */
300
+ --slope-label-min-gap-stacked: 34;
301
+
302
+ /* Indexed slope: hairline at the origin/no-change value */
303
+ --slope-baseline-color: var(--color-border-light);
304
+
305
+ /* Dodged-label leader lines (defaults to the hairline border color) */
306
+ --slope-leader-color: var(--color-border-light);
307
+
308
+ /* Slope band colors (quartiles, from bottom to top) */
309
+ --slope-band-1: #fef2f2;
310
+ --slope-band-2: #fefce8;
311
+ --slope-band-3: #f0fdf4;
312
+ --slope-band-4: #eff6ff;
313
+
314
+ /* ===========================
315
+ AREA CHART SPECIFIC
316
+ =========================== */
317
+
318
+ /* Area fill opacity */
319
+ --area-fill-opacity: 0.35;
320
+ --area-fill-opacity-hover: 0.5;
321
+ --area-fill-opacity-muted: 0.15;
322
+
323
+ /* Area stroke */
324
+ --area-stroke-width: 1.5;
325
+ --area-stroke-opacity: 1;
326
+
327
+ /* Categorical series palette — Set2 (house default, CVD-safe, muted).
328
+ Drives getAreaColors()/getLineColors(): stacked areas, multi-line, grouped/
329
+ stacked bars, donut, dot, scatter categories. Single-series charts use
330
+ --chart-primary, not these. Override any of these to restyle every
331
+ multi-series chart at once. */
332
+ --area-color-1: #66c2a5;
333
+ --area-color-2: #fc8d62;
334
+ --area-color-3: #8da0cb;
335
+ --area-color-4: #e78ac3;
336
+ --area-color-5: #a6d854;
337
+ --area-color-6: #ffd92f;
338
+ --area-color-7: #e5c494;
339
+ --area-color-8: #b3b3b3;
340
+
341
+ /* Area interaction */
342
+ --area-transition-duration: 0.2s;
343
+ --area-dimmed-opacity: 0.2;
344
+
345
+ /* Area animation */
346
+ --area-animation-duration: 1000ms;
347
+ --area-stagger-delay: 100ms;
348
+
349
+ /* Area gradient (for single-series with gradient fill) */
350
+ --area-gradient-opacity-top: 0.3;
351
+ --area-gradient-opacity-bottom: 0.02;
352
+
353
+ /* Smoothing curve tension (0 = linear, 1 = very smooth) */
354
+ --area-curve-tension: 0.4;
355
+
356
+ /* ===========================
357
+ STREAMGRAPH SPECIFIC
358
+ =========================== */
359
+
360
+ /* Stream layer fill */
361
+ --stream-fill-opacity: 1;
362
+ --stream-stroke-color: #fff;
363
+ --stream-stroke-width: 1;
364
+
365
+ /* Stream interaction */
366
+ --stream-dimmed-opacity: 0.25;
367
+
368
+ /* Stream labels */
369
+ --stream-label-size: 11;
370
+
371
+ /* ===========================
372
+ DOT CHART SPECIFIC
373
+ =========================== */
374
+
375
+ /* Dot sizing */
376
+ --dot-radius: 5px;
377
+ --dot-radius-hover: 7px;
378
+
379
+ /* Connector line (dumbbell) */
380
+ --dot-connector-width: 2.5;
381
+ --dot-connector-color: var(--color-text-disabled);
382
+
383
+ /* Row spacing */
384
+ --dot-row-height: 36px;
385
+ --dot-row-padding: 0.3;
386
+
387
+ /* Header positioning */
388
+ --dot-header-offset: 12px;
389
+
390
+ /* ===========================
391
+ COLOR TEMPERATURE PALETTE
392
+ =========================== */
393
+ /* Based on Kelvin color temperature scale
394
+ * Cold (blue) → Warm (amber)
395
+ * Useful for percentile bands, heat maps, diverging scales */
396
+
397
+ --temp-cold-10: #7AB8FF; /* 10000K - deep cold blue */
398
+ --temp-cold-9: #9CCAFF; /* 9000K - cold blue */
399
+ --temp-cold-8: #B8D9FF; /* 8000K - sky blue */
400
+ --temp-cold-7: #D6E8FF; /* 7000K - pale blue */
401
+ --temp-neutral: #F2F7FF; /* 6000K - cool white */
402
+ --temp-warm-5: #FFF1C1; /* 5000K - neutral daylight */
403
+ --temp-warm-4: #FFD27F; /* 4000K - warm white */
404
+ --temp-warm-3: #FFB347; /* 3000K - tungsten */
405
+ --temp-warm-2: #FF8C1A; /* 2000K - warm orange */
406
+ --temp-warm-1: #FF6A00; /* 1000K - deep amber */
407
+
408
+ /* ===========================
409
+ SCATTER PLOT / TRAJECTORY
410
+ =========================== */
411
+
412
+ /* Trail length (number of time units to show in trail) */
413
+ --scatter-trail-length: 5;
414
+
415
+ /* Trail styling */
416
+ --scatter-trail-width: 3;
417
+ --scatter-trail-width-hover: 4;
418
+ --scatter-trail-opacity: 0.7;
419
+ --scatter-trail-opacity-dimmed: 0.08;
420
+
421
+ /* Trail gradient (fade from start to end) */
422
+ --scatter-trail-gradient-start: 0.1;
423
+ --scatter-trail-gradient-end: 0.9;
424
+
425
+ /* Dot sizing */
426
+ --scatter-dot-radius: 6;
427
+ --scatter-dot-radius-hover: 10;
428
+ --scatter-dot-stroke-width: 2;
429
+
430
+ /* Start dot (trail origin marker) */
431
+ --scatter-start-dot-radius: 4;
432
+ --scatter-start-dot-stroke-width: 1.5;
433
+ --scatter-start-dot-opacity: 0.8;
434
+
435
+ /* Animation timing */
436
+ --scatter-animation-speed: 1000;
437
+
438
+ /* Quadrant chart */
439
+ --quadrant-line-width: 1.5;
440
+ --quadrant-bg-opacity: 0.08;
441
+
442
+ /* Density contour / KDE outlines (dark theme flips these to white-based) */
443
+ --contour-line-color: rgba(0, 0, 0, 0.15);
444
+ --kde-contour-line-color: rgba(0, 0, 0, 0.1);
445
+
446
+ /* ===========================
447
+ VOCABULARY FAMILIES (Release C)
448
+ =========================== */
449
+
450
+ /* Histogram */
451
+ --histogram-bar-gap: 1; /* px gap between bin bars */
452
+ --histogram-overlay-opacity: 0.55; /* overlay-series fill opacity */
453
+
454
+ /* Donut */
455
+ --donut-inner-ratio: 0.62; /* inner radius share; 0 = pie */
456
+ --donut-pad-angle: 0.008; /* radians between slices */
457
+ --donut-corner-radius: 2; /* px slice corner rounding */
458
+
459
+ /* Box plot */
460
+ --box-fill-opacity: 0.35; /* box body fill vs stroke */
461
+ --box-median-width: 2.5; /* median line stroke width */
462
+ --box-outlier-radius: 3; /* px outlier ring radius */
463
+
464
+ /* Calendar heatmap */
465
+ --calendar-cell-radius: 2; /* px cell corner rounding */
466
+ --calendar-month-gap: 3; /* px extra gutter at month starts */
467
+ --calendar-max-cell: 22; /* px step cap for the 7-col vertical layout */
468
+
469
+ /* Sankey */
470
+ --sankey-node-width: 12; /* px node bar width */
471
+ --sankey-node-padding: 12; /* px vertical gap between nodes */
472
+ --sankey-link-opacity: 0.3; /* link ribbon opacity (hover +0.25) */
473
+ --sankey-anim-duration: 900; /* ms each column's links take to draw */
474
+ --sankey-anim-pause: 300; /* ms the flow rests at a node before flowing on */
475
+
476
+ /* ===========================
477
+ BUMP CHART SPECIFIC
478
+ =========================== */
479
+
480
+ /* Layout */
481
+ --bump-margin-top: 30;
482
+ --bump-margin-bottom: 40;
483
+ --bump-label-gap: 10;
484
+ --bump-label-padding: 5;
485
+ --bump-axis-offset: 15;
486
+
487
+ /* Line styling */
488
+ --bump-line-width: 2.5;
489
+ --bump-line-width-hover: 3.5;
490
+
491
+ /* Dot styling */
492
+ --bump-dot-radius: 8;
493
+ --bump-dot-radius-hover: 10;
494
+ --bump-dot-stroke-width: 2;
495
+ --bump-rank-font-size: 10px;
496
+
497
+ /* Interaction states */
498
+ --bump-dimmed-opacity: 0.15;
499
+ --bump-transition-duration: 0.2s;
500
+
501
+ /* Animation */
502
+ --bump-animation-duration: 800;
503
+ --bump-stagger-delay: 100;
504
+ --bump-dot-fade-duration: 300;
505
+
506
+ /* ===========================
507
+ SEQUENTIAL COLOR SCALES (bars/heatmaps — createColorScale)
508
+ =========================== */
509
+ --scale-orange-lo: #fff5eb;
510
+ --scale-orange-hi: #ff6b35;
511
+ --scale-orange-red-hi: #d7191c;
512
+ --scale-blue-lo: #e0f3f8;
513
+ --scale-blue-hi: #4575b4;
514
+ --scale-teal-hi: #1a9988;
515
+ --scale-aqua-1: #a4d7e1;
516
+ --scale-aqua-2: #4ec1d4;
517
+ --scale-aqua-3: #0095b7;
518
+ --scale-aqua-4: #005e8b;
519
+ --scale-gray-lo: #f5f5f5;
520
+ --scale-gray-hi: #333333;
521
+ --scale-purple-1: #f3e5f5;
522
+ --scale-purple-2: #ce93d8;
523
+ --scale-purple-3: #ab47bc;
524
+ --scale-purple-4: #7b1fa2;
525
+ --scale-green-1: #e8f5e9;
526
+ --scale-green-2: #a5d6a7;
527
+ --scale-green-3: #66bb6a;
528
+ --scale-green-4: #2e7d32;
529
+ /* Diagonal band scales (Scatter.createDiagonal) — white center outward */
530
+ --scale-diag-green-1: #ffffff;
531
+ --scale-diag-green-2: #c6f6d5;
532
+ --scale-diag-green-3: #68d391;
533
+ --scale-diag-green-4: #38a169;
534
+ --scale-diag-red-1: #ffffff;
535
+ --scale-diag-red-2: #fed7d7;
536
+ --scale-diag-red-3: #fc8181;
537
+ --scale-diag-red-4: #e53e3e;
538
+
539
+ /* ===========================
540
+ BAR CHART SPECIFIC
541
+ =========================== */
542
+
543
+ /* Bar sizing */
544
+ --bar-height: 24px;
545
+ --bar-height-mini: 18px;
546
+ --bar-gap: 8px;
547
+ --bar-border-radius: 3px;
548
+
549
+ /* Zero baseline on mixed-sign bar charts (tracks the dark theme via the
550
+ underlying text token) */
551
+ --bar-zero-line-color: var(--color-text-primary);
552
+ --bar-zero-line-width: 1.5;
553
+
554
+ /* Gantt */
555
+ --gantt-bar-height: 18px;
556
+ --gantt-bar-gap: 6px;
557
+ --gantt-border-radius: 2px;
558
+
559
+ /* Waterfall connector hairlines (between consecutive bars) */
560
+ --waterfall-connector-color: rgba(0, 0, 0, 0.18);
561
+
562
+ /* Container */
563
+ --container-max-width: 660px;
564
+
565
+ /* Bar track (background) */
566
+ --bar-track-color: var(--color-border-row);
567
+ --bar-track-opacity: 0.5;
568
+
569
+ /* Label positioning */
570
+ --bar-label-gap: 8px;
571
+ --bar-value-padding: 6px;
572
+ --bar-inline-threshold: 0.35;
573
+
574
+ /* Lollipop variant */
575
+ --lollipop-stem-width: 2px;
576
+ --lollipop-dot-radius: 6px;
577
+ --lollipop-dot-radius-hover: 8px;
578
+
579
+ /* Bullet chart */
580
+ --bullet-range-opacity: 0.25;
581
+ --bullet-measure-height: 8px;
582
+ --bullet-marker-width: 3px;
583
+ --bullet-marker-height: 16px;
584
+
585
+ /* Animation */
586
+ --bar-animation-duration: 800ms;
587
+ --bar-stagger-delay: 50ms;
588
+
589
+ /* Row layout */
590
+ --bar-row-height: 32px;
591
+ --bar-category-width: 100px;
592
+
593
+ /* ===========================
594
+ STAT FORMATTING OPTIONS
595
+ =========================== */
596
+ /* Baseball batting average format: .345 instead of 0.345
597
+ * Use formatBaseballAvg() helper or set data-format="baseball-avg"
598
+ *
599
+ * JavaScript helper:
600
+ * function formatBaseballAvg(value) {
601
+ * return value.toFixed(3).replace(/^0\./, '.');
602
+ * }
603
+ */
604
+ }
605
+
606
+ /* ===========================
607
+ INSTAGRAM EXPORT TIER
608
+ ===========================
609
+ Opt-in via <html data-size="instagram">, paired with `size: 'instagram'`
610
+ in chart config (the SIZE_PRESETS geometry). Scales the text, stroke,
611
+ marker, and annotation tokens for 1080px social-feed posters that are
612
+ viewed at phone size — roughly 2.4x the screen-reading defaults. Colors
613
+ are untouched, so it composes with the dark theme. Same runtime contract
614
+ as dark mode: builders resolve tokens at render, so flipping the
615
+ attribute needs a re-render (ChartFactory.rerenderAll()). */
616
+ :root[data-size="instagram"] {
617
+ /* Typography */
618
+ --font-size-xs: 22px;
619
+ --font-size-sm: 25px;
620
+ --font-size-base: 27px;
621
+ --font-size-md: 29px;
622
+ --font-size-lg: 32px;
623
+ --font-size-xl: 35px;
624
+ --font-size-title: 64px;
625
+ --font-size-subtitle: 32px;
626
+ --shell-title-size: 38px;
627
+
628
+ /* Marks */
629
+ --line-stroke-width: 5;
630
+ --area-stroke-width: 4;
631
+ --line-dot-stroke-width: 4;
632
+ --bar-height: 56;
633
+ --bar-gap: 18;
634
+
635
+ /* Annotations */
636
+ --annotation-title-size: 34px;
637
+ --annotation-event-font-size: 24px;
638
+ --annotation-event-band-offset: 48;
639
+ --annotation-event-tick-height: 18;
640
+ --annotation-leader-threshold: 30;
641
+
642
+ /* Slope label dodge gaps track the scaled label text */
643
+ --slope-label-min-gap: 34;
644
+ --slope-label-min-gap-stacked: 72;
645
+
646
+ /* Bump chart marks at poster weight */
647
+ --bump-line-width: 6;
648
+ --bump-line-width-hover: 8;
649
+ --bump-dot-radius: 17;
650
+ --bump-dot-radius-hover: 20;
651
+ --bump-dot-stroke-width: 4;
652
+ --bump-rank-font-size: 20px;
653
+ --bump-label-gap: 16;
654
+ --bump-axis-offset: 26;
655
+ --bump-margin-top: 40;
656
+ --bump-margin-bottom: 60;
657
+ }
658
+
659
+ /* ===========================
660
+ DARK THEME
661
+ ===========================
662
+ Opt-in via <html data-theme="dark">. Only color-bearing tokens are
663
+ overridden; spacing, sizing, and opacity tokens carry over.
664
+
665
+ Builders resolve tokens live at render time (getCSSVar reads
666
+ documentElement), so switching themes at runtime requires a re-render:
667
+ call chart.rerender() on each chart facade (tables: table.update(data)).
668
+ CSS-styled surfaces (table chrome, tooltip shell, containers) restyle
669
+ immediately without one. */
670
+ :root[data-theme="dark"] {
671
+ /* Text — --color-black is the strongest ink, not literal black */
672
+ --color-black: #f4f4f5;
673
+ --color-text-primary: #e4e4e7;
674
+ --color-text-muted: #a1a1aa;
675
+ --color-text-subtle: #8b8b93;
676
+ --color-text-faint: #71717a;
677
+ --color-text-disabled: #52525b;
678
+
679
+ /* Backgrounds */
680
+ --color-bg-body: #101012;
681
+ --color-bg-container: #1b1b1e;
682
+ --color-bg-hover: #232327;
683
+ --color-bg-highlight: #2a2a2f;
684
+
685
+ /* Borders */
686
+ --color-border-header: #e4e4e7;
687
+ --color-border-row: #2a2a2e;
688
+ --color-border-light: #333338;
689
+ --color-border-footer: #2f2f34;
690
+ --color-border-tooltip: #3f3f46;
691
+
692
+ /* Shadows */
693
+ --shadow-container: 0 1px 3px rgba(0, 0, 0, 0.6);
694
+ --shadow-tooltip: 0 4px 14px rgba(0, 0, 0, 0.6);
695
+
696
+ /* Chart palette — brightened for dark surfaces */
697
+ --chart-primary: #60a5fa;
698
+ --chart-primary-light: rgba(96, 165, 250, 0.35);
699
+ --chart-primary-faint: rgba(96, 165, 250, 0.12);
700
+ --chart-secondary: #4ade80;
701
+ --chart-tertiary: #fb923c;
702
+ --chart-green: #4ade80;
703
+ --chart-lime: #a3e635;
704
+ --chart-orange: #fb923c;
705
+ --chart-purple: #c084fc;
706
+ --chart-purple-light: rgba(192, 132, 252, 0.4);
707
+ --chart-purple-faint: rgba(192, 132, 252, 0.08);
708
+ --chart-red: #f87171;
709
+
710
+ /* Multi-series palette — lighter gray is the more prominent one on dark */
711
+ --chart-series-1: #64748b;
712
+ --chart-series-2: #94a3b8;
713
+
714
+ /* Neutral/gray for cumulative/baseline lines */
715
+ --chart-neutral: #9ca3af;
716
+ --chart-neutral-light: rgba(156, 163, 175, 0.4);
717
+ --chart-neutral-faint: rgba(156, 163, 175, 0.08);
718
+
719
+ /* Slope */
720
+ --slope-muted-color: #52525b;
721
+ --slope-band-1: #2c1d1d;
722
+ --slope-band-2: #2b2717;
723
+ --slope-band-3: #16281c;
724
+ --slope-band-4: #17222f;
725
+
726
+ /* Misc structural strokes */
727
+ --stream-stroke-color: #1b1b1e;
728
+ --waterfall-connector-color: #52525b;
729
+ --dot-connector-color: #3f3f46;
730
+ --annotation-event-guide-color: #52525b;
731
+ --contour-line-color: rgba(255, 255, 255, 0.15);
732
+ --kde-contour-line-color: rgba(255, 255, 255, 0.12);
733
+
734
+ /* Categorical series palette — Set2, lifted for dark backgrounds */
735
+ --area-color-1: #74d0b3;
736
+ --area-color-2: #ff9e78;
737
+ --area-color-3: #a2b3dc;
738
+ --area-color-4: #ef9dd0;
739
+ --area-color-5: #b6e268;
740
+ --area-color-6: #ffe45c;
741
+ --area-color-7: #ecd2a8;
742
+ --area-color-8: #c6c6c6;
743
+
744
+ /* Temperature palette — extremes stay bright, middle fades to surface */
745
+ --temp-cold-10: #3f7fd1;
746
+ --temp-cold-9: #38689f;
747
+ --temp-cold-8: #2f5377;
748
+ --temp-cold-7: #263e52;
749
+ --temp-neutral: #26262b;
750
+ --temp-warm-5: #4a3a20;
751
+ --temp-warm-4: #6e4f22;
752
+ --temp-warm-3: #9c6a25;
753
+ --temp-warm-2: #d07f1f;
754
+ --temp-warm-1: #ff8a3d;
755
+
756
+ /* Sequential scales — lo ends re-anchor from near-white to surface-tinted
757
+ darks so low values fade into the background, high values stay vivid */
758
+ --scale-orange-lo: #2e2018;
759
+ --scale-orange-hi: #ff8a5c;
760
+ --scale-orange-red-hi: #e35d5d;
761
+ --scale-blue-lo: #16202c;
762
+ --scale-blue-hi: #6ea8dc;
763
+ --scale-teal-hi: #34b3a0;
764
+ --scale-aqua-1: #1a3a42;
765
+ --scale-aqua-2: #16606e;
766
+ --scale-aqua-3: #1d92ab;
767
+ --scale-aqua-4: #5cc6da;
768
+ --scale-gray-lo: #232326;
769
+ --scale-gray-hi: #d4d4d8;
770
+ --scale-purple-1: #241a2b;
771
+ --scale-purple-2: #4a3357;
772
+ --scale-purple-3: #7b57a0;
773
+ --scale-purple-4: #b58ae0;
774
+ --scale-green-1: #16241a;
775
+ --scale-green-2: #2f5138;
776
+ --scale-green-3: #4f8c58;
777
+ --scale-green-4: #7ac47f;
778
+ --scale-diag-green-1: #1b1b1e;
779
+ --scale-diag-green-2: #1d3524;
780
+ --scale-diag-green-3: #2f6b40;
781
+ --scale-diag-green-4: #4caf6d;
782
+ --scale-diag-red-1: #1b1b1e;
783
+ --scale-diag-red-2: #3a2023;
784
+ --scale-diag-red-3: #7a3b3b;
785
+ --scale-diag-red-4: #c25959;
786
+
787
+ /* Chart story — band lifts off the dark container surface; accent follows
788
+ --chart-primary automatically, and the panel wash re-mixes from it */
789
+ --story-band-bg: #2a2a2f;
790
+ --story-band-text: #e4e4e7;
791
+ --story-band-label: #a1a1aa;
792
+ }