@opendata-ai/openchart-core 7.11.0 → 8.0.0-rc.10

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.
Files changed (59) hide show
  1. package/dist/index.d.ts +1348 -132
  2. package/dist/index.js +556 -48
  3. package/dist/index.js.map +1 -1
  4. package/dist/styles.css +2 -1
  5. package/dist/styles.css.map +1 -0
  6. package/package.json +9 -3
  7. package/schema/chart.schema.json +9086 -0
  8. package/schema/table.schema.json +861 -0
  9. package/schema/vizspec.schema.json +17922 -0
  10. package/src/accessibility/alt-text.ts +39 -1
  11. package/src/accessibility/aria.ts +4 -2
  12. package/src/colors/__tests__/colorblind.test.ts +12 -0
  13. package/src/colors/colorblind.ts +11 -2
  14. package/src/colors/contrast.ts +11 -0
  15. package/src/colors/index.ts +9 -1
  16. package/src/colors/palettes.ts +47 -0
  17. package/src/helpers/__tests__/deep-merge.test.ts +54 -0
  18. package/src/helpers/__tests__/spec-builders.test.ts +3 -3
  19. package/src/helpers/deep-merge.ts +29 -0
  20. package/src/helpers/spec-builders.ts +7 -7
  21. package/src/index.ts +29 -1
  22. package/src/labels/collision.ts +1 -0
  23. package/src/layout/chrome.ts +24 -0
  24. package/src/layout/index.ts +1 -1
  25. package/src/locale/__tests__/format.test.ts +347 -11
  26. package/src/locale/format.ts +243 -18
  27. package/src/locale/index.ts +10 -1
  28. package/src/responsive/breakpoints.ts +7 -2
  29. package/src/responsive/index.ts +2 -0
  30. package/src/responsive/metrics.ts +77 -10
  31. package/src/schema/__tests__/generated-artifacts.test.ts +42 -0
  32. package/src/schema/__tests__/schema.test.ts +179 -0
  33. package/src/styles/__tests__/reduced-motion-selectors.test.ts +28 -0
  34. package/src/styles/animation.css +211 -168
  35. package/src/styles/base.css +89 -62
  36. package/src/styles/chrome.css +173 -179
  37. package/src/styles/dark.css +38 -35
  38. package/src/styles/graph.css +116 -111
  39. package/src/styles/index.css +5 -0
  40. package/src/styles/keyframes.css +130 -99
  41. package/src/styles/legend.css +14 -12
  42. package/src/styles/reduced-motion.css +61 -40
  43. package/src/styles/series-search.css +153 -0
  44. package/src/styles/sparkline.css +14 -12
  45. package/src/styles/table-animation.css +82 -80
  46. package/src/styles/table.css +314 -302
  47. package/src/styles/token-definitions.ts +199 -0
  48. package/src/styles/tokens.css +132 -128
  49. package/src/styles/tooltip.css +109 -115
  50. package/src/styles/you-draw-it.css +126 -0
  51. package/src/types/__tests__/encoding.test.ts +86 -17
  52. package/src/types/__tests__/spec.test.ts +16 -12
  53. package/src/types/encoding.ts +69 -25
  54. package/src/types/events.ts +107 -13
  55. package/src/types/index.ts +42 -3
  56. package/src/types/layout.ts +484 -11
  57. package/src/types/schema-roots.ts +32 -0
  58. package/src/types/spec.ts +718 -87
  59. package/src/types/table.ts +3 -2
package/dist/index.d.ts CHANGED
@@ -31,8 +31,13 @@ declare const HEIGHT_SHORT_MAX = 350;
31
31
  declare function getHeightClass(height: number): HeightClass;
32
32
  /** Label display mode at a given breakpoint. */
33
33
  type LabelMode = 'all' | 'important' | 'none';
34
- /** Legend position at a given breakpoint. */
35
- type LegendPosition = 'top' | 'right' | 'bottom' | 'bottom-right' | 'inline';
34
+ /**
35
+ * Legend position at a given breakpoint. `'top-left'` is a map-only overlay
36
+ * position: the legend floats inside the map area's top-left corner (with its
37
+ * own backdrop) instead of reserving a block of figure height. Charts treat
38
+ * it as the default position.
39
+ */
40
+ type LegendPosition = 'top' | 'top-left' | 'right' | 'bottom' | 'bottom-right' | 'inline';
36
41
  /** Annotation position strategy. */
37
42
  type AnnotationPosition = 'inline' | 'tooltip-only';
38
43
  /** Axis label density (controls tick count reduction). */
@@ -139,8 +144,9 @@ interface ColumnConfig {
139
144
  /** Explicit column width (CSS value like "200px" or "20%"). Auto-sized if omitted. */
140
145
  width?: string;
141
146
  /**
142
- * Number/date format string (d3-format or d3-time-format).
143
- * Applied to the raw value before display. e.g. ",.0f" or "%Y-%m-%d".
147
+ * Number/date format string (d3-format or d3-time-format), or semantic
148
+ * keyword `'percent'` / `'currency'`. e.g. ",.0f", "%Y-%m-%d", "percent".
149
+ * Tables always show full precision (no compact notation).
144
150
  */
145
151
  format?: string;
146
152
  /** Heatmap: color the cell background based on numeric value. */
@@ -361,10 +367,12 @@ interface ResolvedTheme extends Theme {
361
367
  * - 'rule': reference lines as data marks
362
368
  * - 'tick': strip/rug plot marks
363
369
  * - 'rect': heatmaps and 2D binned plots
370
+ * - 'range': dumbbell / arrow / range-bar plots spanning two values per category
371
+ * - 'waffle': unit grids ("x of 100") for part-to-whole counts
372
+ * - 'calendar': GitHub-style calendar heatmap (weeks x weekdays, daily dates)
373
+ * - 'parliament': hemicycle seat-dot chart for election/legislature results
364
374
  */
365
- type MarkType = 'bar' | 'line' | 'area' | 'point' | 'circle' | 'arc' | 'text' | 'rule' | 'tick' | 'rect' | 'lollipop';
366
- /** @deprecated Use MarkType instead. Kept for internal migration references. */
367
- type ChartType = MarkType;
375
+ type MarkType = 'bar' | 'line' | 'area' | 'point' | 'circle' | 'arc' | 'text' | 'rule' | 'tick' | 'rect' | 'lollipop' | 'beeswarm' | 'range' | 'waffle' | 'calendar' | 'parliament';
368
376
  /** A single color stop in a gradient definition. */
369
377
  interface GradientStop {
370
378
  /** Position along the gradient, 0 to 1. */
@@ -456,6 +464,19 @@ interface MarkDef {
456
464
  innerRadius?: number;
457
465
  /** Outer radius for arc marks. */
458
466
  outerRadius?: number;
467
+ /**
468
+ * Start angle in radians for arc marks. Defaults to 0 (full pie/donut).
469
+ * Combined with `endAngle`, restricts the pie/donut to a partial sweep,
470
+ * e.g. `startAngle: -Math.PI / 2, endAngle: Math.PI / 2` for a half-donut
471
+ * (election-style results donut). Angles follow d3's convention: 0 is
472
+ * straight up, increasing clockwise. Only meaningful when `type` is `'arc'`.
473
+ */
474
+ startAngle?: number;
475
+ /**
476
+ * End angle in radians for arc marks. Defaults to 2*Math.PI (full pie/donut).
477
+ * See `startAngle`. Only meaningful when `type` is `'arc'`.
478
+ */
479
+ endAngle?: number;
459
480
  /** Corner radius for rect/bar marks. 'pill' sets rx to half the bar thickness. */
460
481
  cornerRadius?: number | 'pill';
461
482
  /** Fixed bar thickness in pixels for bar/column marks. When set, bars are this height (horizontal) or width (vertical), centered within the band. */
@@ -482,6 +503,102 @@ interface MarkDef {
482
503
  * two competing diagonals.
483
504
  */
484
505
  trendline?: boolean;
506
+ /**
507
+ * Horizontal pixel offset from the data anchor. Only meaningful when `type`
508
+ * is `'text'`. Use it to lift a label clear of the mark it annotates instead
509
+ * of shifting the underlying data value.
510
+ */
511
+ dx?: number;
512
+ /**
513
+ * Vertical pixel offset from the data anchor (negative = up). Only meaningful
514
+ * when `type` is `'text'`. `{ type: 'text', dy: -10 }` floats each label ten
515
+ * pixels above its point.
516
+ */
517
+ dy?: number;
518
+ /** Horizontal alignment of text marks relative to their anchor. Defaults to `'center'`. */
519
+ align?: 'left' | 'center' | 'right';
520
+ /** Vertical alignment of text marks relative to their anchor. Defaults to `'middle'`. */
521
+ baseline?: 'top' | 'middle' | 'bottom';
522
+ /**
523
+ * Font size in pixels for text marks. Ignored when the `size` encoding channel
524
+ * is present (which scales font size from a data field). `size` is already
525
+ * taken by bar thickness, hence the distinct name.
526
+ */
527
+ fontSize?: number;
528
+ /**
529
+ * Pattern-fill reinforcement for filled marks (bar, area, arc).
530
+ *
531
+ * - `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots,
532
+ * crosshatch, vertical lines) layered over each series color so series
533
+ * remain distinguishable without color vision. Marks too small to show
534
+ * a legible pattern keep their solid fill.
535
+ * - `'none'` (default): solid fills only.
536
+ */
537
+ fillPattern?: 'auto' | 'none';
538
+ /**
539
+ * Visual style for range marks. Only meaningful when `type` is `'range'`.
540
+ * - 'dumbbell' (default): a dot at each end joined by a connector line.
541
+ * The start dot is muted, the end dot carries the accent color.
542
+ * - 'arrow': a line with an arrowhead at the x2/y2 end, the strongest
543
+ * "change between two points in time" form.
544
+ * - 'bar': a plain floating range bar spanning start to end.
545
+ */
546
+ style?: 'dumbbell' | 'arrow' | 'bar';
547
+ /**
548
+ * Color range marks by direction of change. Only meaningful when `type` is
549
+ * `'range'`. When true, increases (x2 > x, or y2 > y) use the theme's
550
+ * `positive` semantic color and decreases use `negative`. A field-based
551
+ * `encoding.color` takes precedence over direction coloring.
552
+ */
553
+ colorByDirection?: boolean;
554
+ /**
555
+ * Total number of cells in a waffle grid. Only meaningful when `type` is
556
+ * `'waffle'`. Category values normalize to this many cells via
557
+ * largest-remainder rounding so the grid always sums exactly. Default 100.
558
+ */
559
+ units?: number;
560
+ /**
561
+ * Number of columns in a waffle grid. Only meaningful when `type` is
562
+ * `'waffle'`. Rows derive from `units / columns`. Default 10.
563
+ */
564
+ columns?: number;
565
+ /**
566
+ * First day of the week for calendar marks. Only meaningful when `type` is
567
+ * `'calendar'`. Controls which weekday occupies the top row of each band.
568
+ * Defaults to 'monday' (ISO week convention).
569
+ */
570
+ weekStart?: 'monday' | 'sunday';
571
+ /**
572
+ * Corner radius in pixels for calendar day cells. Only meaningful when
573
+ * `type` is `'calendar'`. Defaults to 1.
574
+ */
575
+ cellRadius?: number;
576
+ /**
577
+ * Seat layout shape for parliament marks. Only meaningful when `type` is
578
+ * `'parliament'`. `'hemicycle'` (default, and currently the only shape)
579
+ * packs seats into concentric semicircular arcs, party-grouped left to
580
+ * right.
581
+ */
582
+ shape?: 'hemicycle';
583
+ /**
584
+ * Seat dot radius in pixels for parliament marks. `'auto'` (default) sizes
585
+ * dots to fill the available hemicycle rings for the given seat count.
586
+ * Only meaningful when `type` is `'parliament'`.
587
+ */
588
+ seatRadius?: number | 'auto';
589
+ /**
590
+ * The majority-threshold line and "N to win" label on parliament marks. Only
591
+ * meaningful when `type` is `'parliament'`. `true` (default) draws the line at
592
+ * a simple majority (half the seats plus one); `false` hides it. The object
593
+ * form overrides the threshold and label: `{ seats }` sets a custom seat count
594
+ * (e.g. a two-thirds supermajority) and `{ label }` overrides the default
595
+ * "N to win" text. A `seats` value outside `1..totalSeats` warns and falls
596
+ * back to the simple-majority default.
597
+ */
598
+ majorityLine?: boolean | {
599
+ seats?: number;
600
+ label?: string;
601
+ };
485
602
  }
486
603
  /** Data field type, following Vega-Lite conventions. */
487
604
  type FieldType = 'quantitative' | 'temporal' | 'nominal' | 'ordinal';
@@ -491,7 +608,11 @@ type AggregateOp = 'count' | 'sum' | 'mean' | 'median' | 'min' | 'max' | 'varian
491
608
  interface AxisConfig {
492
609
  /** Axis title text. If omitted, the field name is used. */
493
610
  title?: string;
494
- /** Number format string (d3-format). e.g. ",.0f" for comma-separated integers. */
611
+ /**
612
+ * Number format: d3-format string (e.g. ",.0f"), or semantic keyword
613
+ * `'percent'` / `'currency'`. When omitted, compact notation (1k, 2.5M)
614
+ * is applied automatically.
615
+ */
495
616
  format?: string;
496
617
  /** Override tick count. Engine picks a sensible default if omitted. */
497
618
  tickCount?: number;
@@ -534,8 +655,12 @@ interface AxisConfig {
534
655
  }
535
656
  /** Scale configuration for an encoding channel. */
536
657
  interface ScaleConfig {
537
- /** Explicit domain override. Auto-derived from data if omitted. */
538
- domain?: [number, number] | string[];
658
+ /**
659
+ * Explicit domain override. Auto-derived from data if omitted.
660
+ * Continuous scales take `[min, max]`; threshold scales take the full
661
+ * list of class break values; categorical scales take the value list.
662
+ */
663
+ domain?: number[] | string[];
539
664
  /** Scale type override. Usually inferred from field type. */
540
665
  type?: ScaleType;
541
666
  /** Whether to nice-ify the domain for clean tick values. Defaults to true. */
@@ -546,6 +671,15 @@ interface ScaleConfig {
546
671
  clip?: boolean;
547
672
  /** Explicit range override. */
548
673
  range?: unknown[];
674
+ /**
675
+ * Named color scheme (Vega-Lite aligned). Resolved against the core palette
676
+ * registry (sequential: blue, green, orange, purple, teal; diverging:
677
+ * redBlue, brownTeal; categorical) during the pre-validation sugar
678
+ * expansion; common VL names (blues, greens, category10, ...) are accepted
679
+ * as aliases. Unknown names fail validation with the supported list.
680
+ * An explicit `range` wins over `scheme`.
681
+ */
682
+ scheme?: string;
549
683
  /** Reverse the range direction. */
550
684
  reverse?: boolean;
551
685
  /** Clamp output to the range. */
@@ -565,6 +699,28 @@ interface ScaleConfig {
565
699
  }
566
700
  /** Scale type, following Vega-Lite conventions. */
567
701
  type ScaleType = 'linear' | 'log' | 'pow' | 'sqrt' | 'symlog' | 'time' | 'utc' | 'ordinal' | 'band' | 'point' | 'quantile' | 'quantize' | 'threshold';
702
+ /**
703
+ * Sort a categorical domain by the aggregated values of another field
704
+ * (Vega-Lite EncodingSortField).
705
+ */
706
+ interface SortByField {
707
+ /** Data field whose values determine the category order. */
708
+ field: string;
709
+ /** Aggregate applied per category before comparing. Defaults to 'sum'. */
710
+ op?: AggregateOp;
711
+ /** Sort direction. Defaults to 'ascending'. */
712
+ order?: 'ascending' | 'descending';
713
+ }
714
+ /**
715
+ * A constant value definition for an encoding channel (Vega-Lite aligned).
716
+ * The VL idiom for a fixed visual property, e.g. `color: { value: '#1b7fa3' }`.
717
+ * Expanded to the matching mark-level property (fill/stroke, size, opacity)
718
+ * during the pre-validation sugar expansion.
719
+ */
720
+ interface ValueDef {
721
+ /** The constant value: CSS color string, size number, opacity (0-1), or gradient. */
722
+ value: string | number | boolean | null | GradientDef;
723
+ }
568
724
  /**
569
725
  * A single encoding channel mapping a data field to a visual property.
570
726
  *
@@ -596,8 +752,11 @@ interface EncodingChannel<TData extends DataRow = DataRow> {
596
752
  type: FieldType;
597
753
  /** Optional aggregate to apply before encoding. */
598
754
  aggregate?: AggregateOp;
599
- /** Axis configuration. Set to `false` to suppress axis entirely (no space reserved). */
600
- axis?: AxisConfig | false;
755
+ /**
756
+ * Axis configuration. Set to `false` to suppress axis entirely (no space
757
+ * reserved). `null` is accepted as a VL-aligned alias for `false`.
758
+ */
759
+ axis?: AxisConfig | false | null;
601
760
  /** Scale configuration. */
602
761
  scale?: ScaleConfig;
603
762
  /**
@@ -609,26 +768,28 @@ interface EncodingChannel<TData extends DataRow = DataRow> {
609
768
  * The string forms are still the canonical input — the boolean shorthand is
610
769
  * normalized to the matching string before reaching layout.
611
770
  *
612
- * - undefined: chart-type default (see below)
771
+ * - undefined: chart-type default (see below) -- stacked for bar/column/area
613
772
  * - true | 'zero': stack from zero baseline
614
773
  * - 'normalize': stack and normalize to fraction of total (0-1 per category)
615
774
  * - 'center': center stacks around zero (streamgraph style)
616
- * - null | false: no stacking -- renders overlap (area) or grouped/dodged (bar)
775
+ * - null | false: no stacking -- renders grouped/dodged (bar) or overlap (area)
617
776
  *
618
777
  * **Defaults differ by chart type:**
619
- * - **Bar/Column**: defaults to grouped (side-by-side). Use `stack: 'zero'` (or `true`) for stacked bars.
620
- * - **Area**: defaults to overlap (v6 breaking change). Use `stack: 'zero'` (or `true`)
621
- * to opt into stacked areas. Each overlapping series renders as a translucent
622
- * gradient band anchored at the y-domain baseline.
778
+ * - **Bar/Column**: defaults to stacked (`'zero'`), Vega-Lite aligned. Use
779
+ * `stack: null` (or `false`) for grouped/side-by-side bars.
780
+ * - **Area**: defaults to stacked (`'zero'`), Vega-Lite aligned. Use
781
+ * `stack: null` (or `false`) to opt into overlap rendering, where each
782
+ * series renders as a translucent gradient band anchored at the
783
+ * y-domain baseline.
623
784
  * - **Line**: stacking is not applied (lines always overlap).
624
785
  *
625
786
  * @example
626
- * // Stacked horizontal bars (opt-in; default is grouped):
627
- * "x": { "field": "revenue", "type": "quantitative", "stack": "zero" }
787
+ * // Grouped horizontal bars (opt-out; default is stacked):
788
+ * "x": { "field": "revenue", "type": "quantitative", "stack": null }
628
789
  *
629
790
  * @example
630
- * // Stacked area (opt-in; default is overlap):
631
- * "y": { "field": "value", "type": "quantitative", "stack": "zero" }
791
+ * // Overlap area (opt-out; default is stacked):
792
+ * "y": { "field": "value", "type": "quantitative", "stack": null }
632
793
  */
633
794
  stack?: boolean | 'zero' | 'normalize' | 'center' | null;
634
795
  /**
@@ -651,8 +812,22 @@ interface EncodingChannel<TData extends DataRow = DataRow> {
651
812
  * - 'descending': sort domain values descending
652
813
  * - null: use data order (no sorting)
653
814
  * - undefined: ascending (VL default)
815
+ * - 'x' | '-x' | 'y' | '-y': sort by another channel's values ('-' prefix
816
+ * for descending). The VL ranked-bar idiom: `sort: '-y'` orders categories
817
+ * by value, largest first. Resolved into an explicit domain during the
818
+ * pre-validation sugar expansion.
819
+ * - string[]: explicit value order. Listed values come first (in the given
820
+ * order); unlisted data values follow in data order.
821
+ * - SortByField: sort by the aggregated values of another field.
654
822
  */
655
- sort?: 'ascending' | 'descending' | null;
823
+ sort?: 'ascending' | 'descending' | null | 'x' | '-x' | 'y' | '-y' | string[] | SortByField;
824
+ /**
825
+ * Legend configuration for this channel (Vega-Lite aligned).
826
+ * Only meaningful on the `color` channel. `null` hides the legend (the VL
827
+ * idiom for `legend.show = false`); a config object merges into the
828
+ * top-level `legend` (top-level keys win on conflict).
829
+ */
830
+ legend?: LegendConfig | null;
656
831
  /**
657
832
  * Display title override (Vega-Lite aligned).
658
833
  * Used as the label in tooltips instead of the raw field name.
@@ -661,7 +836,8 @@ interface EncodingChannel<TData extends DataRow = DataRow> {
661
836
  title?: string;
662
837
  /**
663
838
  * Format string for values (Vega-Lite aligned).
664
- * For quantitative fields: d3-format string (e.g. ",.0f", "$,.2f").
839
+ * For quantitative fields: d3-format string (e.g. ",.0f", "$,.2f"),
840
+ * or semantic keyword `'percent'` / `'currency'`.
665
841
  * For temporal fields: d3-time-format string (e.g. "%Y", "%b %d").
666
842
  * Used in tooltips; `axis.format` takes precedence for axis tick labels.
667
843
  */
@@ -721,14 +897,16 @@ interface Encoding<TData extends DataRow = DataRow> {
721
897
  /**
722
898
  * Color channel. Required for arc marks (determines pie/donut slice coloring).
723
899
  * Optional for all other marks -- used for series differentiation on multi-series charts,
724
- * or heatmap intensity. Accepts a conditional definition to apply colors based on data predicates.
900
+ * or heatmap intensity. Accepts a conditional definition to apply colors based on data predicates,
901
+ * or a bare `{ value }` constant (VL aligned; expanded to the mark-level fill/stroke).
725
902
  */
726
- color?: EncodingChannel<TData> | ConditionalValueDef<TData>;
903
+ color?: EncodingChannel<TData> | ConditionalValueDef<TData> | ValueDef;
727
904
  /**
728
905
  * Size channel. Used by point/bubble charts to scale dot area by a quantitative field.
729
- * Accepts a conditional definition to vary size based on data predicates.
906
+ * Accepts a conditional definition to vary size based on data predicates,
907
+ * or a bare `{ value }` constant (VL aligned; expanded to `mark.size`).
730
908
  */
731
- size?: EncodingChannel<TData> | ConditionalValueDef<TData>;
909
+ size?: EncodingChannel<TData> | ConditionalValueDef<TData> | ValueDef;
732
910
  /**
733
911
  * Detail channel. Groups data into multiple series without mapping to a visual property.
734
912
  * Useful when you want separate lines per category but don't need the color to differ.
@@ -752,17 +930,14 @@ interface Encoding<TData extends DataRow = DataRow> {
752
930
  y2?: EncodingChannel<TData>;
753
931
  /**
754
932
  * Data-driven opacity (0-1 range). Accepts a conditional definition to vary opacity
755
- * based on data predicates (e.g., highlight selected points).
756
- */
757
- opacity?: EncodingChannel<TData> | ConditionalValueDef<TData>;
758
- /**
759
- * Point shape encoding. Valid values: 'circle', 'square', 'diamond', 'triangle-up',
760
- * 'triangle-down', 'cross'. Used on point/scatter marks to differentiate series by shape.
933
+ * based on data predicates (e.g., highlight selected points), or a bare `{ value }`
934
+ * constant (VL aligned; expanded to `mark.opacity`).
761
935
  */
762
- shape?: EncodingChannel<TData>;
936
+ opacity?: EncodingChannel<TData> | ConditionalValueDef<TData> | ValueDef;
763
937
  /**
764
- * Stroke dash pattern encoding. Maps a nominal field to different dash patterns
765
- * on line marks. Useful when color alone doesn't distinguish series well.
938
+ * Stroke dash pattern encoding. Maps a nominal/ordinal field to different
939
+ * dash patterns on line and rule marks. Useful when color alone doesn't
940
+ * distinguish series well.
766
941
  */
767
942
  strokeDash?: EncodingChannel<TData>;
768
943
  /** Rotation angle encoding for point marks. Maps a quantitative field to 0-360 degrees. */
@@ -777,25 +952,15 @@ interface Encoding<TData extends DataRow = DataRow> {
777
952
  * multi-field tooltips. Independent of the x/y/color encoding.
778
953
  */
779
954
  tooltip?: EncodingChannel<TData> | EncodingChannel<TData>[];
780
- /** Hyperlink encoding. Maps a field containing URLs to clickable marks. */
781
- href?: EncodingChannel<TData>;
782
955
  /**
783
- * Drawing order. Controls z-order and stacking sort order for bar/area marks.
784
- * Lower values are drawn first (behind higher values).
785
- */
786
- order?: EncodingChannel<TData>;
787
- /**
788
- * Angular position for arc marks (pie/donut).
789
- * Optional -- defaults to the `y` channel value when omitted.
790
- * Not used by any other mark type.
956
+ * Angular position (slice value) for arc marks (pie/donut), the canonical
957
+ * value channel in v8. Waffle and parliament marks accept it too (the same
958
+ * part-to-whole value channel). `y` is still accepted on these marks as a
959
+ * deprecated alias for backward compatibility (a compile warning is emitted
960
+ * and `y` is populated from `theta` internally). Not used by any other mark
961
+ * type.
791
962
  */
792
963
  theta?: EncodingChannel<TData>;
793
- /**
794
- * Radial distance from center for arc marks.
795
- * Optional -- only meaningful on donut charts (controls inner radius boundary).
796
- * Not used by any other mark type.
797
- */
798
- radius?: EncodingChannel<TData>;
799
964
  /**
800
965
  * Facet channel: partitions data into a grid of small-multiple panels.
801
966
  * Each unique value of the facet field produces one panel. Panels share
@@ -803,6 +968,20 @@ interface Encoding<TData extends DataRow = DataRow> {
803
968
  * per-panel axes.
804
969
  */
805
970
  facet?: FacetChannel<TData>;
971
+ /**
972
+ * Row facet channel: partitions data into vertically-stacked panels.
973
+ * Each unique value of the row field produces one panel, arranged in a
974
+ * single column. Shares the x-axis scale by default; y-axis is independent.
975
+ * Mutually exclusive with `facet` and `column`.
976
+ */
977
+ row?: FacetChannel<TData>;
978
+ /**
979
+ * Column facet channel: partitions data into side-by-side panels.
980
+ * Each unique value of the column field produces one panel, arranged in a
981
+ * single row. Shares the y-axis scale by default; x-axis is independent.
982
+ * Mutually exclusive with `facet` and `row`.
983
+ */
984
+ column?: FacetChannel<TData>;
806
985
  }
807
986
  /** Encoding channel for graph nodes and edges. */
808
987
  interface GraphEncodingChannel {
@@ -907,15 +1086,24 @@ interface AnnotationOffset {
907
1086
  }
908
1087
  /** Anchor direction for annotation label placement relative to the data point. */
909
1088
  type AnnotationAnchor = 'top' | 'bottom' | 'left' | 'right' | 'auto';
910
- /** Style overrides for the dot marker drawn at the connector's data-point endpoint. */
1089
+ /** Connector type for annotation labels. */
1090
+ type ConnectorType = 'straight' | 'curve' | 'drop-line';
1091
+ /** Object form of the connector config, allowing arrow control. */
1092
+ interface ConnectorConfig {
1093
+ /** Connector style. */
1094
+ type: ConnectorType;
1095
+ /** Whether to draw an arrowhead at the data-point end. Default: true for curve, false for straight. Drop-line never takes a head. */
1096
+ arrow?: boolean;
1097
+ }
1098
+ /** Style overrides for the dot marker drawn at the data point. */
911
1099
  interface AnnotationDot {
912
- /** Circle radius in pixels. Default 5. */
1100
+ /** Circle radius in pixels. Default 4. */
913
1101
  radius?: number;
914
1102
  /** Fill color. Defaults to theme background for an "open ring" look. */
915
1103
  fill?: string;
916
- /** Stroke color. Defaults to theme text color. */
1104
+ /** Stroke color. Defaults to the connector's resolved stroke, so marker and leader read as one system. */
917
1105
  stroke?: string;
918
- /** Stroke width in pixels. Default 2. */
1106
+ /** Stroke width in pixels. Default 1.5. */
919
1107
  strokeWidth?: number;
920
1108
  }
921
1109
  /** Base properties shared by all annotation types. */
@@ -945,7 +1133,13 @@ interface TextAnnotation extends AnnotationBase {
945
1133
  x: string | number;
946
1134
  /** Y-axis data value or position. */
947
1135
  y: string | number;
948
- /** The annotation text. Required for text annotations. */
1136
+ /**
1137
+ * The annotation text. Required for text annotations.
1138
+ *
1139
+ * Supports inline `**bold**` spans, so emphasis lands on the key phrase rather
1140
+ * than the whole block: `'Inflation peaked at **8.5%**'`. Unmatched `**` (and
1141
+ * empty `****`) render literally. `\n` starts a new line.
1142
+ */
949
1143
  text: string;
950
1144
  /** Thinning priority (lower = kept longer at narrow widths). When omitted, spec order is used. */
951
1145
  priority?: number;
@@ -953,12 +1147,23 @@ interface TextAnnotation extends AnnotationBase {
953
1147
  * Optional muted second-tone text rendered below the primary `text`.
954
1148
  * Used for supporting context (e.g. methodology, source). Newlines in
955
1149
  * `text` still produce multi-line primary; subtitle is a separate block.
1150
+ *
1151
+ * Supports the same inline `**bold**` spans as `text`. When a subtitle is
1152
+ * present and no `fontWeight` is set, the primary `text` resolves to weight
1153
+ * 700 — the lede + context stack.
956
1154
  */
957
1155
  subtitle?: string;
958
1156
  /**
959
- * Optional dot marker drawn at the connector's data-point endpoint.
960
- * `true` enables the default open-ring style. Pass an object to override
961
- * radius, fill, stroke, or strokeWidth.
1157
+ * Open-ring marker drawn on the data point itself (not on the pulled-back
1158
+ * connector tip).
1159
+ *
1160
+ * Left unset, a default marker appears whenever a connector is enabled and
1161
+ * carries no arrowhead — the leader points, the ring lands. An arrowed
1162
+ * connector gets no marker unless you ask for one: the head already marks the
1163
+ * spot. `dot: false` is always bare; an explicit `dot` always wins.
1164
+ *
1165
+ * `true` uses the default style; pass an object to override radius, fill,
1166
+ * stroke, or strokeWidth.
962
1167
  */
963
1168
  dot?: boolean | AnnotationDot;
964
1169
  /** Font size override. */
@@ -971,13 +1176,19 @@ interface TextAnnotation extends AnnotationBase {
971
1176
  anchor?: AnnotationAnchor;
972
1177
  /**
973
1178
  * Connector from label to anchor point.
974
- * - `true` (default): straight line
975
- * - `'straight'`: straight line (alias of `true`)
1179
+ * - `true` (default): straight line, no arrow
1180
+ * - `'straight'`: straight line, no arrow
976
1181
  * - `'curve'`: curved arrow with arrowhead
977
- * - `'drop-line'`: vertical line through the data point's x; label sits beside the line and auto-flips to the opposite side if it would overflow the chart area
1182
+ * - `'drop-line'`: vertical line through the data point's x
1183
+ * - `{ type, arrow? }`: object form for explicit arrow control
978
1184
  * - `false`: no connector
1185
+ *
1186
+ * The connector leaves the text block on the side facing the data point, with
1187
+ * a small standoff gap. A leader shorter than 8px is dropped as noise (the
1188
+ * marker alone reads better), as is a connector whose target sits inside the
1189
+ * text block. Widen `offset` if you want the line back.
979
1190
  */
980
- connector?: boolean | 'straight' | 'curve' | 'drop-line';
1191
+ connector?: boolean | ConnectorType | ConnectorConfig;
981
1192
  /** Per-endpoint offsets for the connector line. Allows fine-tuning where the connector starts and ends. */
982
1193
  connectorOffset?: {
983
1194
  /** Offset for the label-end of the connector. */
@@ -985,8 +1196,14 @@ interface TextAnnotation extends AnnotationBase {
985
1196
  /** Offset for the data-point-end of the connector. */
986
1197
  to?: AnnotationOffset;
987
1198
  };
988
- /** Background color behind the text. Useful for readability over chart lines. */
989
- background?: string;
1199
+ /**
1200
+ * Background plate behind the text, for readability over chart lines.
1201
+ *
1202
+ * `true` uses the theme surface, so the plate follows light/dark mode. A
1203
+ * color string sets it explicitly. Prefer `true`: a hardcoded '#ffffff' stays
1204
+ * white in dark mode and the text turns light-gray-on-white.
1205
+ */
1206
+ background?: string | true;
990
1207
  /** Whether to show the paint-order stroke halo behind text. Default true. Set false for white text on colored backgrounds. */
991
1208
  halo?: boolean;
992
1209
  }
@@ -1028,7 +1245,14 @@ interface RangeAnnotation extends AnnotationBase {
1028
1245
  * Useful for baselines (zero), targets, or thresholds.
1029
1246
  */
1030
1247
  interface RefLineAnnotation extends AnnotationBase {
1031
- type: 'refline' | 'rule';
1248
+ /**
1249
+ * Discriminant. `'refline'` is canonical and the only value in the type.
1250
+ * `'rule'` (it collides with the `rule` mark type) is still accepted at
1251
+ * runtime for backward compatibility -- the spec-sugar pass rewrites it to
1252
+ * `'refline'` with a deprecation warning before validation -- but it is not
1253
+ * part of this type. Use `'refline'`.
1254
+ */
1255
+ type: 'refline';
1032
1256
  /** X-axis value for a vertical reference line. */
1033
1257
  x?: string | number;
1034
1258
  /** Y-axis value for a horizontal reference line. */
@@ -1212,7 +1436,11 @@ interface LabelConfig {
1212
1436
  suffix?: string;
1213
1437
  /** Fixed CSS color for all labels. Overrides the default fill-derived color. */
1214
1438
  color?: string;
1215
- /** Per-series pixel offsets for fine-tuning label positions, keyed by series name. */
1439
+ /**
1440
+ * Per-series pixel offsets for fine-tuning label positions, keyed by series name.
1441
+ * @deprecated Per-series pixel offsets are a workaround for collision bugs
1442
+ * now fixed by the layout engine. Will be removed in a future version.
1443
+ */
1216
1444
  offsets?: Record<string, AnnotationOffset>;
1217
1445
  /** Font size in pixels for bar/column value labels. */
1218
1446
  fontSize?: number;
@@ -1236,6 +1464,46 @@ interface LegendConfig {
1236
1464
  /** Series names to exclude from the legend. Excluded series still render in the chart. */
1237
1465
  exclude?: string[];
1238
1466
  }
1467
+ /**
1468
+ * Configuration for the searchable series highlight input (`seriesSearch`).
1469
+ * The boolean form (`seriesSearch: true`) uses the defaults.
1470
+ */
1471
+ interface SeriesSearchConfig {
1472
+ /** Placeholder text for the search input. Defaults to "Find a series". */
1473
+ placeholder?: string;
1474
+ }
1475
+ /**
1476
+ * Configuration for the "you draw it" interactive engagement format
1477
+ * (`youDrawIt`). The reader draws their guess of the trend from `from` to the
1478
+ * right edge before the real line reveals. Line marks only, single-series only.
1479
+ */
1480
+ interface YouDrawItConfig {
1481
+ /**
1482
+ * The x value (matching `encoding.x`'s domain) where drawing starts. Data
1483
+ * before this point renders normally; data at or after it is hidden behind
1484
+ * a hatched "draw here" region until reveal.
1485
+ */
1486
+ from: string | number;
1487
+ /** Prompt text shown in the hatched drawing region. Defaults to "Draw your guess". */
1488
+ prompt?: string;
1489
+ /** Label for the skip-to-reveal button. Defaults to "Show me". */
1490
+ revealLabel?: string;
1491
+ /**
1492
+ * Optional extra comparison line (e.g. "what everyone else guessed"),
1493
+ * supplied by the host app. Rendered in a third, visually distinct style
1494
+ * alongside the reader's guess and the real line after reveal.
1495
+ */
1496
+ comparisonLine?: Array<{
1497
+ x: string | number;
1498
+ y: number;
1499
+ }>;
1500
+ }
1501
+ /**
1502
+ * Content composition for an endpoint-label entry: the series name, the
1503
+ * formatted value, or both joined on one line. See
1504
+ * {@link EndpointLabelsConfig.content}.
1505
+ */
1506
+ type EndpointLabelContent = 'label' | 'value' | 'label value';
1239
1507
  /**
1240
1508
  * Configuration for the endpoint labels column rendered at the chart's right edge
1241
1509
  * for multi-series line/area charts. Each entry pairs the series name with its
@@ -1273,6 +1541,21 @@ interface EndpointLabelsConfig {
1273
1541
  * - `'both'`: labels on both left (leading) and right (trailing) edges.
1274
1542
  */
1275
1543
  ends?: 'end' | 'both';
1544
+ /**
1545
+ * What each entry renders.
1546
+ * - `'label'`: series name only.
1547
+ * - `'value'`: formatted value only.
1548
+ * - `'label value'`: series name and value joined on one line.
1549
+ *
1550
+ * When omitted, entries render the series name with the formatted value on
1551
+ * a second line (the classic endpoint column). Pass an object to set the
1552
+ * leading and trailing columns independently, e.g. the classic slope-chart
1553
+ * look is `{ leading: 'label value', trailing: 'value' }`.
1554
+ */
1555
+ content?: EndpointLabelContent | {
1556
+ leading?: EndpointLabelContent;
1557
+ trailing?: EndpointLabelContent;
1558
+ };
1276
1559
  /** Field to read the displayed value from. Defaults to `encoding.y.field`. */
1277
1560
  valueField?: string;
1278
1561
  /** d3-format string for the value. Defaults to `encoding.y.axis.format`. */
@@ -1422,14 +1705,14 @@ interface Metric {
1422
1705
  }
1423
1706
  /**
1424
1707
  * Encoding for arc marks (pie/donut charts).
1425
- * - `y`: required (quantitative — the slice value)
1708
+ * - `theta`: required (quantitative — the canonical slice value channel)
1426
1709
  * - `color`: required (nominal/ordinal — the category)
1427
- * - `theta`: optional (defaults to `y` channel)
1428
- * - `radius`: optional (donut inner radius)
1710
+ * - `y`: deprecated alias for `theta`, accepted for backward compatibility
1429
1711
  */
1430
1712
  interface ArcEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1431
- y: EncodingChannel<TData>;
1432
- color: EncodingChannel<TData>;
1713
+ /** Canonical value channel for arc marks (quantitative — the slice value). */
1714
+ theta: EncodingChannel<TData>;
1715
+ color: EncodingChannel<TData> | ConditionalValueDef<TData>;
1433
1716
  }
1434
1717
  /**
1435
1718
  * Encoding for line marks.
@@ -1485,6 +1768,62 @@ interface LollipopEncoding<TData extends DataRow = DataRow> extends Encoding<TDa
1485
1768
  x: EncodingChannel<TData>;
1486
1769
  y: EncodingChannel<TData>;
1487
1770
  }
1771
+ /**
1772
+ * Encoding for beeswarm marks (dodged distribution dots).
1773
+ * - Exactly one positional channel is quantitative: the value axis
1774
+ * (x = horizontal swarm, y = vertical swarm).
1775
+ * - The other positional channel is optional (nominal/ordinal): grouped
1776
+ * swarms, one lane per category. The cross axis has no scale; the dodge
1777
+ * layout computes pixel offsets around each lane center.
1778
+ */
1779
+ interface BeeswarmEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1780
+ x?: EncodingChannel<TData>;
1781
+ y?: EncodingChannel<TData>;
1782
+ }
1783
+ /**
1784
+ * Encoding for range marks (dumbbell / arrow / range-bar plots).
1785
+ * - Horizontal (the common editorial form): `y` nominal/ordinal category,
1786
+ * `x` quantitative start, `x2` quantitative end.
1787
+ * - Vertical: `x` nominal/ordinal category, `y` quantitative start,
1788
+ * `y2` quantitative end.
1789
+ */
1790
+ interface RangeEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1791
+ x: EncodingChannel<TData>;
1792
+ y: EncodingChannel<TData>;
1793
+ }
1794
+ /**
1795
+ * Encoding for waffle marks (unit grids for part-to-whole counts).
1796
+ * - `color`: required (nominal/ordinal, the category)
1797
+ * - `theta`: the canonical quantitative share, the same part-to-whole value
1798
+ * channel arc marks use. `y` is a deprecated alias, accepted for backward
1799
+ * compatibility.
1800
+ */
1801
+ interface WaffleEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1802
+ color: EncodingChannel<TData> | ConditionalValueDef<TData>;
1803
+ }
1804
+ /**
1805
+ * Encoding for parliament marks (hemicycle seat-dot charts).
1806
+ * - `color`: required (nominal/ordinal, the party)
1807
+ * - `theta`: the canonical quantitative seat count, the same part-to-whole
1808
+ * value channel arc/waffle marks use. `y` is a deprecated alias, accepted
1809
+ * for backward compatibility.
1810
+ */
1811
+ interface ParliamentEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1812
+ color: EncodingChannel<TData> | ConditionalValueDef<TData>;
1813
+ }
1814
+ /**
1815
+ * Encoding for calendar marks (GitHub-style calendar heatmaps).
1816
+ * - `x`: required (temporal, daily dates, one row per day)
1817
+ * - `color`: required (quantitative, the per-day value)
1818
+ *
1819
+ * The calendar computes its own weeks-x-weekdays geometry: no positional
1820
+ * scales or axes. Multi-year data partitions into one stacked band per year,
1821
+ * all bands sharing a single color scale and legend.
1822
+ */
1823
+ interface CalendarEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1824
+ x: EncodingChannel<TData>;
1825
+ color: EncodingChannel<TData>;
1826
+ }
1488
1827
  /**
1489
1828
  * Encoding for text marks (data-positioned labels).
1490
1829
  * - `text`: required (the field to render as text)
@@ -1511,6 +1850,28 @@ interface RectEncoding<TData extends DataRow = DataRow> extends Encoding<TData>
1511
1850
  x: EncodingChannel<TData>;
1512
1851
  y: EncodingChannel<TData>;
1513
1852
  }
1853
+ /**
1854
+ * Author-facing accessibility overrides.
1855
+ *
1856
+ * Everything here is optional; when omitted, openchart auto-generates alt
1857
+ * text, ARIA labels, and a screen-reader data table from the spec and data.
1858
+ */
1859
+ interface A11yConfig {
1860
+ /**
1861
+ * Alt text announced by screen readers for the chart as a whole. Replaces
1862
+ * the auto-generated description. Also settable via the top-level
1863
+ * `description` field (Vega-Lite convention); this field wins when both
1864
+ * are present.
1865
+ */
1866
+ description?: string;
1867
+ /**
1868
+ * Hide the chart from assistive technology entirely (`aria-hidden`).
1869
+ * Use only when the chart is decorative or the surrounding content
1870
+ * already conveys the same information. Also suppresses the hidden
1871
+ * screen-reader data table and keyboard mark navigation.
1872
+ */
1873
+ hidden?: boolean;
1874
+ }
1514
1875
  /**
1515
1876
  * Properties shared across all ChartSpec mark variants.
1516
1877
  * Extracted to avoid repeating them in every union member.
@@ -1518,12 +1879,47 @@ interface RectEncoding<TData extends DataRow = DataRow> extends Encoding<TData>
1518
1879
  * @internal
1519
1880
  */
1520
1881
  interface BaseChartSpec<TData extends DataRow = DataRow> {
1521
- /** Data array: each element is a row with field values. */
1882
+ /**
1883
+ * Data array: each element is a row with field values.
1884
+ * The Vega-Lite object form `{ values: [...] }` is also accepted at runtime
1885
+ * and unwrapped during the pre-validation sugar expansion. `{ url }` is not
1886
+ * supported (openchart does not fetch data).
1887
+ */
1522
1888
  data: TData[];
1523
1889
  /** Data transforms applied in order before encoding (filter, bin, calculate, timeUnit). */
1524
1890
  transform?: Transform[];
1525
1891
  /** Editorial chrome (title, subtitle, source, etc.). */
1526
1892
  chrome?: Chrome;
1893
+ /**
1894
+ * Top-level title sugar (Vega-Lite aligned). Expanded into `chrome.title`
1895
+ * (and `chrome.subtitle` for the object form). `chrome.title` wins when
1896
+ * both are set.
1897
+ */
1898
+ title?: string | {
1899
+ text: string;
1900
+ subtitle?: string;
1901
+ };
1902
+ /** Top-level subtitle sugar. Expanded into `chrome.subtitle`; `chrome.subtitle` wins. */
1903
+ subtitle?: string;
1904
+ /**
1905
+ * Alt text for the rendered chart (Vega-Lite aligned). Sugar for
1906
+ * `a11y.description`; `a11y.description` wins when both are set.
1907
+ * When absent, alt text is auto-generated from the spec and data.
1908
+ */
1909
+ description?: string;
1910
+ /** Accessibility overrides. See {@link A11yConfig}. */
1911
+ a11y?: A11yConfig;
1912
+ /**
1913
+ * Fixed render width in pixels (Vega-Lite aligned). Overrides the
1914
+ * container-derived width. When both `width` and `height` are set,
1915
+ * `responsive: false` is implied unless `responsive` is set explicitly.
1916
+ * Omit for the default container-driven sizing.
1917
+ */
1918
+ width?: number;
1919
+ /** Fixed render height in pixels (Vega-Lite aligned). See `width`. */
1920
+ height?: number;
1921
+ /** Accepted for Vega-Lite compatibility and ignored (with a compile warning). */
1922
+ $schema?: string;
1527
1923
  /**
1528
1924
  * KPI/metric cells rendered as a horizontal row between subtitle and chart
1529
1925
  * area. Each cell shows a label/value pair with optional delta and secondary
@@ -1560,7 +1956,12 @@ interface BaseChartSpec<TData extends DataRow = DataRow> {
1560
1956
  darkMode?: DarkMode;
1561
1957
  /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
1562
1958
  watermark?: boolean;
1563
- /** Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed. */
1959
+ /**
1960
+ * Series names to hide from rendering by default. This is the authored
1961
+ * persistence target for legend visibility. Runtime legend toggles
1962
+ * (legend-toggle edit events) layer on top of this via ephemeral runtime
1963
+ * sets but never modify it.
1964
+ */
1564
1965
  hiddenSeries?: string[];
1565
1966
  /** Per-series visual overrides, keyed by series name (the color field value). */
1566
1967
  seriesStyles?: Record<string, SeriesStyle>;
@@ -1584,6 +1985,22 @@ interface BaseChartSpec<TData extends DataRow = DataRow> {
1584
1985
  * explicitly set.
1585
1986
  */
1586
1987
  crosshair?: boolean;
1988
+ /**
1989
+ * Searchable series highlight ("find your country"). Renders a compact
1990
+ * search input above the chart with typeahead over the color channel's
1991
+ * distinct values. Selecting a value adds it to the highlight set
1992
+ * (multi-select chips, removable); clearing restores the authored
1993
+ * `encoding.color.highlight` baseline. Requires a categorical color
1994
+ * encoding. Mutually exclusive with edit mode (search wins).
1995
+ */
1996
+ seriesSearch?: boolean | SeriesSearchConfig;
1997
+ /**
1998
+ * "You draw it" interactive engagement format: the reader draws their
1999
+ * guess of the trend before the real line reveals. Line marks only,
2000
+ * single-series only. Mutually exclusive with edit mode (validation
2001
+ * warning if both are enabled; youDrawIt is disabled).
2002
+ */
2003
+ youDrawIt?: YouDrawItConfig;
1587
2004
  /**
1588
2005
  * Display mode controlling how much chart chrome is rendered.
1589
2006
  *
@@ -1684,6 +2101,21 @@ type ChartSpec<TData extends DataRow = DataRow> = (BaseChartSpec<TData> & {
1684
2101
  type: 'lollipop';
1685
2102
  });
1686
2103
  encoding: LollipopEncoding<TData>;
2104
+ }) | (BaseChartSpec<TData> & {
2105
+ mark: 'beeswarm' | (MarkDef & {
2106
+ type: 'beeswarm';
2107
+ });
2108
+ encoding: BeeswarmEncoding<TData>;
2109
+ }) | (BaseChartSpec<TData> & {
2110
+ mark: 'range' | (MarkDef & {
2111
+ type: 'range';
2112
+ });
2113
+ encoding: RangeEncoding<TData>;
2114
+ }) | (BaseChartSpec<TData> & {
2115
+ mark: 'waffle' | (MarkDef & {
2116
+ type: 'waffle';
2117
+ });
2118
+ encoding: WaffleEncoding<TData>;
1687
2119
  }) | (BaseChartSpec<TData> & {
1688
2120
  mark: 'text' | (MarkDef & {
1689
2121
  type: 'text';
@@ -1704,6 +2136,16 @@ type ChartSpec<TData extends DataRow = DataRow> = (BaseChartSpec<TData> & {
1704
2136
  type: 'rule';
1705
2137
  });
1706
2138
  encoding: Encoding<TData>;
2139
+ }) | (BaseChartSpec<TData> & {
2140
+ mark: 'calendar' | (MarkDef & {
2141
+ type: 'calendar';
2142
+ });
2143
+ encoding: CalendarEncoding<TData>;
2144
+ }) | (BaseChartSpec<TData> & {
2145
+ mark: 'parliament' | (MarkDef & {
2146
+ type: 'parliament';
2147
+ });
2148
+ encoding: ParliamentEncoding<TData>;
1707
2149
  });
1708
2150
  /**
1709
2151
  * Table specification: input for data table visualizations.
@@ -1834,7 +2276,29 @@ interface LayerSpec<TData extends DataRow = DataRow> {
1834
2276
  transform?: Transform[];
1835
2277
  /** Editorial chrome (title, subtitle, source, etc.). */
1836
2278
  chrome?: Chrome;
1837
- /** Annotations on the layered view. */
2279
+ /** Top-level title sugar (VL aligned). Expanded into `chrome.title`; `chrome.title` wins. */
2280
+ title?: string | {
2281
+ text: string;
2282
+ subtitle?: string;
2283
+ };
2284
+ /** Top-level subtitle sugar. Expanded into `chrome.subtitle`; `chrome.subtitle` wins. */
2285
+ subtitle?: string;
2286
+ /** Fixed render width in pixels (VL aligned). Overrides the container-derived width. */
2287
+ width?: number;
2288
+ /** Fixed render height in pixels (VL aligned). Overrides the container-derived height. */
2289
+ height?: number;
2290
+ /** Accepted for Vega-Lite compatibility and ignored (with a compile warning). */
2291
+ $schema?: string;
2292
+ /**
2293
+ * Annotations for the layered chart. When present, these are the
2294
+ * annotations rendered and available for editing. When absent, falls back
2295
+ * to the first leaf layer's annotations. Annotations on second and later
2296
+ * leaf layers are not rendered.
2297
+ *
2298
+ * For edit persistence: the `data-annotation-index` stamped on each
2299
+ * rendered annotation maps to this array (or the first leaf's if this is
2300
+ * unset). Place annotations here for reliable index-based edit matching.
2301
+ */
1838
2302
  annotations?: Annotation[];
1839
2303
  /** Label display configuration. `false` disables all labels, `true` uses defaults. */
1840
2304
  labels?: LabelSpec;
@@ -1850,7 +2314,12 @@ interface LayerSpec<TData extends DataRow = DataRow> {
1850
2314
  watermark?: boolean;
1851
2315
  /** Resolution strategy for shared scales/axes/legends. */
1852
2316
  resolve?: ResolveConfig;
1853
- /** Hidden series names. */
2317
+ /**
2318
+ * Series names to hide from rendering by default. This is the authored
2319
+ * persistence target for legend visibility. Runtime legend toggles
2320
+ * (legend-toggle edit events) layer on top of this via ephemeral runtime
2321
+ * sets but never modify it.
2322
+ */
1854
2323
  hiddenSeries?: string[];
1855
2324
  /** Endpoint labels column, inherited by child layers that don't set their own. */
1856
2325
  endpointLabels?: boolean | EndpointLabelsConfig;
@@ -1924,6 +2393,8 @@ interface SankeySpec {
1924
2393
  * number (data value 28 renders as "28%"). For currency: "$,.0f" or "$~s".
1925
2394
  * For SI suffixes: "~s" (renders 1000 as "1k"). When not set, values use
1926
2395
  * the default number formatter.
2396
+ * @deprecated Use the encoding channel's `format` field instead. This
2397
+ * property is expanded into the encoding during compilation.
1927
2398
  */
1928
2399
  valueFormat?: string;
1929
2400
  }
@@ -1983,6 +2454,102 @@ interface TileMapSpec {
1983
2454
  /**
1984
2455
  * d3-format string applied to tile values, legend labels, and tooltips.
1985
2456
  * Examples: ".1f" for one decimal, "$,.0f" for currency, "~s" for SI.
2457
+ * @deprecated Use the encoding channel's `format` field instead. This
2458
+ * property is expanded into the encoding during compilation.
2459
+ */
2460
+ valueFormat?: string;
2461
+ }
2462
+ /** Supported map projections. */
2463
+ type MapProjection = 'albersUsa' | 'mercator' | 'equalEarth' | 'identity';
2464
+ /** Focus target for map camera: feature id(s) to zoom/pan to. */
2465
+ /**
2466
+ * Fit the point layer's cluster. `true` fits every point; `{ field, value }`
2467
+ * fits only the points whose data row has `row[field] === value` (e.g. only the
2468
+ * campuses rated "F"), which lets a story pan between sub-clusters.
2469
+ */
2470
+ type MapPointsFocus = true | {
2471
+ field: string;
2472
+ value: string | number;
2473
+ };
2474
+ type MapFocus = string | number | Array<string | number> | {
2475
+ features: string | number | Array<string | number>;
2476
+ padding?: number;
2477
+ } | {
2478
+ points: MapPointsFocus;
2479
+ padding?: number;
2480
+ };
2481
+ /** Geo configuration for map specs. */
2482
+ interface MapGeo {
2483
+ /** TopoJSON topology object. User imports this from us-atlas, world-atlas, or their own source. */
2484
+ features: unknown;
2485
+ /** Field in the TopoJSON feature properties to use as the join key. Defaults to 'id'. */
2486
+ idField?: string;
2487
+ /** Map projection. Defaults to 'albersUsa'. */
2488
+ projection?: MapProjection;
2489
+ /**
2490
+ * Focus the camera. A feature id (or array) fits those features; `{ features, padding }`
2491
+ * adds breathing room; `{ points: true, padding }` fits the points layer's cluster
2492
+ * instead of any feature (use when points occupy a small part of a large feature, so
2493
+ * fitting the feature would leave the cluster small and off-center);
2494
+ * `{ points: { field, value }, padding }` fits only the matching point subset, so a
2495
+ * story can pan between sub-clusters. null clears focus from a prior story step.
2496
+ */
2497
+ focus?: MapFocus | null;
2498
+ }
2499
+ /** Encoding channels specific to map visualizations. */
2500
+ interface MapEncoding {
2501
+ /** Join key field in the data. Required for choropleth; optional in basemap-only mode (points layer with empty data). */
2502
+ key?: EncodingChannel;
2503
+ /** Color encoding (quantitative or nominal). Drives fill color of features. Optional when a points layer is present (basemap-only mode). */
2504
+ color?: EncodingChannel;
2505
+ /** Tooltip encoding (optional). */
2506
+ tooltip?: EncodingChannel | EncodingChannel[];
2507
+ }
2508
+ /** Point/symbol layer rendered above choropleth features. */
2509
+ interface MapPointsLayer {
2510
+ /** Tabular data for the points. Independent of the choropleth data join. */
2511
+ data: DataRow[];
2512
+ /** Field holding longitude (x coordinate). */
2513
+ longitude: EncodingChannel;
2514
+ /** Field holding latitude (y coordinate). */
2515
+ latitude: EncodingChannel;
2516
+ /** Optional quantitative size encoding. Area-proportional: r proportional to sqrt(value). No size legend is rendered in v1; explain the scale in chrome.subtitle if needed. */
2517
+ size?: EncodingChannel;
2518
+ /** Optional color encoding (nominal or quantitative). Independent scale from choropleth. */
2519
+ color?: EncodingChannel;
2520
+ /** Tooltip channel(s), same shape as MapEncoding.tooltip. */
2521
+ tooltip?: EncodingChannel | EncodingChannel[];
2522
+ /** Stable id for event callbacks. Data-update transitions for points are not yet supported. */
2523
+ key?: EncodingChannel;
2524
+ /** Fill opacity for point circles. Default 0.65. Range 0-1. */
2525
+ opacity?: number;
2526
+ }
2527
+ interface MapSpec {
2528
+ /** Discriminant: always "map". */
2529
+ type: 'map';
2530
+ /** Geo configuration: TopoJSON features, join key, and projection. */
2531
+ geo: MapGeo;
2532
+ /** Tabular data to join to geo features. */
2533
+ data: DataRow[];
2534
+ /** Encoding channels mapping data fields to visual properties. */
2535
+ encoding: MapEncoding;
2536
+ /** Editorial chrome (title, subtitle, source, byline, footer). */
2537
+ chrome?: Chrome;
2538
+ /** Legend display configuration. */
2539
+ legend?: LegendConfig;
2540
+ /** Theme configuration overrides. */
2541
+ theme?: ThemeConfig;
2542
+ /** Dark mode behavior. Defaults to "off". */
2543
+ darkMode?: DarkMode;
2544
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
2545
+ watermark?: boolean;
2546
+ /** Optional point/symbol layer rendered above choropleth features. */
2547
+ points?: MapPointsLayer;
2548
+ /** Animation configuration for entrance animations. */
2549
+ animation?: AnimationSpec;
2550
+ /**
2551
+ * d3-format string applied to color values in tooltips and legend.
2552
+ * @deprecated Use the encoding channel's `format` field instead.
1986
2553
  */
1987
2554
  valueFormat?: string;
1988
2555
  }
@@ -2015,6 +2582,8 @@ interface BarListSpec {
2015
2582
  /**
2016
2583
  * d3-format string applied to bar values and tooltips.
2017
2584
  * Examples: ".1f" for one decimal, "$,.0f" for currency, "~s" for SI.
2585
+ * @deprecated Use the encoding channel's `format` field instead. This
2586
+ * property is expanded into the encoding during compilation.
2018
2587
  */
2019
2588
  valueFormat?: string;
2020
2589
  }
@@ -2040,9 +2609,17 @@ interface BarListEncoding {
2040
2609
  * - GraphSpec: has `type: 'graph'`
2041
2610
  * - SankeySpec: has `type: 'sankey'`
2042
2611
  * - TileMapSpec: has `type: 'tilemap'`
2612
+ * - MapSpec: has `type: 'map'`
2043
2613
  * - BarListSpec: has `type: 'barlist'`
2614
+ *
2615
+ * Election parliament (hemicycle) charts are `ChartSpec` with `mark:
2616
+ * 'parliament'`, not a separate top-level type: seat data (party + seats)
2617
+ * maps naturally onto encoding.color/y like arc and waffle, and riding the
2618
+ * existing chart pipeline gets scales, legend, tooltips, a11y, and dark mode
2619
+ * for free instead of a second bespoke compile path (see
2620
+ * `packages/engine/src/charts/parliament/`).
2044
2621
  */
2045
- type VizSpec = ChartSpec | LayerSpec | TableSpec | GraphSpec | SankeySpec | TileMapSpec | BarListSpec;
2622
+ type VizSpec = ChartSpec | LayerSpec | TableSpec | GraphSpec | SankeySpec | TileMapSpec | MapSpec | BarListSpec;
2046
2623
  /**
2047
2624
  * Chart spec without runtime data, for persistence/storage.
2048
2625
  * Generic: `ChartSpecWithoutData<MyRow>` constrains encoding field names.
@@ -2056,10 +2633,14 @@ type GraphSpecWithoutData = Omit<GraphSpec, 'nodes' | 'edges'>;
2056
2633
  type SankeySpecWithoutData = Omit<SankeySpec, 'data'>;
2057
2634
  /** TileMap spec without runtime data, for persistence/storage. */
2058
2635
  type TileMapSpecWithoutData = Omit<TileMapSpec, 'data'>;
2636
+ /** Map spec without runtime data, for persistence/storage. */
2637
+ type MapSpecWithoutData = Omit<MapSpec, 'data' | 'points'> & {
2638
+ points?: Omit<MapPointsLayer, 'data'>;
2639
+ };
2059
2640
  /** BarList spec without runtime data, for persistence/storage. */
2060
2641
  type BarListSpecWithoutData = Omit<BarListSpec, 'data'>;
2061
2642
  /** Union of data-stripped spec types for persistence/storage. */
2062
- type StoredVizSpec = ChartSpecWithoutData | TableSpecWithoutData | GraphSpecWithoutData | SankeySpecWithoutData | TileMapSpecWithoutData | BarListSpecWithoutData;
2643
+ type StoredVizSpec = ChartSpecWithoutData | TableSpecWithoutData | GraphSpecWithoutData | SankeySpecWithoutData | TileMapSpecWithoutData | MapSpecWithoutData | BarListSpecWithoutData;
2063
2644
  /** Logical AND combinator for filter predicates. */
2064
2645
  interface LogicalAnd<T> {
2065
2646
  and: T[];
@@ -2215,9 +2796,10 @@ interface Condition<TData extends DataRow = DataRow> {
2215
2796
  test: FilterPredicate;
2216
2797
  /**
2217
2798
  * Static value to use when the condition is true.
2218
- * Accepted values: CSS color string, opacity (0-1), size number, or boolean flag.
2799
+ * Accepted values: CSS color string, opacity (0-1), size number, boolean flag,
2800
+ * or a gradient definition for color channels.
2219
2801
  */
2220
- value?: string | number | boolean | null;
2802
+ value?: string | number | boolean | null | GradientDef;
2221
2803
  /**
2222
2804
  * Data field to use when the condition is true.
2223
2805
  * Constrained to column names of `TData` when using `ChartSpec<TData>`.
@@ -2237,24 +2819,34 @@ interface ConditionalValueDef<TData extends DataRow = DataRow> {
2237
2819
  condition: Condition<TData> | Condition<TData>[];
2238
2820
  /**
2239
2821
  * Default value when no condition matches.
2240
- * Accepted values: CSS color string, opacity (0-1), size number, or boolean flag.
2822
+ * Accepted values: CSS color string, opacity (0-1), size number, boolean flag,
2823
+ * or a gradient definition for color channels.
2241
2824
  */
2242
- value?: string | number | boolean | null;
2825
+ value?: string | number | boolean | null | GradientDef;
2243
2826
  }
2244
2827
  /**
2245
2828
  * Check if a channel definition is a regular EncodingChannel (has 'field' at top level).
2246
- * Use this to narrow `EncodingChannel | ConditionalValueDef` in encoding channels
2247
- * that support conditional encoding (color, size, opacity).
2829
+ * Use this to narrow `EncodingChannel | ConditionalValueDef | ValueDef` in encoding
2830
+ * channels that support conditional encoding (color, size, opacity).
2248
2831
  */
2249
- declare function isEncodingChannel<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | undefined): def is EncodingChannel<TData>;
2832
+ declare function isEncodingChannel<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | ValueDef | undefined): def is EncodingChannel<TData>;
2250
2833
  /**
2251
2834
  * Check if a channel definition is a ConditionalValueDef.
2252
2835
  */
2253
- declare function isConditionalDef<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | undefined): def is ConditionalValueDef<TData>;
2836
+ declare function isConditionalDef<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | ValueDef | undefined): def is ConditionalValueDef<TData>;
2837
+ /**
2838
+ * Check if a channel definition is a bare ValueDef (constant value, no field
2839
+ * or condition). These are expanded to mark-level properties during the
2840
+ * pre-validation sugar expansion.
2841
+ */
2842
+ declare function isValueDef<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | ValueDef | undefined): def is ValueDef;
2254
2843
  /** All valid mark type strings for runtime checking. */
2255
2844
  declare const MARK_TYPES: ReadonlySet<string>;
2256
- /** @deprecated Use MARK_TYPES instead. */
2257
- declare const CHART_TYPES: ReadonlySet<string>;
2845
+ /**
2846
+ * Mark types that lay out inside the chart area without positional axes
2847
+ * (no axis or gridline computation; margins shrink to padding).
2848
+ */
2849
+ declare function isAxislessMark(markType: MarkType): boolean;
2258
2850
  /**
2259
2851
  * Extract the mark type string from a mark field (string or MarkDef).
2260
2852
  */
@@ -2277,11 +2869,17 @@ declare function isSankeySpec(spec: VizSpec | Record<string, unknown>): spec is
2277
2869
  declare function isTileMapSpec(spec: VizSpec | Record<string, unknown>): spec is TileMapSpec;
2278
2870
  /** Check if a spec is a BarListSpec. */
2279
2871
  declare function isBarListSpec(spec: VizSpec | Record<string, unknown>): spec is BarListSpec;
2872
+ /** Check if a spec is a MapSpec. */
2873
+ declare function isMapSpec(spec: VizSpec | Record<string, unknown>): spec is MapSpec;
2280
2874
  /** Check if an annotation is a TextAnnotation. */
2281
2875
  declare function isTextAnnotation(annotation: Annotation): annotation is TextAnnotation;
2282
2876
  /** Check if an annotation is a RangeAnnotation. */
2283
2877
  declare function isRangeAnnotation(annotation: Annotation): annotation is RangeAnnotation;
2284
- /** Check if an annotation is a RefLineAnnotation. */
2878
+ /**
2879
+ * Check if an annotation is a RefLineAnnotation. Also accepts the deprecated
2880
+ * `'rule'` type at runtime (pre-sugar-expansion input), since `annotation.type`
2881
+ * may not yet be narrowed to the canonical `'refline'` form.
2882
+ */
2285
2883
  declare function isRefLineAnnotation(annotation: Annotation): annotation is RefLineAnnotation;
2286
2884
  /** Human-readable display names for mark types (used in alt text, error messages). */
2287
2885
  declare const MARK_DISPLAY_NAMES: Record<MarkType, string>;
@@ -2309,15 +2907,11 @@ interface EncodingRule {
2309
2907
  y2?: ChannelRule;
2310
2908
  color: ChannelRule;
2311
2909
  size: ChannelRule;
2312
- shape?: ChannelRule;
2313
2910
  opacity?: ChannelRule;
2314
2911
  strokeDash?: ChannelRule;
2315
2912
  text?: ChannelRule;
2316
2913
  tooltip?: ChannelRule;
2317
- href?: ChannelRule;
2318
- order?: ChannelRule;
2319
2914
  theta?: ChannelRule;
2320
- radius?: ChannelRule;
2321
2915
  detail: ChannelRule;
2322
2916
  }
2323
2917
  /**
@@ -2336,10 +2930,17 @@ interface EncodingRule {
2336
2930
  * - point: both axes are quantitative
2337
2931
  * - text/rule: fully optional positioning
2338
2932
  * - tick/rect: both axes required, any type
2933
+ * - beeswarm: both axes optional at the channel level; the compiler enforces
2934
+ * the combination (exactly one quantitative value axis, the other axis an
2935
+ * optional nominal/ordinal lane channel)
2936
+ * - waffle: no positional axes; y is the share value (quantitative, with
2937
+ * theta accepted as an alias like arc), color is the category
2938
+ * - calendar: x is the daily date (temporal), color the per-day value
2939
+ * (quantitative); no y channel (the calendar owns its own geometry)
2940
+ * - parliament: no positional axes; theta (aliased to y) is the seat count
2941
+ * (quantitative), color the party
2339
2942
  */
2340
2943
  declare const MARK_ENCODING_RULES: Record<MarkType, EncodingRule>;
2341
- /** @deprecated Use MARK_ENCODING_RULES instead. */
2342
- declare const CHART_ENCODING_RULES: Record<MarkType, EncodingRule>;
2343
2944
  /** Encoding rule for a single graph visual channel. */
2344
2945
  interface GraphChannelRule {
2345
2946
  /** Whether this channel is required. */
@@ -2379,26 +2980,36 @@ type ElementRef = {
2379
2980
  type: 'annotation';
2380
2981
  index: number;
2381
2982
  id?: string;
2983
+ /** RFC 6901 JSON Pointer to the edited property in the spec. */
2984
+ path?: string;
2382
2985
  } | {
2383
2986
  type: 'chrome';
2384
2987
  key: ChromeKey;
2988
+ /** RFC 6901 JSON Pointer to the edited property in the spec. */
2989
+ path?: string;
2385
2990
  } | {
2386
2991
  type: 'series-label';
2387
2992
  series: string;
2993
+ /** RFC 6901 JSON Pointer to the edited property in the spec. */
2994
+ path?: string;
2388
2995
  } | {
2389
2996
  type: 'legend';
2997
+ /** RFC 6901 JSON Pointer to the edited property in the spec. */
2998
+ path?: string;
2390
2999
  } | {
2391
3000
  type: 'legend-entry';
2392
3001
  series: string;
2393
3002
  index: number;
3003
+ /** RFC 6901 JSON Pointer to the edited property in the spec. */
3004
+ path?: string;
2394
3005
  };
2395
3006
  /** Helper constructors for ergonomic ElementRef creation. */
2396
3007
  declare const elementRef: {
2397
- readonly annotation: (index: number, id?: string) => ElementRef;
2398
- readonly chrome: (key: ChromeKey) => ElementRef;
2399
- readonly seriesLabel: (series: string) => ElementRef;
2400
- readonly legend: () => ElementRef;
2401
- readonly legendEntry: (series: string, index: number) => ElementRef;
3008
+ readonly annotation: (index: number, id?: string, path?: string) => ElementRef;
3009
+ readonly chrome: (key: ChromeKey, path?: string) => ElementRef;
3010
+ readonly seriesLabel: (series: string, path?: string) => ElementRef;
3011
+ readonly legend: (path?: string) => ElementRef;
3012
+ readonly legendEntry: (series: string, index: number, path?: string) => ElementRef;
2402
3013
  };
2403
3014
  /**
2404
3015
  * Discriminated union of all element edit events.
@@ -2413,21 +3024,31 @@ declare const elementRef: {
2413
3024
  */
2414
3025
  type ElementEdit = {
2415
3026
  type: 'annotation';
3027
+ element: ElementRef;
2416
3028
  annotation: TextAnnotation;
2417
3029
  offset: AnnotationOffset;
2418
3030
  } | {
2419
3031
  type: 'annotation-connector';
3032
+ element: ElementRef;
2420
3033
  annotation: TextAnnotation;
2421
3034
  endpoint: 'from' | 'to';
2422
3035
  offset: AnnotationOffset;
2423
3036
  } | {
2424
3037
  type: 'range-label';
3038
+ element: ElementRef;
2425
3039
  annotation: RangeAnnotation;
2426
3040
  labelOffset: AnnotationOffset;
2427
3041
  } | {
2428
3042
  type: 'refline-label';
3043
+ element: ElementRef;
2429
3044
  annotation: RefLineAnnotation;
2430
3045
  labelOffset: AnnotationOffset;
3046
+ } | {
3047
+ type: 'annotation-anchor';
3048
+ element: ElementRef;
3049
+ annotation: TextAnnotation;
3050
+ x: string | number;
3051
+ y: string | number;
2431
3052
  } | {
2432
3053
  type: 'chrome';
2433
3054
  key: ChromeKey;
@@ -2440,10 +3061,21 @@ type ElementEdit = {
2440
3061
  } | {
2441
3062
  type: 'legend';
2442
3063
  offset: AnnotationOffset;
2443
- } | {
3064
+ }
3065
+ /**
3066
+ * Transient legend visibility toggle. Reports the same runtime show/hide
3067
+ * a reader triggers; not persisted by openchart. To author default-hidden
3068
+ * series, set `spec.hiddenSeries` directly. Hosts building spec
3069
+ * persistence should generally not write legend-toggle edits back into
3070
+ * the authored spec. `hidden: true` means the series was just hidden.
3071
+ */
3072
+ | {
2444
3073
  type: 'legend-toggle';
2445
3074
  series: string;
2446
3075
  hidden: boolean;
3076
+ } | {
3077
+ type: 'add';
3078
+ annotation: TextAnnotation;
2447
3079
  } | {
2448
3080
  type: 'delete';
2449
3081
  element: ElementRef;
@@ -2479,6 +3111,14 @@ interface MarkEvent {
2479
3111
  * or ChartProps (React component) to receive interaction callbacks.
2480
3112
  */
2481
3113
  interface ChartEventHandlers {
3114
+ /**
3115
+ * Explicitly enable or disable edit interactions (drag, select, keyboard
3116
+ * delete/edit). When true, the interaction layer is wired even without
3117
+ * onEdit. When false, all edit interactions are suppressed even when
3118
+ * onEdit is provided. When omitted, each interaction block uses its
3119
+ * existing callback-presence gate.
3120
+ */
3121
+ editable?: boolean;
2482
3122
  /** Called when a data mark is clicked. */
2483
3123
  onMarkClick?: (event: MarkEvent) => void;
2484
3124
  /** Called when the mouse enters a data mark. */
@@ -2487,6 +3127,12 @@ interface ChartEventHandlers {
2487
3127
  onMarkLeave?: () => void;
2488
3128
  /** Called when a legend entry is toggled (clicked to show/hide a series). */
2489
3129
  onLegendToggle?: (series: string, visible: boolean) => void;
3130
+ /**
3131
+ * Called when the series search (`seriesSearch`) changes the highlight set.
3132
+ * Receives the full set of highlighted values after the change, including
3133
+ * the authored `encoding.color.highlight` baseline.
3134
+ */
3135
+ onHighlightChange?: (values: string[]) => void;
2490
3136
  /** Called when an annotation element is clicked. */
2491
3137
  onAnnotationClick?: (annotation: Annotation, event: MouseEvent) => void;
2492
3138
  /** Called when a text annotation label is dragged to a new position. */
@@ -2499,6 +3145,15 @@ interface ChartEventHandlers {
2499
3145
  onDeselect?: (element: ElementRef) => void;
2500
3146
  /** Fired when inline text editing commits. Also flows through onEdit as a 'text-edit' event. */
2501
3147
  onTextEdit?: (element: ElementRef, oldText: string, newText: string) => void;
3148
+ /**
3149
+ * Fired when a "you draw it" (`youDrawIt`) drawing is revealed (button click
3150
+ * or programmatic reveal), with the reader's guess in data coordinates,
3151
+ * ordered by x. For newsrooms aggregating reader guesses.
3152
+ */
3153
+ onReveal?: (guess: Array<{
3154
+ x: string | number;
3155
+ y: number;
3156
+ }>) => void;
2502
3157
  }
2503
3158
 
2504
3159
  /** A rectangle defined by position and dimensions. */
@@ -2593,6 +3248,12 @@ interface AxisTick {
2593
3248
  position: number;
2594
3249
  /** Formatted label string for display. */
2595
3250
  label: string;
3251
+ /**
3252
+ * The untruncated label, set only when `label` was shortened with an ellipsis
3253
+ * to fit the rotated-tick reservation. Renderers should surface this as the
3254
+ * tick's accessible/tooltip text so the full category name is never lost.
3255
+ */
3256
+ fullLabel?: string;
2596
3257
  /** Secondary label text from axis.labelField. */
2597
3258
  subtitle?: string;
2598
3259
  }
@@ -2653,6 +3314,23 @@ interface AxisLayout {
2653
3314
  angle?: number;
2654
3315
  };
2655
3316
  }
3317
+ /** The four built-in fill pattern shapes for series reinforcement. */
3318
+ type FillPatternType = 'diagonal' | 'dot' | 'crosshatch' | 'vertical';
3319
+ /**
3320
+ * A resolved per-mark fill pattern (engine output for `mark.fillPattern: 'auto'`).
3321
+ *
3322
+ * The renderer builds one SVG `<pattern>` per unique (type, base, line)
3323
+ * combination: a tile of `base` color overlaid with `line`-colored pattern
3324
+ * geometry. Marks referencing the same resolved pattern share one def.
3325
+ */
3326
+ interface ResolvedFillPattern {
3327
+ /** Pattern shape assigned to this mark's series. */
3328
+ type: FillPatternType;
3329
+ /** Base tile color: the series' solid color. */
3330
+ base: string;
3331
+ /** Pattern line/dot color, contrast-picked against `base`. */
3332
+ line: string;
3333
+ }
2656
3334
  /** Accessibility attributes for a mark. */
2657
3335
  interface MarkAria {
2658
3336
  /** ARIA label for the mark. Optional when `decorative: true` — decorative
@@ -2738,6 +3416,8 @@ interface AreaMark {
2738
3416
  interpolate?: string;
2739
3417
  /** Fill color or gradient. */
2740
3418
  fill: string | GradientDef;
3419
+ /** Resolved fill pattern. Present only when `mark.fillPattern: 'auto'` assigned one. */
3420
+ pattern?: ResolvedFillPattern;
2741
3421
  /** Fill opacity. */
2742
3422
  fillOpacity: number;
2743
3423
  /** Optional stroke for the top boundary. */
@@ -2783,6 +3463,8 @@ interface RectMark {
2783
3463
  height: number;
2784
3464
  /** Fill color or gradient. */
2785
3465
  fill: string | GradientDef;
3466
+ /** Resolved fill pattern. Present only when `mark.fillPattern: 'auto'` assigned one. */
3467
+ pattern?: ResolvedFillPattern;
2786
3468
  /** Stroke color. */
2787
3469
  stroke?: string;
2788
3470
  /** Stroke width. */
@@ -2840,6 +3522,8 @@ interface ArcMark {
2840
3522
  endAngle: number;
2841
3523
  /** Fill color or gradient. */
2842
3524
  fill: string | GradientDef;
3525
+ /** Resolved fill pattern. Present only when `mark.fillPattern: 'auto'` assigned one. */
3526
+ pattern?: ResolvedFillPattern;
2843
3527
  /** Stroke color (usually white for slice separation). */
2844
3528
  stroke: string;
2845
3529
  /** Stroke width. */
@@ -2896,10 +3580,14 @@ interface TextMarkLayout {
2896
3580
  type: 'textMark';
2897
3581
  /** Stable identity key for data-update transitions. */
2898
3582
  key?: string;
2899
- /** X position. */
3583
+ /** X position, including any `dx` offset. */
2900
3584
  x: number;
2901
- /** Y position. */
3585
+ /** Y position, including any `dy` offset. */
2902
3586
  y: number;
3587
+ /** Data-space anchor before `dx` was applied. Set only when the mark is offset. */
3588
+ anchorX?: number;
3589
+ /** Data-space anchor before `dy` was applied. Set only when the mark is offset. */
3590
+ anchorY?: number;
2903
3591
  /** Text content to display. */
2904
3592
  text: string;
2905
3593
  /** Fill color. */
@@ -2912,6 +3600,11 @@ interface TextMarkLayout {
2912
3600
  fontFamily?: string;
2913
3601
  /** Horizontal text alignment. */
2914
3602
  textAnchor: 'start' | 'middle' | 'end';
3603
+ /**
3604
+ * Vertical baseline. Omit for the SVG default (alphabetic), which the calendar
3605
+ * and parliament marks rely on — they hand-compute baseline-relative offsets.
3606
+ */
3607
+ dominantBaseline?: 'auto' | 'hanging' | 'central' | 'text-after-edge';
2915
3608
  /** Rotation angle in degrees. */
2916
3609
  angle?: number;
2917
3610
  /** Original data row. */
@@ -3016,10 +3709,22 @@ interface ResolvedLabel {
3016
3709
  to: Point;
3017
3710
  /** Actual data point the connector is calling out. Renderer uses this for the endpoint marker. */
3018
3711
  endpoint?: Point;
3712
+ /** Author's from-end nudge, re-applied on every geometry rebuild (mirrors `endpoint` for `to`). */
3713
+ fromOffset?: {
3714
+ dx: number;
3715
+ dy: number;
3716
+ };
3019
3717
  /** Connector line color. */
3020
3718
  stroke: string;
3021
3719
  /** Connector style: straight line, curved arrow, or vertical drop-line through the data point. */
3022
3720
  style: 'straight' | 'curve' | 'drop-line';
3721
+ /** Whether to draw an arrowhead at the data-point end. */
3722
+ arrow: boolean;
3723
+ /**
3724
+ * Which edge of the label block the connector leaves from. Renderers use it
3725
+ * to shape the curve's control point. Internal; not part of the spec.
3726
+ */
3727
+ exit?: 'horizontal' | 'vertical';
3023
3728
  };
3024
3729
  /** Background color behind the label text. */
3025
3730
  background?: string;
@@ -3036,6 +3741,16 @@ interface ResolvedAnnotation {
3036
3741
  type: 'text' | 'range' | 'refline';
3037
3742
  /** Stable identifier from the spec annotation, for selection/edit callbacks. */
3038
3743
  id?: string;
3744
+ /**
3745
+ * Index of the originating annotation in `spec.annotations`.
3746
+ *
3747
+ * The resolved array is a *filtered* view of the spec array -- an annotation
3748
+ * whose position falls outside the scale domain resolves to nothing and is
3749
+ * dropped -- so a resolved array index is NOT a spec index. Anything that has
3750
+ * to reach back to the authored annotation (thinning's priority/responsive
3751
+ * lookup, faceted footnote numbering) must key on this.
3752
+ */
3753
+ specIndex?: number;
3039
3754
  /** Label text (if any). */
3040
3755
  label?: ResolvedLabel;
3041
3756
  /** For range: the highlighted rectangle in pixel coordinates. */
@@ -3058,8 +3773,9 @@ interface ResolvedAnnotation {
3058
3773
  /** Z-index for render ordering. Higher values render on top. */
3059
3774
  zIndex?: number;
3060
3775
  /**
3061
- * For text annotations: optional dot marker drawn at the connector's
3062
- * data-point endpoint. Coordinates match `label.connector.to` exactly.
3776
+ * For text annotations: the endpoint marker drawn at the data point.
3777
+ * Coordinates sit on the data point itself (plus any `connectorOffset.to`),
3778
+ * not on the pulled-back `label.connector.to`.
3063
3779
  */
3064
3780
  dot?: {
3065
3781
  x: number;
@@ -3108,10 +3824,24 @@ interface LegendEntry {
3108
3824
  /** True for overflow indicator entries ("+N more"). Not interactive. */
3109
3825
  overflow?: boolean;
3110
3826
  }
3827
+ /** The encoding channel a legend keys. */
3828
+ type LegendChannel = 'color' | 'size';
3111
3829
  /** Base legend layout fields shared by all legend types. */
3112
3830
  interface BaseLegendLayout {
3113
- /** Where the legend is positioned relative to the chart area. */
3114
- position: 'top' | 'right' | 'bottom' | 'bottom-right' | 'inline';
3831
+ /**
3832
+ * Which encoding channel this legend keys. Omitted means `'color'` (every
3833
+ * legend was a color legend before size legends existed).
3834
+ *
3835
+ * This is the identity that makes plural legends safe: click-to-toggle-series
3836
+ * must fire for color entries and never for size circles, and selection/drag
3837
+ * must be able to tell two legends apart. Without it, `.oc-legend` and
3838
+ * `data-legend-index` are ambiguous across legends and the DOM queries that
3839
+ * read them silently pick whichever comes first.
3840
+ */
3841
+ channel?: LegendChannel;
3842
+ /** Where the legend is positioned relative to the chart area. `'top-left'`
3843
+ * is a map-only overlay position (floats inside the map area). */
3844
+ position: 'top' | 'top-left' | 'right' | 'bottom' | 'bottom-right' | 'inline';
3115
3845
  /** Bounding box for the legend (pixel coordinates). */
3116
3846
  bounds: Rect;
3117
3847
  /** Entry label style. */
@@ -3165,8 +3895,94 @@ interface GradientLegendLayout extends BaseLegendLayout {
3165
3895
  /** Formatted maximum value label. */
3166
3896
  maxLabel: string;
3167
3897
  }
3168
- /** Resolved legend layout — either categorical (swatches) or gradient (continuous bar). */
3169
- type LegendLayout = CategoricalLegendLayout | GradientLegendLayout;
3898
+ /** A positioned value label on a continuous/binned color legend. */
3899
+ interface ContinuousLegendTick {
3900
+ /** The data value this label marks. */
3901
+ value: number;
3902
+ /** Formatted label text (channel format applied). */
3903
+ label: string;
3904
+ /** Label x position (pixel coordinates). */
3905
+ x: number;
3906
+ /** Horizontal text anchor for the label. */
3907
+ anchor: 'start' | 'middle' | 'end';
3908
+ }
3909
+ /** A discrete class swatch in a binned continuous legend. */
3910
+ interface ContinuousLegendBin {
3911
+ /** Swatch x position (pixel coordinates). */
3912
+ x: number;
3913
+ /** Swatch width in pixels. */
3914
+ width: number;
3915
+ /** Fill color. */
3916
+ color: string;
3917
+ }
3918
+ /**
3919
+ * Continuous color legend for quantitative color scales on charts.
3920
+ *
3921
+ * Two rendering modes, resolved from the color channel's scale type:
3922
+ * - `gradient`: a single gradient-filled bar with min/max labels (plus a
3923
+ * midpoint label at the neutral value for diverging ramps).
3924
+ * - `binned`: a contiguous swatch row (quantile/quantize/threshold scales)
3925
+ * with boundary value labels between swatches at the class breaks.
3926
+ */
3927
+ interface ContinuousLegendLayout extends BaseLegendLayout {
3928
+ /** Discriminant for legend type. */
3929
+ type: 'continuous';
3930
+ /** Rendering mode: gradient bar or binned swatch row. */
3931
+ mode: 'gradient' | 'binned';
3932
+ /** The color bar rectangle (pixel coordinates). */
3933
+ bar: Rect;
3934
+ /** Gradient color stops (empty in binned mode). */
3935
+ colorStops: GradientColorStop[];
3936
+ /** Class swatches, left to right (empty in gradient mode). */
3937
+ bins: ContinuousLegendBin[];
3938
+ /** Value labels rendered below the bar. */
3939
+ ticks: ContinuousLegendTick[];
3940
+ /** Text baseline y for the value labels (pixel coordinates). */
3941
+ labelY: number;
3942
+ }
3943
+ /**
3944
+ * One graduated circle in a size legend: a representative value from the size
3945
+ * scale's domain, drawn at the radius that value actually resolves to.
3946
+ */
3947
+ interface SizeLegendCircle {
3948
+ /** The data value this circle stands for. */
3949
+ value: number;
3950
+ /** Formatted label text. */
3951
+ label: string;
3952
+ /** Radius in pixels -- the *same* scale the marks use, never a re-derivation. */
3953
+ radius: number;
3954
+ /** Center x, relative to the legend bounds. */
3955
+ cx: number;
3956
+ /** Center y, relative to the legend bounds. Circles sit on a common baseline. */
3957
+ cy: number;
3958
+ /** Label baseline y, relative to the legend bounds. */
3959
+ labelY: number;
3960
+ }
3961
+ /**
3962
+ * Size legend: graduated circles keying a quantitative `size` encoding.
3963
+ *
3964
+ * The circles are drawn nested (concentric, sharing a bottom edge) rather than
3965
+ * side by side -- the newsroom convention, and it makes the *ratio* between
3966
+ * magnitudes legible instead of just their order.
3967
+ *
3968
+ * The keyed values come from the scale's DOMAIN, not the data extent. The size
3969
+ * scale clamps, so with an explicit `scale.domain` every datum past `domain[1]`
3970
+ * renders at max radius; keying the data extent would imply the biggest circle
3971
+ * equals the biggest datum when it may not.
3972
+ */
3973
+ interface SizeLegendLayout extends BaseLegendLayout {
3974
+ /** Discriminant for legend type. */
3975
+ type: 'size';
3976
+ /** Graduated circles, smallest first. */
3977
+ circles: SizeLegendCircle[];
3978
+ /** Stroke color for the circle outlines. */
3979
+ stroke: string;
3980
+ }
3981
+ /**
3982
+ * Resolved legend layout: categorical (swatches), gradient (tilemap bar),
3983
+ * continuous (chart color bar), or size (graduated circles).
3984
+ */
3985
+ type LegendLayout = CategoricalLegendLayout | GradientLegendLayout | ContinuousLegendLayout | SizeLegendLayout;
3170
3986
  /** A single resolved endpoint-label entry, fully positioned. */
3171
3987
  interface EndpointLabelEntry {
3172
3988
  /** Series identifier (matches mark.seriesKey). */
@@ -3241,7 +4057,7 @@ interface TooltipContent {
3241
4057
  }
3242
4058
  /** Accessibility metadata for the entire visualization. */
3243
4059
  interface A11yMetadata {
3244
- /** Generated alt text describing the visualization. */
4060
+ /** Alt text describing the visualization: the author's `a11y.description` / `description` when set, auto-generated otherwise. */
3245
4061
  altText: string;
3246
4062
  /** Tabular data fallback for screen readers. Each inner array is a row. */
3247
4063
  dataTableFallback: unknown[][];
@@ -3249,6 +4065,13 @@ interface A11yMetadata {
3249
4065
  role: string;
3250
4066
  /** Whether the visualization is keyboard-navigable. */
3251
4067
  keyboardNavigable: boolean;
4068
+ /**
4069
+ * Hide the visualization from assistive technology (`aria-hidden="true"`,
4070
+ * no screen-reader table, no keyboard mark navigation). Optional because
4071
+ * only chart compilation sets it, and only on author opt-out via
4072
+ * `a11y.hidden`.
4073
+ */
4074
+ hidden?: boolean;
3252
4075
  }
3253
4076
  /** One fully-resolved animation phase. */
3254
4077
  interface ResolvedAnimationPhase {
@@ -3302,6 +4125,95 @@ interface ResolvedMetricBar {
3302
4125
  /** Cells laid out evenly across the chart width. */
3303
4126
  cells: ResolvedMetricCell[];
3304
4127
  }
4128
+ /**
4129
+ * Reserved band for the series search input (`seriesSearch`). The band is
4130
+ * empty SVG space between the chrome/metrics block and the top legend; the
4131
+ * vanilla adapter overlays an absolutely-positioned DOM combobox aligned to
4132
+ * this rect, so mounting it never changes the container's size.
4133
+ */
4134
+ interface ResolvedSeriesSearch {
4135
+ /** Band left x in layout coordinates. */
4136
+ x: number;
4137
+ /** Band top y in layout coordinates. */
4138
+ y: number;
4139
+ /** Band width. */
4140
+ width: number;
4141
+ /** Input row height (the reservation adds a gap below this). */
4142
+ height: number;
4143
+ /** Resolved input placeholder text. */
4144
+ placeholder: string;
4145
+ /** Distinct color-channel values available for search, in data order. */
4146
+ values: string[];
4147
+ }
4148
+ /**
4149
+ * One x-sample the reader's drawing snaps to: a pixel x position paired with
4150
+ * its data-x value. `xValue` is the value reported by `onReveal`; it falls
4151
+ * back to the pixel x when the data value can't be recovered.
4152
+ */
4153
+ interface YouDrawItSample {
4154
+ /** Pixel x position within the chart area. */
4155
+ px: number;
4156
+ /** Data-coordinate x value at this sample (from the target line's data). */
4157
+ xValue: string | number;
4158
+ }
4159
+ /**
4160
+ * Linear y pixel-to-data anchors, so the vanilla layer can map a drawn pixel
4161
+ * y back to a data value for `onReveal` without holding the scale object.
4162
+ * Present only when the y scale is invertible (continuous scales).
4163
+ */
4164
+ interface YouDrawItYInvert {
4165
+ /** Top pixel of the drawing area. */
4166
+ topPixel: number;
4167
+ /** Bottom pixel of the drawing area. */
4168
+ bottomPixel: number;
4169
+ /** Data value at the top pixel. */
4170
+ topData: number;
4171
+ /** Data value at the bottom pixel. */
4172
+ bottomData: number;
4173
+ }
4174
+ /**
4175
+ * Scale inversion anchors serialized from the engine so the vanilla layer
4176
+ * can convert pixel positions to data-space values without holding d3
4177
+ * scale objects. Generalizes the YouDrawItYInvert pattern to both axes.
4178
+ */
4179
+ interface ScaleInvert {
4180
+ /** For continuous scales: two reference pixel-to-data anchor points. */
4181
+ topPixel: number;
4182
+ bottomPixel: number;
4183
+ topData: number;
4184
+ bottomData: number;
4185
+ /** Scale type so the vanilla layer applies the correct inverse transform. */
4186
+ scaleType?: 'linear' | 'log' | 'pow' | 'sqrt' | 'symlog' | 'time';
4187
+ /** For band/point scales: ordered domain values and their pixel centers. */
4188
+ domain?: (string | number)[];
4189
+ positions?: number[];
4190
+ }
4191
+ /**
4192
+ * Resolved geometry and config for the "you draw it" interactive format
4193
+ * (`youDrawIt`). The vanilla adapter uses this to render the hatched drawing
4194
+ * region, capture pointer input, and mask/reveal the real line. Present only
4195
+ * when the spec enables `youDrawIt` on a valid single-series line chart.
4196
+ */
4197
+ interface ResolvedYouDrawIt {
4198
+ /** Pixel x position of `from` within the chart area (drawing starts here). */
4199
+ fromX: number;
4200
+ /** The chart drawing area, for clamping pointer input and sizing the hatch region. */
4201
+ area: Rect;
4202
+ /** X samples (pixel + data value) at or after `from`, ascending by pixel x. Drawing snaps to these. */
4203
+ samples: YouDrawItSample[];
4204
+ /** Resolved prompt text for the hatched region. */
4205
+ prompt: string;
4206
+ /** Resolved skip-to-reveal button label. */
4207
+ revealLabel: string;
4208
+ /** Stroke color of the target line (the reader's guess uses a distinct pen style, but shares the palette for continuity). */
4209
+ lineColor: string;
4210
+ /** seriesKey of the target line mark, so the vanilla layer can find its DOM element. Undefined for a single-series line with no color encoding. */
4211
+ targetSeriesKey?: string;
4212
+ /** Pixel-to-data y anchors for reporting the guess in data coordinates. Absent when the y scale isn't invertible. */
4213
+ yInvert?: YouDrawItYInvert;
4214
+ /** Optional comparison line, resolved to pixel points parallel to the target line's points. */
4215
+ comparisonPoints?: Point[];
4216
+ }
3305
4217
  /**
3306
4218
  * A single panel in a faceted (small-multiples) layout.
3307
4219
  * Each panel has its own marks, axes, and drawing area but typically
@@ -3328,6 +4240,7 @@ interface FacetPanelLayout {
3328
4240
  y: number;
3329
4241
  fontSize: number;
3330
4242
  fontWeight: number;
4243
+ textAnchor?: 'start' | 'middle' | 'end';
3331
4244
  };
3332
4245
  }
3333
4246
  /** Layout for a faceted (small-multiples) chart. */
@@ -3340,6 +4253,8 @@ interface FacetLayout {
3340
4253
  columns: number;
3341
4254
  /** Whether scales are shared across panels. */
3342
4255
  sharedScales: boolean;
4256
+ /** How panels are arranged: 'row' (vertical stack), 'column' (horizontal), 'wrap' (grid). */
4257
+ direction: 'row' | 'column' | 'wrap';
3343
4258
  }
3344
4259
  /**
3345
4260
  * ChartLayout: the complete engine output for chart visualizations.
@@ -3356,6 +4271,10 @@ interface ChartLayout {
3356
4271
  chrome: ResolvedChrome;
3357
4272
  /** Resolved KPI metric bar. Present only when spec.metrics is supplied and fits. */
3358
4273
  metrics?: ResolvedMetricBar;
4274
+ /** Reserved series-search band. Present only when spec.seriesSearch is enabled. */
4275
+ seriesSearch?: ResolvedSeriesSearch;
4276
+ /** Resolved "you draw it" config. Present only when spec.youDrawIt is enabled and valid. */
4277
+ youDrawIt?: ResolvedYouDrawIt;
3359
4278
  /** Resolved axis layouts. */
3360
4279
  axes: {
3361
4280
  x?: AxisLayout;
@@ -3367,8 +4286,22 @@ interface ChartLayout {
3367
4286
  marks: Mark[];
3368
4287
  /** Resolved annotations with pixel positions. */
3369
4288
  annotations: ResolvedAnnotation[];
3370
- /** Legend layout (position, entries, bounds). */
4289
+ /**
4290
+ * Primary legend (the color legend). Kept as a singular slot: it is what every
4291
+ * existing consumer reads, and a chart's legend is a color legend unless it
4292
+ * says otherwise.
4293
+ */
3371
4294
  legend: LegendLayout;
4295
+ /**
4296
+ * Every legend on the chart, in render order -- the color legend plus, when a
4297
+ * quantitative `size` channel is encoded, a size legend.
4298
+ *
4299
+ * `legend` is `legends[0]` when a color legend exists. Consumers that reserve
4300
+ * space or hit-test **must** iterate this array: a bubble chart keys both
4301
+ * continent (color) and population (size), and reserving margin for only the
4302
+ * first leaves the second drawing on top of the plot.
4303
+ */
4304
+ legends: LegendLayout[];
3372
4305
  /**
3373
4306
  * Right-side endpoint labels column for multi-series line/area charts.
3374
4307
  * Empty `entries` means the column is suppressed (single-series, opt-out, or
@@ -3396,6 +4329,10 @@ interface ChartLayout {
3396
4329
  crosshair: boolean;
3397
4330
  /** Real text measurement function from the adapter (for accurate SVG text wrapping). */
3398
4331
  measureText?: MeasureTextFn;
4332
+ /** Scale inversion anchors for the x axis. Used by anchor drag to convert pixel to data-space. */
4333
+ xInvert?: ScaleInvert;
4334
+ /** Scale inversion anchors for the y axis. Used by anchor drag to convert pixel to data-space. */
4335
+ yInvert?: ScaleInvert;
3399
4336
  /** Faceted (small-multiples) layout. When present, renderers iterate panels instead of using top-level marks/axes. */
3400
4337
  facet?: FacetLayout;
3401
4338
  }
@@ -3806,6 +4743,143 @@ interface TileMapLayout {
3806
4743
  /** Text measurement function for the rendering adapter. */
3807
4744
  measureText: MeasureTextFn;
3808
4745
  }
4746
+ /** A structured compile warning with a code and context. */
4747
+ interface CompileWarning {
4748
+ code: string;
4749
+ message: string;
4750
+ context?: Record<string, unknown>;
4751
+ }
4752
+ /** Resolved focus target for map camera framing. */
4753
+ interface MapFocusLayout {
4754
+ target: {
4755
+ x: number;
4756
+ y: number;
4757
+ width: number;
4758
+ height: number;
4759
+ padding: number;
4760
+ };
4761
+ ids: Array<string | number>;
4762
+ }
4763
+ /** A resolved map feature mark with computed SVG path and fill. */
4764
+ interface MapFeatureMark {
4765
+ type: 'map-feature';
4766
+ /** SVG path `d` attribute for this feature's shape. */
4767
+ path: string;
4768
+ /** CSS fill color. */
4769
+ fill: string;
4770
+ /** CSS stroke color (border). */
4771
+ stroke: string;
4772
+ /** Stroke width in px. */
4773
+ strokeWidth: number;
4774
+ /** Feature ID (from TopoJSON). */
4775
+ id: string | number;
4776
+ /** Feature name (from properties, if available). */
4777
+ name?: string;
4778
+ /** Original joined data row (null if no data matched this feature). */
4779
+ data: Record<string, unknown> | null;
4780
+ /** Accessibility attributes. */
4781
+ aria: MarkAria;
4782
+ /** Index for stagger animation ordering. */
4783
+ animationIndex: number;
4784
+ /** Bounding box in map-local coordinates. */
4785
+ bounds: {
4786
+ x: number;
4787
+ y: number;
4788
+ width: number;
4789
+ height: number;
4790
+ };
4791
+ /** Centroid in map-local coordinates. */
4792
+ centroid: [number, number];
4793
+ }
4794
+ /** A resolved map point mark with projected coordinates and visual properties. */
4795
+ interface MapPointMark {
4796
+ type: 'map-point';
4797
+ /** Projected center x in map-local coordinates. */
4798
+ cx: number;
4799
+ /** Projected center y in map-local coordinates. */
4800
+ cy: number;
4801
+ /** Radius in pixels (base radius before camera counter-scaling). */
4802
+ r: number;
4803
+ /** CSS fill color. */
4804
+ fill: string;
4805
+ /** CSS stroke color. */
4806
+ stroke: string;
4807
+ /** Stroke width in px. */
4808
+ strokeWidth: number;
4809
+ /** Fill opacity (0-1). */
4810
+ fillOpacity: number;
4811
+ /** Stable key for events. */
4812
+ key: string;
4813
+ /** Original data row. */
4814
+ data: Record<string, unknown>;
4815
+ /** Accessibility attributes. */
4816
+ aria: MarkAria;
4817
+ /** Index for stagger animation ordering. */
4818
+ animationIndex: number;
4819
+ }
4820
+ /** Border paths for the map (interior borders and coastline/outline). */
4821
+ interface MapBorders {
4822
+ /** SVG path `d` for interior borders between features. */
4823
+ interiorPath: string;
4824
+ /** SVG path `d` for the outer boundary / coastline. */
4825
+ outlinePath: string;
4826
+ /** Stroke color for interior borders. */
4827
+ interiorStroke: string;
4828
+ /** Stroke color for outer boundary. */
4829
+ outlineStroke: string;
4830
+ }
4831
+ /**
4832
+ * MapLayout: the complete engine output for map visualizations.
4833
+ *
4834
+ * Contains everything an adapter needs to render the map: dimensions,
4835
+ * chrome, feature paths, borders, legend, tooltip descriptors, and
4836
+ * accessibility metadata.
4837
+ */
4838
+ interface MapLayout {
4839
+ /** The map drawing area (after chrome and legend are subtracted). */
4840
+ area: Rect;
4841
+ /** Resolved chrome text elements with positions and styles. */
4842
+ chrome: ResolvedChrome;
4843
+ /** Resolved feature marks with paths, fills, and joined data. */
4844
+ features: MapFeatureMark[];
4845
+ /** Border paths (interior mesh and outline). */
4846
+ borders: MapBorders;
4847
+ /** Resolved point marks for the symbol layer (empty array if no points layer). */
4848
+ pointMarks: MapPointMark[];
4849
+ /** Categorical legend for the point color channel (null if no point color). */
4850
+ pointCategoricalLegend: CategoricalLegendLayout | null;
4851
+ /** Continuous legend for the point color channel (null if no point color). */
4852
+ pointContinuousLegend: ContinuousLegendLayout | null;
4853
+ /** Continuous color legend for quantitative mode (null if hidden or categorical). */
4854
+ continuousLegend: ContinuousLegendLayout | null;
4855
+ /** Categorical swatch legend for nominal mode (null if hidden or quantitative). */
4856
+ categoricalLegend: CategoricalLegendLayout | null;
4857
+ /** Tooltip content descriptors keyed by feature ID. */
4858
+ tooltipDescriptors: Map<string, TooltipContent>;
4859
+ /** Accessibility metadata for the map. */
4860
+ a11y: A11yMetadata;
4861
+ /** Resolved theme. */
4862
+ theme: ResolvedTheme;
4863
+ /** Total SVG width in pixels. */
4864
+ width: number;
4865
+ /** Total SVG height in pixels. */
4866
+ height: number;
4867
+ /** Resolved animation config (undefined if animation is disabled). */
4868
+ animation: ResolvedAnimation | undefined;
4869
+ /** Whether to render the watermark. */
4870
+ watermark: boolean;
4871
+ /** Text measurement function for the rendering adapter. */
4872
+ measureText: MeasureTextFn;
4873
+ /** Structured compile warnings (unmatched keys, missing data, winding issues). */
4874
+ warnings: CompileWarning[];
4875
+ /** The (width, height) passed to the projection's fitSize -- camera fits against this, not the SVG viewBox. */
4876
+ mapSize: {
4877
+ width: number;
4878
+ height: number;
4879
+ };
4880
+ /** Resolved focus target for camera framing (null if no focus). */
4881
+ focus: MapFocusLayout | null;
4882
+ }
3809
4883
  /** A resolved bar list row with computed positions and visual properties. */
3810
4884
  interface BarListRowMark {
3811
4885
  type: 'barlist-row';
@@ -3907,6 +4981,24 @@ interface CompileOptions {
3907
4981
  darkMode?: boolean;
3908
4982
  /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
3909
4983
  watermark?: boolean;
4984
+ /**
4985
+ * Enable development-time diagnostics (WCAG contrast warnings via
4986
+ * `console.warn`). The engine is isomorphic, so it never sniffs
4987
+ * `process.env`; the host decides what "dev" means and passes this flag.
4988
+ * Warnings are advisory only and never throw. Defaults to false.
4989
+ */
4990
+ dev?: boolean;
4991
+ /**
4992
+ * Sink for advisory spec warnings (deprecations, did-you-mean suggestions,
4993
+ * unknown scheme names, overplotting/beeswarm budget notes, etc.). The engine
4994
+ * is isomorphic and side-effect-free by default: rather than writing to the
4995
+ * global `console`, it routes every advisory warning through this callback so
4996
+ * a host can collect, reroute, or silence them (e.g. suppress under SSR, fail
4997
+ * a test on any warning, surface them in a dev overlay). When omitted, the
4998
+ * engine defaults to `console.warn`. Never called with fatal errors, which are
4999
+ * thrown instead.
5000
+ */
5001
+ onWarn?: (message: string) => void;
3910
5002
  /**
3911
5003
  * Real text measurement function provided by the adapter.
3912
5004
  * Uses a hidden canvas or DOM element for accurate text dimensions.
@@ -3915,6 +5007,18 @@ interface CompileOptions {
3915
5007
  measureText?: MeasureTextFn;
3916
5008
  /** Extra pixels to reserve on the right margin for a secondary y-axis. Set by compileLayer when resolve.scale.y is 'independent'. */
3917
5009
  rightAxisReserve?: number;
5010
+ /**
5011
+ * Extra pixels to reserve below the plot for the auto-thinning footnote list.
5012
+ * Chicken-and-egg: thinning needs the chart area to decide what to demote, but
5013
+ * the footnotes it produces need space carved out of that same area. compileChart
5014
+ * resolves it by compiling once to learn the footnote count, then recompiling with
5015
+ * this set. Callers should not set it; it is internal to that second pass.
5016
+ */
5017
+ footnoteReserve?: number;
5018
+ /** Recursion depth of the footnote-reserve convergence pass. Internal to compileChart. */
5019
+ footnotePass?: number;
5020
+ /** Set after facet height growth to prevent unbounded recursion. Internal to compileFaceted. */
5021
+ facetHeightGrown?: boolean;
3918
5022
  /**
3919
5023
  * Use this chart drawing area instead of computing one from chrome, axes,
3920
5024
  * and legend reservations. Set by compileLayer when compiling leaf layers so
@@ -3945,8 +5049,8 @@ interface CompileTableOptions extends CompileOptions {
3945
5049
  * These are approximations suitable for checking palette accessibility,
3946
5050
  * not medical-grade simulations.
3947
5051
  */
3948
- /** The three common types of color vision deficiency. */
3949
- type ColorBlindnessType = 'protanopia' | 'deuteranopia' | 'tritanopia';
5052
+ /** The four common types of color vision deficiency. */
5053
+ type ColorBlindnessType = 'protanopia' | 'deuteranopia' | 'tritanopia' | 'achromatopsia';
3950
5054
  /**
3951
5055
  * Simulate how a color appears under a given color blindness type.
3952
5056
  * Returns a hex color string.
@@ -3961,6 +5065,13 @@ declare function simulateColorBlindness(color: string, type: ColorBlindnessType)
3961
5065
  */
3962
5066
  declare function checkPaletteDistinguishability(colors: string[], type: ColorBlindnessType, minDistance?: number): boolean;
3963
5067
 
5068
+ /**
5069
+ * Check whether a color string parses to a fully opaque color.
5070
+ * Contrast ratios are only meaningful against opaque backgrounds; the
5071
+ * default chart background ('transparent') defers to the host page and
5072
+ * can't be checked at compile time.
5073
+ */
5074
+ declare function isOpaqueColor(color: string): boolean;
3964
5075
  /**
3965
5076
  * Compute the WCAG contrast ratio between two colors.
3966
5077
  * Returns a value between 1 (identical) and 21 (black on white).
@@ -4062,6 +5173,15 @@ interface DivergingPalette {
4062
5173
  }
4063
5174
  /** All diverging palettes keyed by name. */
4064
5175
  declare const DIVERGING_PALETTES: Record<string, string[]>;
5176
+ /**
5177
+ * Resolve a scheme name to its palette stops. Accepts openchart palette names
5178
+ * (blue, green, orange, purple, teal, redBlue, brownTeal, categorical) and
5179
+ * common Vega-Lite aliases (blues, greens, ..., redblue, category10).
5180
+ * Case-insensitive. Returns undefined for unknown names.
5181
+ */
5182
+ declare function resolveSchemeName(name: string): string[] | undefined;
5183
+ /** Scheme names accepted by `scale: { scheme }`, for validation messages. */
5184
+ declare const SUPPORTED_SCHEME_NAMES: readonly string[];
4065
5185
 
4066
5186
  /**
4067
5187
  * Dark mode theme adaptation.
@@ -4175,6 +5295,23 @@ declare function resolveTheme(userTheme?: ThemeConfig, base?: Theme): ResolvedTh
4175
5295
  * chrome proportional to the container.
4176
5296
  */
4177
5297
 
5298
+ /**
5299
+ * Line-height multiplier for the auto-thinning footnote list.
5300
+ *
5301
+ * Exported because the chrome renderer steps each footnote line down by it
5302
+ * while the band height below is computed from it -- the two have to agree, or
5303
+ * the last line drifts outside the band that was reserved for it.
5304
+ */
5305
+ declare const FOOTNOTE_LINE_HEIGHT = 1.3;
5306
+ /**
5307
+ * Vertical space a footnote list of `count` lines occupies below the plot.
5308
+ *
5309
+ * Single source of truth, deliberately: the engine reserves this band in the
5310
+ * bottom margin, the chrome renderer shifts the source row down by it, and the
5311
+ * brand watermark shifts by it too. When those three disagree the footnotes
5312
+ * land on top of the watermark, which is exactly the bug this centralizes away.
5313
+ */
5314
+ declare function footnoteBandHeight(count: number, theme: ResolvedTheme): number;
4178
5315
  /**
4179
5316
  * Compute resolved chrome layout from a Chrome spec and resolved theme.
4180
5317
  *
@@ -4369,10 +5506,13 @@ declare const X_AXIS_BAND_HEIGHT = 26;
4369
5506
  declare const X_AXIS_TITLE_BAND = 22;
4370
5507
  declare const X_AXIS_TITLE_BAND_ROTATED = 20;
4371
5508
  /**
4372
- * Ceiling on the vertical extent reserved for rotated x tick labels. Labels
4373
- * whose rotated projection exceeds this clip at the reservation edge, so the
4374
- * angle ladder (engine layout/axes/rotation) avoids -90° for labels longer
4375
- * than this.
5509
+ * Ceiling on the vertical extent reserved for rotated x tick labels. The angle
5510
+ * ladder (engine layout/axes/rotation) avoids -90° for labels longer than this.
5511
+ *
5512
+ * The cap bounds the RESERVATION, so a label whose rotated projection exceeds it
5513
+ * must be truncated to fit — see `truncateRotatedLabel`. Reserving a capped band
5514
+ * while drawing the full string is what let long ticks spill past the axis and
5515
+ * collide with the source line.
4376
5516
  */
4377
5517
  declare const X_AXIS_ROTATED_EXTENT_CAP = 120;
4378
5518
  /**
@@ -4392,6 +5532,28 @@ interface XAxisExtentInput {
4392
5532
  measure?: (text: string, fontSize: number, fontWeight: number) => number;
4393
5533
  }
4394
5534
  declare function computeXAxisExtentFromLabels(input: XAxisExtentInput): number;
5535
+ /**
5536
+ * The widest a rotated tick label may be before its vertical projection exceeds
5537
+ * `X_AXIS_ROTATED_EXTENT_CAP`.
5538
+ *
5539
+ * Inverts the rotated-extent formula in `computeXAxisExtentFromLabels`:
5540
+ *
5541
+ * extent = width*sin(θ) + lineHeight*cos(θ) + gap
5542
+ * width = (cap - lineHeight*cos(θ) - gap) / sin(θ)
5543
+ *
5544
+ * Deriving it from the same terms (rather than hardcoding a second number) keeps
5545
+ * the truncation budget and the space reservation locked together — if one
5546
+ * changes, the other follows.
5547
+ */
5548
+ declare function maxRotatedLabelWidth(tickAngle: number, tickFontSize: number): number;
5549
+ /**
5550
+ * Truncate a rotated tick label with an ellipsis so it fits inside the capped
5551
+ * reservation. Returns the label unchanged when it already fits.
5552
+ *
5553
+ * Binary-search on character count against the real measure function, so it
5554
+ * respects proportional fonts rather than assuming a fixed character width.
5555
+ */
5556
+ declare function truncateRotatedLabel(label: string, tickAngle: number, tickFontSize: number, tickFontWeight: number, measure?: (text: string, fontSize: number, fontWeight?: number) => number): string;
4395
5557
 
4396
5558
  /**
4397
5559
  * Label collision detection and resolution.
@@ -4491,23 +5653,42 @@ declare function computeLabelBounds(label: ResolvedLabel): Rect;
4491
5653
  * @param locale - Locale string (currently unused, reserved for i18n).
4492
5654
  */
4493
5655
  declare function formatNumber(value: number, _locale?: string): string;
5656
+ declare function abbreviateNumber(value: number): string;
4494
5657
  /**
4495
- * Abbreviate a large number with a suffix.
4496
- *
4497
- * Examples:
4498
- * - 1500000 -> "1.5M"
4499
- * - 2300 -> "2.3K"
4500
- * - 42 -> "42"
4501
- * - 1000000000 -> "1B"
5658
+ * Format a number as an English ordinal: 1 -> "1st", 2 -> "2nd", 3 -> "3rd",
5659
+ * 11 -> "11th", 22 -> "22nd". Values are rounded to the nearest integer first;
5660
+ * intended for rank axes where the domain is integer positions.
4502
5661
  */
4503
- declare function abbreviateNumber(value: number): string;
5662
+ declare function formatOrdinal(value: number): string;
4504
5663
  /**
4505
5664
  * Build a number formatter from a d3-format string, with support for a
4506
5665
  * trailing literal suffix that d3-format itself would reject.
4507
5666
  *
5667
+ * Also accepts the OpenChart extension `'ordinal'`, which renders English
5668
+ * ordinals ("1st", "2nd", "3rd") for rank axes.
5669
+ *
4508
5670
  * Returns null if the format string is falsy or unparseable.
4509
5671
  */
4510
5672
  declare function buildD3Formatter(formatStr: string | undefined): ((v: number) => string) | null;
5673
+ type NumberFormatter = (value: number) => string;
5674
+ declare function formatPercent(value: number, options?: {
5675
+ fraction?: boolean;
5676
+ }): string;
5677
+ declare function formatCurrency(value: number, options?: {
5678
+ compact?: boolean;
5679
+ }): string;
5680
+ interface FieldFormatContext {
5681
+ extent?: [number, number];
5682
+ allIntegers?: boolean;
5683
+ surface?: 'chart' | 'table';
5684
+ step?: number;
5685
+ stepReference?: number;
5686
+ }
5687
+ declare function computeFieldFormatContext(values: Iterable<unknown>, surface?: 'chart' | 'table'): FieldFormatContext;
5688
+ declare function isYearContext(ctx: FieldFormatContext | undefined): boolean;
5689
+ declare function resolveNumberFormatter(formatStr: string | undefined, ctx?: FieldFormatContext): NumberFormatter | null;
5690
+ declare function defaultNumberFormatter(ctx?: FieldFormatContext): NumberFormatter;
5691
+ declare function buildCompactStepFormatter(step: number): NumberFormatter;
4511
5692
  /** Granularity levels for date formatting. */
4512
5693
  type DateGranularity = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute';
4513
5694
  /**
@@ -4673,12 +5854,12 @@ declare function columnChart(data: DataRow[], x: FieldRef, y: FieldRef, options?
4673
5854
  /**
4674
5855
  * Create a pie chart spec.
4675
5856
  *
4676
- * Convention: category maps to the color channel, value maps to y.
5857
+ * Convention: category maps to the color channel, value maps to theta.
4677
5858
  * Pie charts have no x-axis.
4678
5859
  *
4679
5860
  * @param data - Array of data rows.
4680
5861
  * @param category - Category field (color channel, nominal).
4681
- * @param value - Value field (y channel, quantitative).
5862
+ * @param value - Value field (theta channel, quantitative).
4682
5863
  * @param options - Optional chrome, annotations, theme, etc.
4683
5864
  * Note: color option is ignored since category is used for color.
4684
5865
  */
@@ -4695,12 +5876,12 @@ declare function areaChart(data: DataRow[], x: FieldRef, y: FieldRef, options?:
4695
5876
  /**
4696
5877
  * Create a donut chart spec.
4697
5878
  *
4698
- * Convention: category maps to the color channel, value maps to y.
5879
+ * Convention: category maps to the color channel, value maps to theta.
4699
5880
  * Donut charts have no x-axis.
4700
5881
  *
4701
5882
  * @param data - Array of data rows.
4702
5883
  * @param category - Category field (color channel, nominal).
4703
- * @param value - Value field (y channel, quantitative).
5884
+ * @param value - Value field (theta channel, quantitative).
4704
5885
  * @param options - Optional chrome, annotations, theme, etc.
4705
5886
  * Note: color option is ignored since category is used for color.
4706
5887
  */
@@ -4771,4 +5952,39 @@ interface TileMapBuilderOptions {
4771
5952
  */
4772
5953
  declare function tileMap(data: Record<string, number | string | null> | DataRow[], options?: TileMapBuilderOptions): TileMapSpec;
4773
5954
 
4774
- export { type A11yMetadata, ACHROMATIC_RAMP, AXIS_TITLE_GAP, AXIS_TITLE_OFFSET_COMPACT, AXIS_TITLE_OFFSET_DEFAULT, AXIS_TITLE_TRAILING_PAD, type AggregateOp, type AggregateTransform, type AnimationConfig, type AnimationEase, type AnimationPhaseConfig, type AnimationSpec, type AnimationStagger, type Annotation, type AnnotationAnchor, type AnnotationFootnote, type AnnotationOffset, type AnnotationPosition, type ArcEncoding, type ArcMark, type AreaEncoding, type AreaMark, type AxisConfig, type AxisLabelDensity, type AxisLayout, type AxisTick, BRAND_FONT_SIZE, BRAND_MIN_WIDTH, BRAND_RESERVE_WIDTH, BREAKPOINT_COMPACT_MAX, BREAKPOINT_MEDIUM_MAX, type BarColumnConfig, type BarEncoding, type BarListEncoding, type BarListLayout, type BarListRowMark, type BarListSpec, type BarListSpecWithoutData, type BarTableCell, type BaseLegendLayout, type BinParams, type BinTransform, type Breakpoint, CATEGORICAL_PALETTE, CHART_ENCODING_RULES, CHART_TYPES, COMPACT_WIDTH, type CalculateExpression, type CalculateTransform, type CategoricalLegendLayout, type CategoricalPalette, type CategoryColorsConfig, type CategoryTableCell, type CellStyle, type ChannelRule, type ChartBuilderOptions, type ChartEventHandlers, type ChartLayout, type ChartSpec, type ChartSpecOverride, type ChartSpecWithoutData, type ChartType, type Chrome, type ChromeDefaults, type ChromeKey, type ChromeMode, type ChromeText, type ChromeTextStyle, type ChromeThemeOverride, type CircleEncoding, type ColorBlindnessType, type ColumnConfig, type CompileOptions, type CompileTableOptions, type Condition, type ConditionalValueDef, DEFAULT_THEME, DIVERGING_PALETTES, type DarkMode, type DataRow, type DateGranularity, type Display, type DivergingPalette, EXTENDED_OFFSET_STRATEGIES, type ElementEdit, type ElementRef, type Encoding, type EncodingChannel, type EncodingRule, type EndpointLabelEntry, type EndpointLabelsConfig, type EndpointLabelsLayout, type FacetChannel, type FacetLayout, type FacetPanelLayout, type FieldPredicate, type FieldRef, type FieldType, type FilterPredicate, type FilterTransform, type FlagTableCell, type FoldTransform, GRAPH_ENCODING_RULES, type GradientColorStop, type GradientDef, type GradientLegendLayout, type GradientStop, type GraphChannelRule, type GraphEdge, type GraphEdgeLayout, type GraphEncoding, type GraphEncodingChannel, type GraphLayout, type GraphLayoutConfig, type GraphNode, type GraphNodeLayout, type GraphSpec, type GraphSpecWithoutData, type Gridline, HEIGHT_CRAMPED_MAX, HEIGHT_SHORT_MAX, HPAD_COMPACT_FRACTION, HPAD_COMPACT_MIN, type HeatmapColumnConfig, type HeatmapTableCell, type HeightClass, type ImageColumnConfig, type ImageTableCell, LABEL_GAP_COMPACT, LABEL_GAP_DEFAULT, LABEL_GAP_NARROW_MAX, type LabelCandidate, type LabelConfig, type LabelDensity, type LabelMode, type LabelPriority, type LabelSpec, type LayerSpec, type LayoutStrategy, type LegendConfig, type LegendEntry, type LegendEntryPosition, type LegendLayout, type LegendPosition, type LineEncoding, type LineMark, type LinearGradient, type LogicalAnd, type LogicalNot, type LogicalOr, type LollipopEncoding, MARK_DISPLAY_NAMES, MARK_ENCODING_RULES, MARK_TYPES, MAX_LEFT_LABEL_FRACTION_COMPACT, MAX_LEFT_LABEL_FRACTION_DEFAULT, MAX_LEFT_LABEL_FRACTION_MEDIUM, MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX, type Margins, type Mark, type MarkAria, type MarkDef, type MarkEvent, type MarkType, type MeasureTextFn, type Metric, NARROW_VIEWPORT_MAX, type NodeOverride, OFFSET_STRATEGIES, type OffsetStrategy, type PaginationState, type Point, type PointEncoding, type PointMark, type RadialGradient, type RangeAnnotation, type Rect, type RectEncoding, type RectMark, type RefLineAnnotation, type RelativeTimeRef, type ResolveConfig, type ResolveMode, type ResolvedAnimation, type ResolvedAnimationPhase, type ResolvedAnnotation, type ResolvedChrome, type ResolvedChromeElement, type ResolvedColumn, type ResolvedLabel, type ResolvedMetricBar, type ResolvedMetricCell, type ResolvedTheme, type ResponsiveConfig, type RuleMarkLayout, SEQUENTIAL_PALETTES, type SankeyEncoding, type SankeyLayout, type SankeyLinkColor, type SankeyLinkMark, type SankeyNodeAlign, type SankeyNodeMark, type SankeySpec, type SankeySpecWithoutData, type ScaleConfig, type ScaleType, type SequentialPalette, type SeriesStrategy, type SeriesStyle, type SortState, type SparklineColumnConfig, type SparklineData, type SparklineTableCell, type StoredVizSpec, TICK_LABEL_OFFSET, TICK_LINE_HEIGHT_FACTOR, TOP_PAD_EXTRA_NARROW, TOP_PAD_NARROW_MAX, type TableBuilderOptions, type TableCell, type TableCellBase, type TableLayout, type TableRow, type TableSpec, type TableSpecWithoutData, type TextAnnotation, type TextEncoding, type TextMarkLayout, type TextStyle, type TextTableCell, type Theme, type ThemeChromeDefaults, type ThemeColors, type ThemeConfig, type ThemeFontSizes, type ThemeFontWeights, type ThemeFonts, type ThemeSpacing, type TickEncoding, type TickMarkLayout, type TileMapBuilderOptions, type TileMapEncoding, type TileMapLayout, type TileMapPalette, type TileMapSpec, type TileMapSpecWithoutData, type TileMapTileMark, type TimeUnit, type TimeUnitTransform, type TokenValue, type TooltipContent, type TooltipField, type Transform, type VizSpec, type WindowFieldDef, type WindowOp, type WindowSortField, type WindowTransform, type XAxisExtentInput, X_AXIS_BAND_HEIGHT, X_AXIS_ROTATED_EXTENT_CAP, X_AXIS_TITLE_BAND, X_AXIS_TITLE_BAND_ROTATED, abbreviateNumber, adaptColorForDarkMode, adaptForLightLineStroke, adaptTheme, areaChart, axisTitleOffset, barChart, buildD3Formatter, buildTemporalFormatter, checkPaletteDistinguishability, columnChart, computeChrome, computeLabelBounds, computeXAxisExtentFromLabels, contrastRatio, dataTable, detectCollision, donutChart, dotChart, editorial, elementRef, essay, estimateTextWidth, findAccessibleColor, formatDate, formatNumber, generateAltText, generateAriaLabels, generateDataTable, getAxisTitleOffset, getBreakpoint, getHeightClass, getLayoutStrategy, getRepresentativeColor, heuristicMeasure, inferFieldType, isBarListSpec, isChartSpec, isConditionalDef, isEncodingChannel, isGradientDef, isGraphSpec, isLayerSpec, isRangeAnnotation, isRefLineAnnotation, isSankeySpec, isTableSpec, isTextAnnotation, isTileMapSpec, lineChart, meetsAA, overlapArea, pickLabelColor, pieChart, resolveCollisions, resolveMarkDef, resolveMarkType, resolveMeasurer, resolveTheme, scatterChart, simulateColorBlindness, textAscent, tileMap, wire, wrapText };
5955
+ /**
5956
+ * Deep merge for applying partial patches onto a spec-shaped object.
5957
+ *
5958
+ * Used by the scrollytelling story layer (`vanilla/story`) to apply
5959
+ * cumulative deep-partial spec patches onto a base spec. Arrays REPLACE
5960
+ * rather than merge element-by-element: predictable behavior for things
5961
+ * like `annotations` or `data`, where index-wise merging would silently
5962
+ * produce nonsense. Plain objects merge key-by-key, recursively.
5963
+ */
5964
+ declare function deepMergeSpec(target: any, source: any): any;
5965
+
5966
+ interface CssTokenDef {
5967
+ name: string;
5968
+ light: string;
5969
+ dark?: string;
5970
+ comment?: string;
5971
+ consumers?: string;
5972
+ section?: string;
5973
+ }
5974
+ declare const CSS_TOKEN_ROOT_SELECTORS: readonly string[];
5975
+ /**
5976
+ * Text levels. Names invert across modes: in light mode "muted" sits at
5977
+ * a lighter zinc step than "subtle" because both are picked relative to
5978
+ * the active background, not from a fixed lightness ladder. The intent
5979
+ * is "muted = first step away from primary text"; "subtle = next step
5980
+ * down"; etc.
5981
+ *
5982
+ * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
5983
+ * subtle=zinc-400, faint=zinc-300
5984
+ * Dark mode (dark.css): inverts the surface tokens but keeps the same
5985
+ * muted -> subtle -> faint progression away from primary.
5986
+ */
5987
+ declare const CSS_TOKENS: readonly CssTokenDef[];
5988
+ declare function cssTokenDefault(name: string, mode: 'light' | 'dark'): string;
5989
+
5990
+ export { type A11yConfig, type A11yMetadata, ACHROMATIC_RAMP, AXIS_TITLE_GAP, AXIS_TITLE_OFFSET_COMPACT, AXIS_TITLE_OFFSET_DEFAULT, AXIS_TITLE_TRAILING_PAD, type AggregateOp, type AggregateTransform, type AnimationConfig, type AnimationEase, type AnimationPhaseConfig, type AnimationSpec, type AnimationStagger, type Annotation, type AnnotationAnchor, type AnnotationFootnote, type AnnotationOffset, type AnnotationPosition, type ArcEncoding, type ArcMark, type AreaEncoding, type AreaMark, type AxisConfig, type AxisLabelDensity, type AxisLayout, type AxisTick, BRAND_FONT_SIZE, BRAND_MIN_WIDTH, BRAND_RESERVE_WIDTH, BREAKPOINT_COMPACT_MAX, BREAKPOINT_MEDIUM_MAX, type BarColumnConfig, type BarEncoding, type BarListEncoding, type BarListLayout, type BarListRowMark, type BarListSpec, type BarListSpecWithoutData, type BarTableCell, type BaseLegendLayout, type BeeswarmEncoding, type BinParams, type BinTransform, type Breakpoint, CATEGORICAL_PALETTE, COMPACT_WIDTH, CSS_TOKENS, CSS_TOKEN_ROOT_SELECTORS, type CalculateExpression, type CalculateTransform, type CalendarEncoding, type CategoricalLegendLayout, type CategoricalPalette, type CategoryColorsConfig, type CategoryTableCell, type CellStyle, type ChannelRule, type ChartBuilderOptions, type ChartEventHandlers, type ChartLayout, type ChartSpec, type ChartSpecOverride, type ChartSpecWithoutData, type Chrome, type ChromeDefaults, type ChromeKey, type ChromeMode, type ChromeText, type ChromeTextStyle, type ChromeThemeOverride, type CircleEncoding, type ColorBlindnessType, type ColumnConfig, type CompileOptions, type CompileTableOptions, type CompileWarning, type Condition, type ConditionalValueDef, type ConnectorConfig, type ConnectorType, type ContinuousLegendBin, type ContinuousLegendLayout, type ContinuousLegendTick, type CssTokenDef, DEFAULT_THEME, DIVERGING_PALETTES, type DarkMode, type DataRow, type DateGranularity, type Display, type DivergingPalette, EXTENDED_OFFSET_STRATEGIES, type ElementEdit, type ElementRef, type Encoding, type EncodingChannel, type EncodingRule, type EndpointLabelContent, type EndpointLabelEntry, type EndpointLabelsConfig, type EndpointLabelsLayout, FOOTNOTE_LINE_HEIGHT, type FacetChannel, type FacetLayout, type FacetPanelLayout, type FieldFormatContext, type FieldPredicate, type FieldRef, type FieldType, type FillPatternType, type FilterPredicate, type FilterTransform, type FlagTableCell, type FoldTransform, GRAPH_ENCODING_RULES, type GradientColorStop, type GradientDef, type GradientLegendLayout, type GradientStop, type GraphChannelRule, type GraphEdge, type GraphEdgeLayout, type GraphEncoding, type GraphEncodingChannel, type GraphLayout, type GraphLayoutConfig, type GraphNode, type GraphNodeLayout, type GraphSpec, type GraphSpecWithoutData, type Gridline, HEIGHT_CRAMPED_MAX, HEIGHT_SHORT_MAX, HPAD_COMPACT_FRACTION, HPAD_COMPACT_MIN, type HeatmapColumnConfig, type HeatmapTableCell, type HeightClass, type ImageColumnConfig, type ImageTableCell, LABEL_GAP_COMPACT, LABEL_GAP_DEFAULT, LABEL_GAP_NARROW_MAX, type LabelCandidate, type LabelConfig, type LabelDensity, type LabelMode, type LabelPriority, type LabelSpec, type LayerSpec, type LayoutStrategy, type LegendChannel, type LegendConfig, type LegendEntry, type LegendEntryPosition, type LegendLayout, type LegendPosition, type LineEncoding, type LineMark, type LinearGradient, type LogicalAnd, type LogicalNot, type LogicalOr, type LollipopEncoding, MARK_DISPLAY_NAMES, MARK_ENCODING_RULES, MARK_TYPES, MAX_LEFT_LABEL_FRACTION_COMPACT, MAX_LEFT_LABEL_FRACTION_DEFAULT, MAX_LEFT_LABEL_FRACTION_MEDIUM, MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX, type MapBorders, type MapEncoding, type MapFeatureMark, type MapFocus, type MapFocusLayout, type MapGeo, type MapLayout, type MapPointMark, type MapPointsLayer, type MapProjection, type MapSpec, type MapSpecWithoutData, type Margins, type Mark, type MarkAria, type MarkDef, type MarkEvent, type MarkType, type MeasureTextFn, type Metric, NARROW_VIEWPORT_MAX, type NodeOverride, type NumberFormatter, OFFSET_STRATEGIES, type OffsetStrategy, type PaginationState, type ParliamentEncoding, type Point, type PointEncoding, type PointMark, type RadialGradient, type RangeAnnotation, type RangeEncoding, type Rect, type RectEncoding, type RectMark, type RefLineAnnotation, type RelativeTimeRef, type ResolveConfig, type ResolveMode, type ResolvedAnimation, type ResolvedAnimationPhase, type ResolvedAnnotation, type ResolvedChrome, type ResolvedChromeElement, type ResolvedColumn, type ResolvedFillPattern, type ResolvedLabel, type ResolvedMetricBar, type ResolvedMetricCell, type ResolvedSeriesSearch, type ResolvedTheme, type ResolvedYouDrawIt, type ResponsiveConfig, type RuleMarkLayout, SEQUENTIAL_PALETTES, SUPPORTED_SCHEME_NAMES, type SankeyEncoding, type SankeyLayout, type SankeyLinkColor, type SankeyLinkMark, type SankeyNodeAlign, type SankeyNodeMark, type SankeySpec, type SankeySpecWithoutData, type ScaleConfig, type ScaleInvert, type ScaleType, type SequentialPalette, type SeriesSearchConfig, type SeriesStrategy, type SeriesStyle, type SizeLegendCircle, type SizeLegendLayout, type SortByField, type SortState, type SparklineColumnConfig, type SparklineData, type SparklineTableCell, type StoredVizSpec, TICK_LABEL_OFFSET, TICK_LINE_HEIGHT_FACTOR, TOP_PAD_EXTRA_NARROW, TOP_PAD_NARROW_MAX, type TableBuilderOptions, type TableCell, type TableCellBase, type TableLayout, type TableRow, type TableSpec, type TableSpecWithoutData, type TextAnnotation, type TextEncoding, type TextMarkLayout, type TextStyle, type TextTableCell, type Theme, type ThemeChromeDefaults, type ThemeColors, type ThemeConfig, type ThemeFontSizes, type ThemeFontWeights, type ThemeFonts, type ThemeSpacing, type TickEncoding, type TickMarkLayout, type TileMapBuilderOptions, type TileMapEncoding, type TileMapLayout, type TileMapPalette, type TileMapSpec, type TileMapSpecWithoutData, type TileMapTileMark, type TimeUnit, type TimeUnitTransform, type TokenValue, type TooltipContent, type TooltipField, type Transform, type ValueDef, type VizSpec, type WaffleEncoding, type WindowFieldDef, type WindowOp, type WindowSortField, type WindowTransform, type XAxisExtentInput, X_AXIS_BAND_HEIGHT, X_AXIS_ROTATED_EXTENT_CAP, X_AXIS_TITLE_BAND, X_AXIS_TITLE_BAND_ROTATED, type YouDrawItConfig, type YouDrawItSample, type YouDrawItYInvert, abbreviateNumber, adaptColorForDarkMode, adaptForLightLineStroke, adaptTheme, areaChart, axisTitleOffset, barChart, buildCompactStepFormatter, buildD3Formatter, buildTemporalFormatter, checkPaletteDistinguishability, columnChart, computeChrome, computeFieldFormatContext, computeLabelBounds, computeXAxisExtentFromLabels, contrastRatio, cssTokenDefault, dataTable, deepMergeSpec, defaultNumberFormatter, detectCollision, donutChart, dotChart, editorial, elementRef, essay, estimateTextWidth, findAccessibleColor, footnoteBandHeight, formatCurrency, formatDate, formatNumber, formatOrdinal, formatPercent, generateAltText, generateAriaLabels, generateDataTable, getAxisTitleOffset, getBreakpoint, getHeightClass, getLayoutStrategy, getRepresentativeColor, heuristicMeasure, inferFieldType, isAxislessMark, isBarListSpec, isChartSpec, isConditionalDef, isEncodingChannel, isGradientDef, isGraphSpec, isLayerSpec, isMapSpec, isOpaqueColor, isRangeAnnotation, isRefLineAnnotation, isSankeySpec, isTableSpec, isTextAnnotation, isTileMapSpec, isValueDef, isYearContext, lineChart, maxRotatedLabelWidth, meetsAA, overlapArea, pickLabelColor, pieChart, resolveCollisions, resolveMarkDef, resolveMarkType, resolveMeasurer, resolveNumberFormatter, resolveSchemeName, resolveTheme, scatterChart, simulateColorBlindness, textAscent, tileMap, truncateRotatedLabel, wire, wrapText };