@opendata-ai/openchart-core 8.0.0-rc.2 → 8.0.0-rc.21

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 (82) hide show
  1. package/dist/index.d.ts +490 -25
  2. package/dist/index.js +57 -19
  3. package/dist/index.js.map +1 -1
  4. package/dist/styles.css +1 -1
  5. package/dist/styles.css.map +1 -1
  6. package/package.json +1 -2
  7. package/schema/chart.schema.json +485 -35
  8. package/schema/table.schema.json +35 -1
  9. package/schema/vizspec.schema.json +1305 -120
  10. package/src/accessibility/__tests__/alt-text.test.ts +0 -110
  11. package/src/accessibility/__tests__/aria.test.ts +0 -125
  12. package/src/accessibility/alt-text.ts +0 -155
  13. package/src/accessibility/aria.ts +0 -75
  14. package/src/accessibility/index.ts +0 -6
  15. package/src/colors/__tests__/colorblind.test.ts +0 -75
  16. package/src/colors/__tests__/contrast.test.ts +0 -119
  17. package/src/colors/__tests__/palettes.test.ts +0 -74
  18. package/src/colors/colorblind.ts +0 -131
  19. package/src/colors/contrast.ts +0 -143
  20. package/src/colors/index.ts +0 -36
  21. package/src/colors/palettes.ts +0 -206
  22. package/src/helpers/__tests__/deep-merge.test.ts +0 -54
  23. package/src/helpers/__tests__/spec-builders.test.ts +0 -338
  24. package/src/helpers/deep-merge.ts +0 -29
  25. package/src/helpers/spec-builders.ts +0 -479
  26. package/src/index.ts +0 -215
  27. package/src/labels/__tests__/collision.test.ts +0 -310
  28. package/src/labels/collision.ts +0 -249
  29. package/src/labels/index.ts +0 -13
  30. package/src/layout/__tests__/chrome.test.ts +0 -208
  31. package/src/layout/__tests__/text-measure.test.ts +0 -57
  32. package/src/layout/chrome.ts +0 -470
  33. package/src/layout/index.ts +0 -18
  34. package/src/layout/text-measure.ts +0 -404
  35. package/src/layout/text-wrap.ts +0 -93
  36. package/src/locale/__tests__/format.test.ts +0 -606
  37. package/src/locale/format.ts +0 -459
  38. package/src/locale/index.ts +0 -21
  39. package/src/responsive/__tests__/breakpoints.test.ts +0 -146
  40. package/src/responsive/__tests__/metrics.test.ts +0 -52
  41. package/src/responsive/breakpoints.ts +0 -172
  42. package/src/responsive/index.ts +0 -53
  43. package/src/responsive/metrics.ts +0 -299
  44. package/src/schema/__tests__/generated-artifacts.test.ts +0 -42
  45. package/src/schema/__tests__/schema.test.ts +0 -179
  46. package/src/styles/__tests__/reduced-motion-selectors.test.ts +0 -28
  47. package/src/styles/animation.css +0 -199
  48. package/src/styles/base.css +0 -102
  49. package/src/styles/chrome.css +0 -174
  50. package/src/styles/dark.css +0 -44
  51. package/src/styles/graph.css +0 -135
  52. package/src/styles/index.css +0 -28
  53. package/src/styles/keyframes.css +0 -135
  54. package/src/styles/legend.css +0 -18
  55. package/src/styles/reduced-motion.css +0 -67
  56. package/src/styles/series-search.css +0 -153
  57. package/src/styles/sparkline.css +0 -16
  58. package/src/styles/table-animation.css +0 -93
  59. package/src/styles/table.css +0 -398
  60. package/src/styles/token-definitions.ts +0 -199
  61. package/src/styles/tokens.css +0 -156
  62. package/src/styles/tooltip.css +0 -126
  63. package/src/styles/you-draw-it.css +0 -126
  64. package/src/theme/__tests__/dark-mode.test.ts +0 -232
  65. package/src/theme/__tests__/defaults.test.ts +0 -73
  66. package/src/theme/__tests__/resolve.test.ts +0 -277
  67. package/src/theme/dark-mode.ts +0 -258
  68. package/src/theme/defaults.ts +0 -110
  69. package/src/theme/index.ts +0 -8
  70. package/src/theme/presets.ts +0 -64
  71. package/src/theme/resolve.ts +0 -312
  72. package/src/types/__tests__/encoding.test.ts +0 -368
  73. package/src/types/__tests__/gradient.test.ts +0 -95
  74. package/src/types/__tests__/spec.test.ts +0 -499
  75. package/src/types/encoding.ts +0 -280
  76. package/src/types/events.ts +0 -154
  77. package/src/types/index.ts +0 -287
  78. package/src/types/layout.ts +0 -1966
  79. package/src/types/schema-roots.ts +0 -32
  80. package/src/types/spec.ts +0 -2867
  81. package/src/types/table.ts +0 -108
  82. package/src/types/theme.ts +0 -209
@@ -181,8 +181,15 @@
181
181
  "description": "Clip marks to the chart area."
182
182
  },
183
183
  "trendline": {
184
- "type": "boolean",
185
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
184
+ "anyOf": [
185
+ {
186
+ "type": "boolean"
187
+ },
188
+ {
189
+ "$ref": "#/definitions/TrendlineConfig"
190
+ }
191
+ ],
192
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
186
193
  },
187
194
  "dx": {
188
195
  "type": "number",
@@ -222,6 +229,15 @@
222
229
  ],
223
230
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
224
231
  },
232
+ "render": {
233
+ "type": "string",
234
+ "enum": [
235
+ "auto",
236
+ "svg",
237
+ "canvas"
238
+ ],
239
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
240
+ },
225
241
  "style": {
226
242
  "type": "string",
227
243
  "enum": [
@@ -450,8 +466,15 @@
450
466
  "description": "Clip marks to the chart area."
451
467
  },
452
468
  "trendline": {
453
- "type": "boolean",
454
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
469
+ "anyOf": [
470
+ {
471
+ "type": "boolean"
472
+ },
473
+ {
474
+ "$ref": "#/definitions/TrendlineConfig"
475
+ }
476
+ ],
477
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
455
478
  },
456
479
  "dx": {
457
480
  "type": "number",
@@ -491,6 +514,15 @@
491
514
  ],
492
515
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
493
516
  },
517
+ "render": {
518
+ "type": "string",
519
+ "enum": [
520
+ "auto",
521
+ "svg",
522
+ "canvas"
523
+ ],
524
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
525
+ },
494
526
  "style": {
495
527
  "type": "string",
496
528
  "enum": [
@@ -719,8 +751,15 @@
719
751
  "description": "Clip marks to the chart area."
720
752
  },
721
753
  "trendline": {
722
- "type": "boolean",
723
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
754
+ "anyOf": [
755
+ {
756
+ "type": "boolean"
757
+ },
758
+ {
759
+ "$ref": "#/definitions/TrendlineConfig"
760
+ }
761
+ ],
762
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
724
763
  },
725
764
  "dx": {
726
765
  "type": "number",
@@ -760,6 +799,15 @@
760
799
  ],
761
800
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
762
801
  },
802
+ "render": {
803
+ "type": "string",
804
+ "enum": [
805
+ "auto",
806
+ "svg",
807
+ "canvas"
808
+ ],
809
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
810
+ },
763
811
  "style": {
764
812
  "type": "string",
765
813
  "enum": [
@@ -988,8 +1036,15 @@
988
1036
  "description": "Clip marks to the chart area."
989
1037
  },
990
1038
  "trendline": {
991
- "type": "boolean",
992
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
1039
+ "anyOf": [
1040
+ {
1041
+ "type": "boolean"
1042
+ },
1043
+ {
1044
+ "$ref": "#/definitions/TrendlineConfig"
1045
+ }
1046
+ ],
1047
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
993
1048
  },
994
1049
  "dx": {
995
1050
  "type": "number",
@@ -1029,6 +1084,15 @@
1029
1084
  ],
1030
1085
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
1031
1086
  },
1087
+ "render": {
1088
+ "type": "string",
1089
+ "enum": [
1090
+ "auto",
1091
+ "svg",
1092
+ "canvas"
1093
+ ],
1094
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
1095
+ },
1032
1096
  "style": {
1033
1097
  "type": "string",
1034
1098
  "enum": [
@@ -1257,8 +1321,15 @@
1257
1321
  "description": "Clip marks to the chart area."
1258
1322
  },
1259
1323
  "trendline": {
1260
- "type": "boolean",
1261
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
1324
+ "anyOf": [
1325
+ {
1326
+ "type": "boolean"
1327
+ },
1328
+ {
1329
+ "$ref": "#/definitions/TrendlineConfig"
1330
+ }
1331
+ ],
1332
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
1262
1333
  },
1263
1334
  "dx": {
1264
1335
  "type": "number",
@@ -1298,6 +1369,15 @@
1298
1369
  ],
1299
1370
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
1300
1371
  },
1372
+ "render": {
1373
+ "type": "string",
1374
+ "enum": [
1375
+ "auto",
1376
+ "svg",
1377
+ "canvas"
1378
+ ],
1379
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
1380
+ },
1301
1381
  "style": {
1302
1382
  "type": "string",
1303
1383
  "enum": [
@@ -1526,8 +1606,15 @@
1526
1606
  "description": "Clip marks to the chart area."
1527
1607
  },
1528
1608
  "trendline": {
1529
- "type": "boolean",
1530
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
1609
+ "anyOf": [
1610
+ {
1611
+ "type": "boolean"
1612
+ },
1613
+ {
1614
+ "$ref": "#/definitions/TrendlineConfig"
1615
+ }
1616
+ ],
1617
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
1531
1618
  },
1532
1619
  "dx": {
1533
1620
  "type": "number",
@@ -1567,6 +1654,15 @@
1567
1654
  ],
1568
1655
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
1569
1656
  },
1657
+ "render": {
1658
+ "type": "string",
1659
+ "enum": [
1660
+ "auto",
1661
+ "svg",
1662
+ "canvas"
1663
+ ],
1664
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
1665
+ },
1570
1666
  "style": {
1571
1667
  "type": "string",
1572
1668
  "enum": [
@@ -1795,8 +1891,15 @@
1795
1891
  "description": "Clip marks to the chart area."
1796
1892
  },
1797
1893
  "trendline": {
1798
- "type": "boolean",
1799
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
1894
+ "anyOf": [
1895
+ {
1896
+ "type": "boolean"
1897
+ },
1898
+ {
1899
+ "$ref": "#/definitions/TrendlineConfig"
1900
+ }
1901
+ ],
1902
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
1800
1903
  },
1801
1904
  "dx": {
1802
1905
  "type": "number",
@@ -1836,6 +1939,15 @@
1836
1939
  ],
1837
1940
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
1838
1941
  },
1942
+ "render": {
1943
+ "type": "string",
1944
+ "enum": [
1945
+ "auto",
1946
+ "svg",
1947
+ "canvas"
1948
+ ],
1949
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
1950
+ },
1839
1951
  "style": {
1840
1952
  "type": "string",
1841
1953
  "enum": [
@@ -2064,8 +2176,15 @@
2064
2176
  "description": "Clip marks to the chart area."
2065
2177
  },
2066
2178
  "trendline": {
2067
- "type": "boolean",
2068
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
2179
+ "anyOf": [
2180
+ {
2181
+ "type": "boolean"
2182
+ },
2183
+ {
2184
+ "$ref": "#/definitions/TrendlineConfig"
2185
+ }
2186
+ ],
2187
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
2069
2188
  },
2070
2189
  "dx": {
2071
2190
  "type": "number",
@@ -2105,6 +2224,15 @@
2105
2224
  ],
2106
2225
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
2107
2226
  },
2227
+ "render": {
2228
+ "type": "string",
2229
+ "enum": [
2230
+ "auto",
2231
+ "svg",
2232
+ "canvas"
2233
+ ],
2234
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
2235
+ },
2108
2236
  "style": {
2109
2237
  "type": "string",
2110
2238
  "enum": [
@@ -2333,8 +2461,15 @@
2333
2461
  "description": "Clip marks to the chart area."
2334
2462
  },
2335
2463
  "trendline": {
2336
- "type": "boolean",
2337
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
2464
+ "anyOf": [
2465
+ {
2466
+ "type": "boolean"
2467
+ },
2468
+ {
2469
+ "$ref": "#/definitions/TrendlineConfig"
2470
+ }
2471
+ ],
2472
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
2338
2473
  },
2339
2474
  "dx": {
2340
2475
  "type": "number",
@@ -2374,6 +2509,15 @@
2374
2509
  ],
2375
2510
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
2376
2511
  },
2512
+ "render": {
2513
+ "type": "string",
2514
+ "enum": [
2515
+ "auto",
2516
+ "svg",
2517
+ "canvas"
2518
+ ],
2519
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
2520
+ },
2377
2521
  "style": {
2378
2522
  "type": "string",
2379
2523
  "enum": [
@@ -2602,8 +2746,15 @@
2602
2746
  "description": "Clip marks to the chart area."
2603
2747
  },
2604
2748
  "trendline": {
2605
- "type": "boolean",
2606
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
2749
+ "anyOf": [
2750
+ {
2751
+ "type": "boolean"
2752
+ },
2753
+ {
2754
+ "$ref": "#/definitions/TrendlineConfig"
2755
+ }
2756
+ ],
2757
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
2607
2758
  },
2608
2759
  "dx": {
2609
2760
  "type": "number",
@@ -2643,6 +2794,15 @@
2643
2794
  ],
2644
2795
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
2645
2796
  },
2797
+ "render": {
2798
+ "type": "string",
2799
+ "enum": [
2800
+ "auto",
2801
+ "svg",
2802
+ "canvas"
2803
+ ],
2804
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
2805
+ },
2646
2806
  "style": {
2647
2807
  "type": "string",
2648
2808
  "enum": [
@@ -2871,8 +3031,15 @@
2871
3031
  "description": "Clip marks to the chart area."
2872
3032
  },
2873
3033
  "trendline": {
2874
- "type": "boolean",
2875
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
3034
+ "anyOf": [
3035
+ {
3036
+ "type": "boolean"
3037
+ },
3038
+ {
3039
+ "$ref": "#/definitions/TrendlineConfig"
3040
+ }
3041
+ ],
3042
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
2876
3043
  },
2877
3044
  "dx": {
2878
3045
  "type": "number",
@@ -2912,6 +3079,15 @@
2912
3079
  ],
2913
3080
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
2914
3081
  },
3082
+ "render": {
3083
+ "type": "string",
3084
+ "enum": [
3085
+ "auto",
3086
+ "svg",
3087
+ "canvas"
3088
+ ],
3089
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
3090
+ },
2915
3091
  "style": {
2916
3092
  "type": "string",
2917
3093
  "enum": [
@@ -3140,8 +3316,15 @@
3140
3316
  "description": "Clip marks to the chart area."
3141
3317
  },
3142
3318
  "trendline": {
3143
- "type": "boolean",
3144
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
3319
+ "anyOf": [
3320
+ {
3321
+ "type": "boolean"
3322
+ },
3323
+ {
3324
+ "$ref": "#/definitions/TrendlineConfig"
3325
+ }
3326
+ ],
3327
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
3145
3328
  },
3146
3329
  "dx": {
3147
3330
  "type": "number",
@@ -3181,6 +3364,15 @@
3181
3364
  ],
3182
3365
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
3183
3366
  },
3367
+ "render": {
3368
+ "type": "string",
3369
+ "enum": [
3370
+ "auto",
3371
+ "svg",
3372
+ "canvas"
3373
+ ],
3374
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
3375
+ },
3184
3376
  "style": {
3185
3377
  "type": "string",
3186
3378
  "enum": [
@@ -3409,8 +3601,15 @@
3409
3601
  "description": "Clip marks to the chart area."
3410
3602
  },
3411
3603
  "trendline": {
3412
- "type": "boolean",
3413
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
3604
+ "anyOf": [
3605
+ {
3606
+ "type": "boolean"
3607
+ },
3608
+ {
3609
+ "$ref": "#/definitions/TrendlineConfig"
3610
+ }
3611
+ ],
3612
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
3414
3613
  },
3415
3614
  "dx": {
3416
3615
  "type": "number",
@@ -3450,6 +3649,15 @@
3450
3649
  ],
3451
3650
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
3452
3651
  },
3652
+ "render": {
3653
+ "type": "string",
3654
+ "enum": [
3655
+ "auto",
3656
+ "svg",
3657
+ "canvas"
3658
+ ],
3659
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
3660
+ },
3453
3661
  "style": {
3454
3662
  "type": "string",
3455
3663
  "enum": [
@@ -3678,8 +3886,15 @@
3678
3886
  "description": "Clip marks to the chart area."
3679
3887
  },
3680
3888
  "trendline": {
3681
- "type": "boolean",
3682
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
3889
+ "anyOf": [
3890
+ {
3891
+ "type": "boolean"
3892
+ },
3893
+ {
3894
+ "$ref": "#/definitions/TrendlineConfig"
3895
+ }
3896
+ ],
3897
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
3683
3898
  },
3684
3899
  "dx": {
3685
3900
  "type": "number",
@@ -3719,6 +3934,15 @@
3719
3934
  ],
3720
3935
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
3721
3936
  },
3937
+ "render": {
3938
+ "type": "string",
3939
+ "enum": [
3940
+ "auto",
3941
+ "svg",
3942
+ "canvas"
3943
+ ],
3944
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
3945
+ },
3722
3946
  "style": {
3723
3947
  "type": "string",
3724
3948
  "enum": [
@@ -3947,8 +4171,15 @@
3947
4171
  "description": "Clip marks to the chart area."
3948
4172
  },
3949
4173
  "trendline": {
3950
- "type": "boolean",
3951
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
4174
+ "anyOf": [
4175
+ {
4176
+ "type": "boolean"
4177
+ },
4178
+ {
4179
+ "$ref": "#/definitions/TrendlineConfig"
4180
+ }
4181
+ ],
4182
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
3952
4183
  },
3953
4184
  "dx": {
3954
4185
  "type": "number",
@@ -3988,6 +4219,15 @@
3988
4219
  ],
3989
4220
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
3990
4221
  },
4222
+ "render": {
4223
+ "type": "string",
4224
+ "enum": [
4225
+ "auto",
4226
+ "svg",
4227
+ "canvas"
4228
+ ],
4229
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
4230
+ },
3991
4231
  "style": {
3992
4232
  "type": "string",
3993
4233
  "enum": [
@@ -4216,8 +4456,15 @@
4216
4456
  "description": "Clip marks to the chart area."
4217
4457
  },
4218
4458
  "trendline": {
4219
- "type": "boolean",
4220
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
4459
+ "anyOf": [
4460
+ {
4461
+ "type": "boolean"
4462
+ },
4463
+ {
4464
+ "$ref": "#/definitions/TrendlineConfig"
4465
+ }
4466
+ ],
4467
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
4221
4468
  },
4222
4469
  "dx": {
4223
4470
  "type": "number",
@@ -4257,6 +4504,15 @@
4257
4504
  ],
4258
4505
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
4259
4506
  },
4507
+ "render": {
4508
+ "type": "string",
4509
+ "enum": [
4510
+ "auto",
4511
+ "svg",
4512
+ "canvas"
4513
+ ],
4514
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
4515
+ },
4260
4516
  "style": {
4261
4517
  "type": "string",
4262
4518
  "enum": [
@@ -4484,6 +4740,29 @@
4484
4740
  "additionalProperties": false,
4485
4741
  "description": "Radial gradient definition. Coordinates are in [0,1] normalized space relative to the mark's bounding box."
4486
4742
  },
4743
+ "TrendlineConfig": {
4744
+ "type": "object",
4745
+ "properties": {
4746
+ "layer": {
4747
+ "type": "string",
4748
+ "enum": [
4749
+ "above",
4750
+ "below"
4751
+ ],
4752
+ "description": "Stacking order relative to the point marks. `'below'` (default) draws the line behind the dots; `'above'` draws it on top so it stays visible over a dense cloud."
4753
+ },
4754
+ "stroke": {
4755
+ "type": "string",
4756
+ "description": "Stroke color. Defaults to the theme's high-contrast text color so the line reads on both light and dark grounds. Set an explicit CSS color to override."
4757
+ },
4758
+ "strokeWidth": {
4759
+ "type": "number",
4760
+ "description": "Stroke width in pixels. Defaults to 1.5. Over a dense cloud with `layer: 'above'`, a heavier line (e.g. 2.5) stays legible where the thin default disappears into the dots."
4761
+ }
4762
+ },
4763
+ "additionalProperties": false,
4764
+ "description": "Configuration for the scatter regression trend line. Passed as the object form of `MarkDef.trendline`."
4765
+ },
4487
4766
  "ArcEncoding<DataRow>": {
4488
4767
  "type": "object",
4489
4768
  "properties": {
@@ -4583,6 +4862,14 @@
4583
4862
  "facet": {
4584
4863
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
4585
4864
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
4865
+ },
4866
+ "row": {
4867
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
4868
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
4869
+ },
4870
+ "column": {
4871
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
4872
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
4586
4873
  }
4587
4874
  },
4588
4875
  "required": [
@@ -5092,12 +5379,13 @@
5092
5379
  "type": "string",
5093
5380
  "enum": [
5094
5381
  "top",
5382
+ "top-left",
5095
5383
  "right",
5096
5384
  "bottom",
5097
5385
  "bottom-right",
5098
5386
  "inline"
5099
5387
  ],
5100
- "description": "Legend position at a given breakpoint."
5388
+ "description": "Legend position at a given breakpoint. `'top-left'` is a map-only overlay position: the legend floats inside the map area's top-left corner (with its own backdrop) instead of reserving a block of figure height. Charts treat it as the default position."
5101
5389
  },
5102
5390
  "AnnotationOffset": {
5103
5391
  "type": "object",
@@ -5889,6 +6177,10 @@
5889
6177
  "color": {
5890
6178
  "type": "string",
5891
6179
  "description": "Text color (CSS color string)."
6180
+ },
6181
+ "maxLines": {
6182
+ "type": "number",
6183
+ "description": "Maximum number of lines this chrome text may wrap to. Additional lines are dropped and the last kept line is truncated with an ellipsis. Bounds the chrome's contribution to layout height at any container width. Applies whether the font size comes from the theme default or an explicit override. Caps height only, not horizontal overflow of a single unbreakable word."
5892
6184
  }
5893
6185
  },
5894
6186
  "additionalProperties": false,
@@ -6941,7 +7233,7 @@
6941
7233
  "update": {
6942
7234
  "anyOf": [
6943
7235
  {
6944
- "$ref": "#/definitions/AnimationPhaseConfig"
7236
+ "$ref": "#/definitions/UpdatePhaseConfig"
6945
7237
  },
6946
7238
  {
6947
7239
  "type": "boolean"
@@ -7022,33 +7314,63 @@
7022
7314
  "additionalProperties": false,
7023
7315
  "description": "Stagger configuration for sequential element reveal."
7024
7316
  },
7025
- "Display": {
7026
- "type": "string",
7027
- "enum": [
7028
- "full",
7029
- "sparkline"
7030
- ],
7031
- "description": "Chart display mode.\n- `'full'` (default): standard chart with chrome, axes, legend, padding.\n- `'sparkline'`: minimal inline mini-chart. Strips chrome, axes, legend, watermark, animation, and reduces margins to a tiny safety pad. The mark fills the container edge-to-edge. Best with `mark: 'line' | 'area' | 'bar'`. Explicit user fields (chrome, legend, encoding.x.axis, etc.) still render when set, so users can opt in to specific elements."
7032
- },
7033
- "SeriesSearchConfig": {
7034
- "type": "object",
7035
- "properties": {
7036
- "placeholder": {
7037
- "type": "string",
7038
- "description": "Placeholder text for the search input. Defaults to \"Find a series\"."
7039
- }
7040
- },
7041
- "additionalProperties": false,
7042
- "description": "Configuration for the searchable series highlight input (`seriesSearch`). The boolean form (`seriesSearch: true`) uses the defaults."
7043
- },
7044
- "YouDrawItConfig": {
7317
+ "UpdatePhaseConfig": {
7045
7318
  "type": "object",
7046
7319
  "properties": {
7047
- "from": {
7048
- "type": [
7049
- "string",
7050
- "number"
7051
- ],
7320
+ "duration": {
7321
+ "type": "number",
7322
+ "description": "Duration in ms. Default: 500 for enter."
7323
+ },
7324
+ "ease": {
7325
+ "$ref": "#/definitions/AnimationEase",
7326
+ "description": "Easing preset. Default: 'smooth'."
7327
+ },
7328
+ "stagger": {
7329
+ "anyOf": [
7330
+ {
7331
+ "$ref": "#/definitions/AnimationStagger"
7332
+ },
7333
+ {
7334
+ "type": "boolean"
7335
+ }
7336
+ ],
7337
+ "description": "Stagger config. true = defaults, false = no stagger. Default: true for enter."
7338
+ },
7339
+ "maxMarks": {
7340
+ "type": "number",
7341
+ "description": "Maximum mark count that still runs a tweened data-update transition. Default: 500. Above the cap the chart swaps instantly instead, because per-frame SVG attribute writes on thousands of elements drop frames on low-end devices. Raise it deliberately when the audience is desktop-heavy and you have measured the frame budget."
7342
+ }
7343
+ },
7344
+ "additionalProperties": false,
7345
+ "description": "Data-update phase config. Adds `maxMarks` on top of the shared phase options; the cap is meaningless for enter/exit, which animate via CSS."
7346
+ },
7347
+ "Display": {
7348
+ "type": "string",
7349
+ "enum": [
7350
+ "full",
7351
+ "sparkline"
7352
+ ],
7353
+ "description": "Chart display mode.\n- `'full'` (default): standard chart with chrome, axes, legend, padding.\n- `'sparkline'`: minimal inline mini-chart. Strips chrome, axes, legend, watermark, animation, and reduces margins to a tiny safety pad. The mark fills the container edge-to-edge. Best with `mark: 'line' | 'area' | 'bar'`. Explicit user fields (chrome, legend, encoding.x.axis, etc.) still render when set, so users can opt in to specific elements."
7354
+ },
7355
+ "SeriesSearchConfig": {
7356
+ "type": "object",
7357
+ "properties": {
7358
+ "placeholder": {
7359
+ "type": "string",
7360
+ "description": "Placeholder text for the search input. Defaults to \"Find a series\"."
7361
+ }
7362
+ },
7363
+ "additionalProperties": false,
7364
+ "description": "Configuration for the searchable series highlight input (`seriesSearch`). The boolean form (`seriesSearch: true`) uses the defaults."
7365
+ },
7366
+ "YouDrawItConfig": {
7367
+ "type": "object",
7368
+ "properties": {
7369
+ "from": {
7370
+ "type": [
7371
+ "string",
7372
+ "number"
7373
+ ],
7052
7374
  "description": "The x value (matching `encoding.x`'s domain) where drawing starts. Data before this point renders normally; data at or after it is hidden behind a hatched \"draw here\" region until reveal."
7053
7375
  },
7054
7376
  "prompt": {
@@ -7248,6 +7570,14 @@
7248
7570
  "facet": {
7249
7571
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7250
7572
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
7573
+ },
7574
+ "row": {
7575
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7576
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
7577
+ },
7578
+ "column": {
7579
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7580
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7251
7581
  }
7252
7582
  },
7253
7583
  "required": [
@@ -7359,6 +7689,14 @@
7359
7689
  "facet": {
7360
7690
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7361
7691
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
7692
+ },
7693
+ "row": {
7694
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7695
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
7696
+ },
7697
+ "column": {
7698
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7699
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7362
7700
  }
7363
7701
  },
7364
7702
  "required": [
@@ -7470,6 +7808,14 @@
7470
7808
  "facet": {
7471
7809
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7472
7810
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
7811
+ },
7812
+ "row": {
7813
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7814
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
7815
+ },
7816
+ "column": {
7817
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7818
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7473
7819
  }
7474
7820
  },
7475
7821
  "required": [
@@ -7581,6 +7927,14 @@
7581
7927
  "facet": {
7582
7928
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7583
7929
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
7930
+ },
7931
+ "row": {
7932
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7933
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
7934
+ },
7935
+ "column": {
7936
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7937
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7584
7938
  }
7585
7939
  },
7586
7940
  "required": [
@@ -7692,6 +8046,14 @@
7692
8046
  "facet": {
7693
8047
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7694
8048
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8049
+ },
8050
+ "row": {
8051
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8052
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8053
+ },
8054
+ "column": {
8055
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8056
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7695
8057
  }
7696
8058
  },
7697
8059
  "required": [
@@ -7803,6 +8165,14 @@
7803
8165
  "facet": {
7804
8166
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7805
8167
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8168
+ },
8169
+ "row": {
8170
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8171
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8172
+ },
8173
+ "column": {
8174
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8175
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7806
8176
  }
7807
8177
  },
7808
8178
  "required": [
@@ -7914,6 +8284,14 @@
7914
8284
  "facet": {
7915
8285
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
7916
8286
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8287
+ },
8288
+ "row": {
8289
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8290
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8291
+ },
8292
+ "column": {
8293
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8294
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
7917
8295
  }
7918
8296
  },
7919
8297
  "additionalProperties": false,
@@ -8021,6 +8399,14 @@
8021
8399
  "facet": {
8022
8400
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8023
8401
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8402
+ },
8403
+ "row": {
8404
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8405
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8406
+ },
8407
+ "column": {
8408
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8409
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8024
8410
  }
8025
8411
  },
8026
8412
  "required": [
@@ -8129,6 +8515,14 @@
8129
8515
  "facet": {
8130
8516
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8131
8517
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8518
+ },
8519
+ "row": {
8520
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8521
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8522
+ },
8523
+ "column": {
8524
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8525
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8132
8526
  }
8133
8527
  },
8134
8528
  "required": [
@@ -8239,6 +8633,14 @@
8239
8633
  "facet": {
8240
8634
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8241
8635
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8636
+ },
8637
+ "row": {
8638
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8639
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8640
+ },
8641
+ "column": {
8642
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8643
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8242
8644
  }
8243
8645
  },
8244
8646
  "required": [
@@ -8349,6 +8751,14 @@
8349
8751
  "facet": {
8350
8752
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8351
8753
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8754
+ },
8755
+ "row": {
8756
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8757
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8758
+ },
8759
+ "column": {
8760
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8761
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8352
8762
  }
8353
8763
  },
8354
8764
  "required": [
@@ -8460,6 +8870,14 @@
8460
8870
  "facet": {
8461
8871
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8462
8872
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8873
+ },
8874
+ "row": {
8875
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8876
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8877
+ },
8878
+ "column": {
8879
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8880
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8463
8881
  }
8464
8882
  },
8465
8883
  "required": [
@@ -8571,6 +8989,14 @@
8571
8989
  "facet": {
8572
8990
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8573
8991
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
8992
+ },
8993
+ "row": {
8994
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8995
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
8996
+ },
8997
+ "column": {
8998
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8999
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8574
9000
  }
8575
9001
  },
8576
9002
  "additionalProperties": false,
@@ -8668,6 +9094,14 @@
8668
9094
  "facet": {
8669
9095
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8670
9096
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
9097
+ },
9098
+ "row": {
9099
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
9100
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
9101
+ },
9102
+ "column": {
9103
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
9104
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8671
9105
  }
8672
9106
  },
8673
9107
  "required": [
@@ -8776,6 +9210,14 @@
8776
9210
  "facet": {
8777
9211
  "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
8778
9212
  "description": "Facet channel: partitions data into a grid of small-multiple panels. Each unique value of the facet field produces one panel. Panels share scales by default; use `resolve: { scale: { y: 'independent' } }` for per-panel axes."
9213
+ },
9214
+ "row": {
9215
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
9216
+ "description": "Row facet channel: partitions data into vertically-stacked panels. Each unique value of the row field produces one panel, arranged in a single column. Shares the x-axis scale by default; y-axis is independent. Mutually exclusive with `facet` and `column`."
9217
+ },
9218
+ "column": {
9219
+ "$ref": "#/definitions/FacetChannel%3CDataRow%3E",
9220
+ "description": "Column facet channel: partitions data into side-by-side panels. Each unique value of the column field produces one panel, arranged in a single row. Shares the y-axis scale by default; x-axis is independent. Mutually exclusive with `facet` and `row`."
8779
9221
  }
8780
9222
  },
8781
9223
  "required": [
@@ -8867,7 +9309,7 @@
8867
9309
  "items": {
8868
9310
  "$ref": "#/definitions/Annotation"
8869
9311
  },
8870
- "description": "Annotations on the layered view."
9312
+ "description": "Annotations for the layered chart. When present, these are the annotations rendered and available for editing. When absent, falls back to the first leaf layer's annotations. Annotations on second and later leaf layers are not rendered.\n\nFor edit persistence: the `data-annotation-index` stamped on each rendered annotation maps to this array (or the first leaf's if this is unset). Place annotations here for reliable index-based edit matching."
8871
9313
  },
8872
9314
  "labels": {
8873
9315
  "$ref": "#/definitions/LabelSpec",
@@ -8902,7 +9344,7 @@
8902
9344
  "items": {
8903
9345
  "type": "string"
8904
9346
  },
8905
- "description": "Hidden series names."
9347
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
8906
9348
  },
8907
9349
  "endpointLabels": {
8908
9350
  "anyOf": [
@@ -9063,8 +9505,15 @@
9063
9505
  "description": "Clip marks to the chart area."
9064
9506
  },
9065
9507
  "trendline": {
9066
- "type": "boolean",
9067
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
9508
+ "anyOf": [
9509
+ {
9510
+ "type": "boolean"
9511
+ },
9512
+ {
9513
+ "$ref": "#/definitions/TrendlineConfig"
9514
+ }
9515
+ ],
9516
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
9068
9517
  },
9069
9518
  "dx": {
9070
9519
  "type": "number",
@@ -9104,6 +9553,15 @@
9104
9553
  ],
9105
9554
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
9106
9555
  },
9556
+ "render": {
9557
+ "type": "string",
9558
+ "enum": [
9559
+ "auto",
9560
+ "svg",
9561
+ "canvas"
9562
+ ],
9563
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
9564
+ },
9107
9565
  "style": {
9108
9566
  "type": "string",
9109
9567
  "enum": [
@@ -9202,6 +9660,14 @@
9202
9660
  "$ref": "#/definitions/Chrome",
9203
9661
  "description": "Editorial chrome (title, subtitle, source, etc.)."
9204
9662
  },
9663
+ "chromeLayout": {
9664
+ "type": "string",
9665
+ "enum": [
9666
+ "subtract",
9667
+ "grow"
9668
+ ],
9669
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
9670
+ },
9205
9671
  "title": {
9206
9672
  "anyOf": [
9207
9673
  {
@@ -9310,7 +9776,7 @@
9310
9776
  "items": {
9311
9777
  "type": "string"
9312
9778
  },
9313
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
9779
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
9314
9780
  },
9315
9781
  "seriesStyles": {
9316
9782
  "type": "object",
@@ -9512,8 +9978,15 @@
9512
9978
  "description": "Clip marks to the chart area."
9513
9979
  },
9514
9980
  "trendline": {
9515
- "type": "boolean",
9516
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
9981
+ "anyOf": [
9982
+ {
9983
+ "type": "boolean"
9984
+ },
9985
+ {
9986
+ "$ref": "#/definitions/TrendlineConfig"
9987
+ }
9988
+ ],
9989
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
9517
9990
  },
9518
9991
  "dx": {
9519
9992
  "type": "number",
@@ -9553,6 +10026,15 @@
9553
10026
  ],
9554
10027
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
9555
10028
  },
10029
+ "render": {
10030
+ "type": "string",
10031
+ "enum": [
10032
+ "auto",
10033
+ "svg",
10034
+ "canvas"
10035
+ ],
10036
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
10037
+ },
9556
10038
  "style": {
9557
10039
  "type": "string",
9558
10040
  "enum": [
@@ -9651,6 +10133,14 @@
9651
10133
  "$ref": "#/definitions/Chrome",
9652
10134
  "description": "Editorial chrome (title, subtitle, source, etc.)."
9653
10135
  },
10136
+ "chromeLayout": {
10137
+ "type": "string",
10138
+ "enum": [
10139
+ "subtract",
10140
+ "grow"
10141
+ ],
10142
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
10143
+ },
9654
10144
  "title": {
9655
10145
  "anyOf": [
9656
10146
  {
@@ -9759,7 +10249,7 @@
9759
10249
  "items": {
9760
10250
  "type": "string"
9761
10251
  },
9762
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
10252
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
9763
10253
  },
9764
10254
  "seriesStyles": {
9765
10255
  "type": "object",
@@ -9961,8 +10451,15 @@
9961
10451
  "description": "Clip marks to the chart area."
9962
10452
  },
9963
10453
  "trendline": {
9964
- "type": "boolean",
9965
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
10454
+ "anyOf": [
10455
+ {
10456
+ "type": "boolean"
10457
+ },
10458
+ {
10459
+ "$ref": "#/definitions/TrendlineConfig"
10460
+ }
10461
+ ],
10462
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
9966
10463
  },
9967
10464
  "dx": {
9968
10465
  "type": "number",
@@ -10002,6 +10499,15 @@
10002
10499
  ],
10003
10500
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
10004
10501
  },
10502
+ "render": {
10503
+ "type": "string",
10504
+ "enum": [
10505
+ "auto",
10506
+ "svg",
10507
+ "canvas"
10508
+ ],
10509
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
10510
+ },
10005
10511
  "style": {
10006
10512
  "type": "string",
10007
10513
  "enum": [
@@ -10100,6 +10606,14 @@
10100
10606
  "$ref": "#/definitions/Chrome",
10101
10607
  "description": "Editorial chrome (title, subtitle, source, etc.)."
10102
10608
  },
10609
+ "chromeLayout": {
10610
+ "type": "string",
10611
+ "enum": [
10612
+ "subtract",
10613
+ "grow"
10614
+ ],
10615
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
10616
+ },
10103
10617
  "title": {
10104
10618
  "anyOf": [
10105
10619
  {
@@ -10208,7 +10722,7 @@
10208
10722
  "items": {
10209
10723
  "type": "string"
10210
10724
  },
10211
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
10725
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
10212
10726
  },
10213
10727
  "seriesStyles": {
10214
10728
  "type": "object",
@@ -10410,8 +10924,15 @@
10410
10924
  "description": "Clip marks to the chart area."
10411
10925
  },
10412
10926
  "trendline": {
10413
- "type": "boolean",
10414
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
10927
+ "anyOf": [
10928
+ {
10929
+ "type": "boolean"
10930
+ },
10931
+ {
10932
+ "$ref": "#/definitions/TrendlineConfig"
10933
+ }
10934
+ ],
10935
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
10415
10936
  },
10416
10937
  "dx": {
10417
10938
  "type": "number",
@@ -10451,6 +10972,15 @@
10451
10972
  ],
10452
10973
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
10453
10974
  },
10975
+ "render": {
10976
+ "type": "string",
10977
+ "enum": [
10978
+ "auto",
10979
+ "svg",
10980
+ "canvas"
10981
+ ],
10982
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
10983
+ },
10454
10984
  "style": {
10455
10985
  "type": "string",
10456
10986
  "enum": [
@@ -10549,6 +11079,14 @@
10549
11079
  "$ref": "#/definitions/Chrome",
10550
11080
  "description": "Editorial chrome (title, subtitle, source, etc.)."
10551
11081
  },
11082
+ "chromeLayout": {
11083
+ "type": "string",
11084
+ "enum": [
11085
+ "subtract",
11086
+ "grow"
11087
+ ],
11088
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
11089
+ },
10552
11090
  "title": {
10553
11091
  "anyOf": [
10554
11092
  {
@@ -10657,7 +11195,7 @@
10657
11195
  "items": {
10658
11196
  "type": "string"
10659
11197
  },
10660
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
11198
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
10661
11199
  },
10662
11200
  "seriesStyles": {
10663
11201
  "type": "object",
@@ -10859,8 +11397,15 @@
10859
11397
  "description": "Clip marks to the chart area."
10860
11398
  },
10861
11399
  "trendline": {
10862
- "type": "boolean",
10863
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
11400
+ "anyOf": [
11401
+ {
11402
+ "type": "boolean"
11403
+ },
11404
+ {
11405
+ "$ref": "#/definitions/TrendlineConfig"
11406
+ }
11407
+ ],
11408
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
10864
11409
  },
10865
11410
  "dx": {
10866
11411
  "type": "number",
@@ -10900,6 +11445,15 @@
10900
11445
  ],
10901
11446
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
10902
11447
  },
11448
+ "render": {
11449
+ "type": "string",
11450
+ "enum": [
11451
+ "auto",
11452
+ "svg",
11453
+ "canvas"
11454
+ ],
11455
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
11456
+ },
10903
11457
  "style": {
10904
11458
  "type": "string",
10905
11459
  "enum": [
@@ -10998,6 +11552,14 @@
10998
11552
  "$ref": "#/definitions/Chrome",
10999
11553
  "description": "Editorial chrome (title, subtitle, source, etc.)."
11000
11554
  },
11555
+ "chromeLayout": {
11556
+ "type": "string",
11557
+ "enum": [
11558
+ "subtract",
11559
+ "grow"
11560
+ ],
11561
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
11562
+ },
11001
11563
  "title": {
11002
11564
  "anyOf": [
11003
11565
  {
@@ -11106,7 +11668,7 @@
11106
11668
  "items": {
11107
11669
  "type": "string"
11108
11670
  },
11109
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
11671
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
11110
11672
  },
11111
11673
  "seriesStyles": {
11112
11674
  "type": "object",
@@ -11308,8 +11870,15 @@
11308
11870
  "description": "Clip marks to the chart area."
11309
11871
  },
11310
11872
  "trendline": {
11311
- "type": "boolean",
11312
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
11873
+ "anyOf": [
11874
+ {
11875
+ "type": "boolean"
11876
+ },
11877
+ {
11878
+ "$ref": "#/definitions/TrendlineConfig"
11879
+ }
11880
+ ],
11881
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
11313
11882
  },
11314
11883
  "dx": {
11315
11884
  "type": "number",
@@ -11349,6 +11918,15 @@
11349
11918
  ],
11350
11919
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
11351
11920
  },
11921
+ "render": {
11922
+ "type": "string",
11923
+ "enum": [
11924
+ "auto",
11925
+ "svg",
11926
+ "canvas"
11927
+ ],
11928
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
11929
+ },
11352
11930
  "style": {
11353
11931
  "type": "string",
11354
11932
  "enum": [
@@ -11447,6 +12025,14 @@
11447
12025
  "$ref": "#/definitions/Chrome",
11448
12026
  "description": "Editorial chrome (title, subtitle, source, etc.)."
11449
12027
  },
12028
+ "chromeLayout": {
12029
+ "type": "string",
12030
+ "enum": [
12031
+ "subtract",
12032
+ "grow"
12033
+ ],
12034
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
12035
+ },
11450
12036
  "title": {
11451
12037
  "anyOf": [
11452
12038
  {
@@ -11555,7 +12141,7 @@
11555
12141
  "items": {
11556
12142
  "type": "string"
11557
12143
  },
11558
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
12144
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
11559
12145
  },
11560
12146
  "seriesStyles": {
11561
12147
  "type": "object",
@@ -11757,8 +12343,15 @@
11757
12343
  "description": "Clip marks to the chart area."
11758
12344
  },
11759
12345
  "trendline": {
11760
- "type": "boolean",
11761
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
12346
+ "anyOf": [
12347
+ {
12348
+ "type": "boolean"
12349
+ },
12350
+ {
12351
+ "$ref": "#/definitions/TrendlineConfig"
12352
+ }
12353
+ ],
12354
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
11762
12355
  },
11763
12356
  "dx": {
11764
12357
  "type": "number",
@@ -11798,6 +12391,15 @@
11798
12391
  ],
11799
12392
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
11800
12393
  },
12394
+ "render": {
12395
+ "type": "string",
12396
+ "enum": [
12397
+ "auto",
12398
+ "svg",
12399
+ "canvas"
12400
+ ],
12401
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
12402
+ },
11801
12403
  "style": {
11802
12404
  "type": "string",
11803
12405
  "enum": [
@@ -11896,6 +12498,14 @@
11896
12498
  "$ref": "#/definitions/Chrome",
11897
12499
  "description": "Editorial chrome (title, subtitle, source, etc.)."
11898
12500
  },
12501
+ "chromeLayout": {
12502
+ "type": "string",
12503
+ "enum": [
12504
+ "subtract",
12505
+ "grow"
12506
+ ],
12507
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
12508
+ },
11899
12509
  "title": {
11900
12510
  "anyOf": [
11901
12511
  {
@@ -12004,7 +12614,7 @@
12004
12614
  "items": {
12005
12615
  "type": "string"
12006
12616
  },
12007
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
12617
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
12008
12618
  },
12009
12619
  "seriesStyles": {
12010
12620
  "type": "object",
@@ -12206,8 +12816,15 @@
12206
12816
  "description": "Clip marks to the chart area."
12207
12817
  },
12208
12818
  "trendline": {
12209
- "type": "boolean",
12210
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
12819
+ "anyOf": [
12820
+ {
12821
+ "type": "boolean"
12822
+ },
12823
+ {
12824
+ "$ref": "#/definitions/TrendlineConfig"
12825
+ }
12826
+ ],
12827
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
12211
12828
  },
12212
12829
  "dx": {
12213
12830
  "type": "number",
@@ -12247,6 +12864,15 @@
12247
12864
  ],
12248
12865
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
12249
12866
  },
12867
+ "render": {
12868
+ "type": "string",
12869
+ "enum": [
12870
+ "auto",
12871
+ "svg",
12872
+ "canvas"
12873
+ ],
12874
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
12875
+ },
12250
12876
  "style": {
12251
12877
  "type": "string",
12252
12878
  "enum": [
@@ -12345,6 +12971,14 @@
12345
12971
  "$ref": "#/definitions/Chrome",
12346
12972
  "description": "Editorial chrome (title, subtitle, source, etc.)."
12347
12973
  },
12974
+ "chromeLayout": {
12975
+ "type": "string",
12976
+ "enum": [
12977
+ "subtract",
12978
+ "grow"
12979
+ ],
12980
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
12981
+ },
12348
12982
  "title": {
12349
12983
  "anyOf": [
12350
12984
  {
@@ -12453,7 +13087,7 @@
12453
13087
  "items": {
12454
13088
  "type": "string"
12455
13089
  },
12456
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
13090
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
12457
13091
  },
12458
13092
  "seriesStyles": {
12459
13093
  "type": "object",
@@ -12655,8 +13289,15 @@
12655
13289
  "description": "Clip marks to the chart area."
12656
13290
  },
12657
13291
  "trendline": {
12658
- "type": "boolean",
12659
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
13292
+ "anyOf": [
13293
+ {
13294
+ "type": "boolean"
13295
+ },
13296
+ {
13297
+ "$ref": "#/definitions/TrendlineConfig"
13298
+ }
13299
+ ],
13300
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
12660
13301
  },
12661
13302
  "dx": {
12662
13303
  "type": "number",
@@ -12696,6 +13337,15 @@
12696
13337
  ],
12697
13338
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
12698
13339
  },
13340
+ "render": {
13341
+ "type": "string",
13342
+ "enum": [
13343
+ "auto",
13344
+ "svg",
13345
+ "canvas"
13346
+ ],
13347
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
13348
+ },
12699
13349
  "style": {
12700
13350
  "type": "string",
12701
13351
  "enum": [
@@ -12794,6 +13444,14 @@
12794
13444
  "$ref": "#/definitions/Chrome",
12795
13445
  "description": "Editorial chrome (title, subtitle, source, etc.)."
12796
13446
  },
13447
+ "chromeLayout": {
13448
+ "type": "string",
13449
+ "enum": [
13450
+ "subtract",
13451
+ "grow"
13452
+ ],
13453
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
13454
+ },
12797
13455
  "title": {
12798
13456
  "anyOf": [
12799
13457
  {
@@ -12902,7 +13560,7 @@
12902
13560
  "items": {
12903
13561
  "type": "string"
12904
13562
  },
12905
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
13563
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
12906
13564
  },
12907
13565
  "seriesStyles": {
12908
13566
  "type": "object",
@@ -13104,8 +13762,15 @@
13104
13762
  "description": "Clip marks to the chart area."
13105
13763
  },
13106
13764
  "trendline": {
13107
- "type": "boolean",
13108
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
13765
+ "anyOf": [
13766
+ {
13767
+ "type": "boolean"
13768
+ },
13769
+ {
13770
+ "$ref": "#/definitions/TrendlineConfig"
13771
+ }
13772
+ ],
13773
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
13109
13774
  },
13110
13775
  "dx": {
13111
13776
  "type": "number",
@@ -13145,6 +13810,15 @@
13145
13810
  ],
13146
13811
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
13147
13812
  },
13813
+ "render": {
13814
+ "type": "string",
13815
+ "enum": [
13816
+ "auto",
13817
+ "svg",
13818
+ "canvas"
13819
+ ],
13820
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
13821
+ },
13148
13822
  "style": {
13149
13823
  "type": "string",
13150
13824
  "enum": [
@@ -13243,6 +13917,14 @@
13243
13917
  "$ref": "#/definitions/Chrome",
13244
13918
  "description": "Editorial chrome (title, subtitle, source, etc.)."
13245
13919
  },
13920
+ "chromeLayout": {
13921
+ "type": "string",
13922
+ "enum": [
13923
+ "subtract",
13924
+ "grow"
13925
+ ],
13926
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
13927
+ },
13246
13928
  "title": {
13247
13929
  "anyOf": [
13248
13930
  {
@@ -13351,7 +14033,7 @@
13351
14033
  "items": {
13352
14034
  "type": "string"
13353
14035
  },
13354
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
14036
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
13355
14037
  },
13356
14038
  "seriesStyles": {
13357
14039
  "type": "object",
@@ -13553,8 +14235,15 @@
13553
14235
  "description": "Clip marks to the chart area."
13554
14236
  },
13555
14237
  "trendline": {
13556
- "type": "boolean",
13557
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
14238
+ "anyOf": [
14239
+ {
14240
+ "type": "boolean"
14241
+ },
14242
+ {
14243
+ "$ref": "#/definitions/TrendlineConfig"
14244
+ }
14245
+ ],
14246
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
13558
14247
  },
13559
14248
  "dx": {
13560
14249
  "type": "number",
@@ -13594,6 +14283,15 @@
13594
14283
  ],
13595
14284
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
13596
14285
  },
14286
+ "render": {
14287
+ "type": "string",
14288
+ "enum": [
14289
+ "auto",
14290
+ "svg",
14291
+ "canvas"
14292
+ ],
14293
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
14294
+ },
13597
14295
  "style": {
13598
14296
  "type": "string",
13599
14297
  "enum": [
@@ -13692,6 +14390,14 @@
13692
14390
  "$ref": "#/definitions/Chrome",
13693
14391
  "description": "Editorial chrome (title, subtitle, source, etc.)."
13694
14392
  },
14393
+ "chromeLayout": {
14394
+ "type": "string",
14395
+ "enum": [
14396
+ "subtract",
14397
+ "grow"
14398
+ ],
14399
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
14400
+ },
13695
14401
  "title": {
13696
14402
  "anyOf": [
13697
14403
  {
@@ -13800,7 +14506,7 @@
13800
14506
  "items": {
13801
14507
  "type": "string"
13802
14508
  },
13803
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
14509
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
13804
14510
  },
13805
14511
  "seriesStyles": {
13806
14512
  "type": "object",
@@ -14002,8 +14708,15 @@
14002
14708
  "description": "Clip marks to the chart area."
14003
14709
  },
14004
14710
  "trendline": {
14005
- "type": "boolean",
14006
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
14711
+ "anyOf": [
14712
+ {
14713
+ "type": "boolean"
14714
+ },
14715
+ {
14716
+ "$ref": "#/definitions/TrendlineConfig"
14717
+ }
14718
+ ],
14719
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
14007
14720
  },
14008
14721
  "dx": {
14009
14722
  "type": "number",
@@ -14043,6 +14756,15 @@
14043
14756
  ],
14044
14757
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
14045
14758
  },
14759
+ "render": {
14760
+ "type": "string",
14761
+ "enum": [
14762
+ "auto",
14763
+ "svg",
14764
+ "canvas"
14765
+ ],
14766
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
14767
+ },
14046
14768
  "style": {
14047
14769
  "type": "string",
14048
14770
  "enum": [
@@ -14141,6 +14863,14 @@
14141
14863
  "$ref": "#/definitions/Chrome",
14142
14864
  "description": "Editorial chrome (title, subtitle, source, etc.)."
14143
14865
  },
14866
+ "chromeLayout": {
14867
+ "type": "string",
14868
+ "enum": [
14869
+ "subtract",
14870
+ "grow"
14871
+ ],
14872
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
14873
+ },
14144
14874
  "title": {
14145
14875
  "anyOf": [
14146
14876
  {
@@ -14249,7 +14979,7 @@
14249
14979
  "items": {
14250
14980
  "type": "string"
14251
14981
  },
14252
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
14982
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
14253
14983
  },
14254
14984
  "seriesStyles": {
14255
14985
  "type": "object",
@@ -14451,8 +15181,15 @@
14451
15181
  "description": "Clip marks to the chart area."
14452
15182
  },
14453
15183
  "trendline": {
14454
- "type": "boolean",
14455
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
15184
+ "anyOf": [
15185
+ {
15186
+ "type": "boolean"
15187
+ },
15188
+ {
15189
+ "$ref": "#/definitions/TrendlineConfig"
15190
+ }
15191
+ ],
15192
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
14456
15193
  },
14457
15194
  "dx": {
14458
15195
  "type": "number",
@@ -14492,6 +15229,15 @@
14492
15229
  ],
14493
15230
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
14494
15231
  },
15232
+ "render": {
15233
+ "type": "string",
15234
+ "enum": [
15235
+ "auto",
15236
+ "svg",
15237
+ "canvas"
15238
+ ],
15239
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
15240
+ },
14495
15241
  "style": {
14496
15242
  "type": "string",
14497
15243
  "enum": [
@@ -14590,6 +15336,14 @@
14590
15336
  "$ref": "#/definitions/Chrome",
14591
15337
  "description": "Editorial chrome (title, subtitle, source, etc.)."
14592
15338
  },
15339
+ "chromeLayout": {
15340
+ "type": "string",
15341
+ "enum": [
15342
+ "subtract",
15343
+ "grow"
15344
+ ],
15345
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
15346
+ },
14593
15347
  "title": {
14594
15348
  "anyOf": [
14595
15349
  {
@@ -14698,7 +15452,7 @@
14698
15452
  "items": {
14699
15453
  "type": "string"
14700
15454
  },
14701
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
15455
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
14702
15456
  },
14703
15457
  "seriesStyles": {
14704
15458
  "type": "object",
@@ -14900,8 +15654,15 @@
14900
15654
  "description": "Clip marks to the chart area."
14901
15655
  },
14902
15656
  "trendline": {
14903
- "type": "boolean",
14904
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
15657
+ "anyOf": [
15658
+ {
15659
+ "type": "boolean"
15660
+ },
15661
+ {
15662
+ "$ref": "#/definitions/TrendlineConfig"
15663
+ }
15664
+ ],
15665
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
14905
15666
  },
14906
15667
  "dx": {
14907
15668
  "type": "number",
@@ -14941,6 +15702,15 @@
14941
15702
  ],
14942
15703
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
14943
15704
  },
15705
+ "render": {
15706
+ "type": "string",
15707
+ "enum": [
15708
+ "auto",
15709
+ "svg",
15710
+ "canvas"
15711
+ ],
15712
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
15713
+ },
14944
15714
  "style": {
14945
15715
  "type": "string",
14946
15716
  "enum": [
@@ -15039,6 +15809,14 @@
15039
15809
  "$ref": "#/definitions/Chrome",
15040
15810
  "description": "Editorial chrome (title, subtitle, source, etc.)."
15041
15811
  },
15812
+ "chromeLayout": {
15813
+ "type": "string",
15814
+ "enum": [
15815
+ "subtract",
15816
+ "grow"
15817
+ ],
15818
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
15819
+ },
15042
15820
  "title": {
15043
15821
  "anyOf": [
15044
15822
  {
@@ -15147,7 +15925,7 @@
15147
15925
  "items": {
15148
15926
  "type": "string"
15149
15927
  },
15150
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
15928
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
15151
15929
  },
15152
15930
  "seriesStyles": {
15153
15931
  "type": "object",
@@ -15349,8 +16127,15 @@
15349
16127
  "description": "Clip marks to the chart area."
15350
16128
  },
15351
16129
  "trendline": {
15352
- "type": "boolean",
15353
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
16130
+ "anyOf": [
16131
+ {
16132
+ "type": "boolean"
16133
+ },
16134
+ {
16135
+ "$ref": "#/definitions/TrendlineConfig"
16136
+ }
16137
+ ],
16138
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
15354
16139
  },
15355
16140
  "dx": {
15356
16141
  "type": "number",
@@ -15390,6 +16175,15 @@
15390
16175
  ],
15391
16176
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
15392
16177
  },
16178
+ "render": {
16179
+ "type": "string",
16180
+ "enum": [
16181
+ "auto",
16182
+ "svg",
16183
+ "canvas"
16184
+ ],
16185
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
16186
+ },
15393
16187
  "style": {
15394
16188
  "type": "string",
15395
16189
  "enum": [
@@ -15488,6 +16282,14 @@
15488
16282
  "$ref": "#/definitions/Chrome",
15489
16283
  "description": "Editorial chrome (title, subtitle, source, etc.)."
15490
16284
  },
16285
+ "chromeLayout": {
16286
+ "type": "string",
16287
+ "enum": [
16288
+ "subtract",
16289
+ "grow"
16290
+ ],
16291
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
16292
+ },
15491
16293
  "title": {
15492
16294
  "anyOf": [
15493
16295
  {
@@ -15596,7 +16398,7 @@
15596
16398
  "items": {
15597
16399
  "type": "string"
15598
16400
  },
15599
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
16401
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
15600
16402
  },
15601
16403
  "seriesStyles": {
15602
16404
  "type": "object",
@@ -15798,8 +16600,15 @@
15798
16600
  "description": "Clip marks to the chart area."
15799
16601
  },
15800
16602
  "trendline": {
15801
- "type": "boolean",
15802
- "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals."
16603
+ "anyOf": [
16604
+ {
16605
+ "type": "boolean"
16606
+ },
16607
+ {
16608
+ "$ref": "#/definitions/TrendlineConfig"
16609
+ }
16610
+ ],
16611
+ "description": "Overlay a least-squares regression line on scatter (point) marks. Defaults to true. Set `mark: { type: 'point', trendline: false }` to suppress it when the chart already carries its own reference line (e.g. a manual x=y diagonal in a separate layer), which would otherwise produce two competing diagonals.\n\nPass an object to control stacking order: the line renders `below` the points by default, but a dense scatter (thousands of overlapping dots) fully occludes a line drawn underneath, so `{ layer: 'above' }` lifts the reference line over the cloud."
15803
16612
  },
15804
16613
  "dx": {
15805
16614
  "type": "number",
@@ -15839,6 +16648,15 @@
15839
16648
  ],
15840
16649
  "description": "Pattern-fill reinforcement for filled marks (bar, area, arc).\n\n- `'auto'`: assign a per-series SVG pattern (diagonal hatch, dots, crosshatch, vertical lines) layered over each series color so series remain distinguishable without color vision. Marks too small to show a legible pattern keep their solid fill.\n- `'none'` (default): solid fills only."
15841
16650
  },
16651
+ "render": {
16652
+ "type": "string",
16653
+ "enum": [
16654
+ "auto",
16655
+ "svg",
16656
+ "canvas"
16657
+ ],
16658
+ "description": "Rendering backend for point marks. High-cardinality scatter plots draw thousands of SVG circles, which is what makes their entrance and update transitions stutter; a canvas layer draws the same dots in a handful of batched paint calls.\n\n- `'auto'` (default): canvas above 1,000 compiled point marks, SVG below.\n- `'svg'`: always SVG, whatever the point count.\n- `'canvas'`: always canvas, whatever the point count.\n\nCanvas mode trades per-mark keyboard focus and edit-mode point selection (the screen-reader data table still carries every row), and draws the trendline above the dots even when `trendline.layer` is `'below'`. Tooltips, hover, and click callbacks work the same in both modes.\n\nIgnored with a console warning for non-point marks, and for faceted, layered, and sparkline charts."
16659
+ },
15842
16660
  "style": {
15843
16661
  "type": "string",
15844
16662
  "enum": [
@@ -15937,6 +16755,14 @@
15937
16755
  "$ref": "#/definitions/Chrome",
15938
16756
  "description": "Editorial chrome (title, subtitle, source, etc.)."
15939
16757
  },
16758
+ "chromeLayout": {
16759
+ "type": "string",
16760
+ "enum": [
16761
+ "subtract",
16762
+ "grow"
16763
+ ],
16764
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
16765
+ },
15940
16766
  "title": {
15941
16767
  "anyOf": [
15942
16768
  {
@@ -16045,7 +16871,7 @@
16045
16871
  "items": {
16046
16872
  "type": "string"
16047
16873
  },
16048
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
16874
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
16049
16875
  },
16050
16876
  "seriesStyles": {
16051
16877
  "type": "object",
@@ -16198,7 +17024,7 @@
16198
17024
  "items": {
16199
17025
  "$ref": "#/definitions/Annotation"
16200
17026
  },
16201
- "description": "Annotations on the layered view."
17027
+ "description": "Annotations for the layered chart. When present, these are the annotations rendered and available for editing. When absent, falls back to the first leaf layer's annotations. Annotations on second and later leaf layers are not rendered.\n\nFor edit persistence: the `data-annotation-index` stamped on each rendered annotation maps to this array (or the first leaf's if this is unset). Place annotations here for reliable index-based edit matching."
16202
17028
  },
16203
17029
  "labels": {
16204
17030
  "$ref": "#/definitions/LabelSpec",
@@ -16233,7 +17059,7 @@
16233
17059
  "items": {
16234
17060
  "type": "string"
16235
17061
  },
16236
- "description": "Hidden series names."
17062
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
16237
17063
  },
16238
17064
  "endpointLabels": {
16239
17065
  "anyOf": [
@@ -16570,6 +17396,34 @@
16570
17396
  "$ref": "#/definitions/DarkMode",
16571
17397
  "description": "Dark mode behavior."
16572
17398
  },
17399
+ "animation": {
17400
+ "$ref": "#/definitions/GraphAnimationSpec",
17401
+ "description": "Motion configuration. DEFAULT-ON: omit for full defaults, `false` to opt out of choreography (layout warmup still applies)."
17402
+ },
17403
+ "interaction": {
17404
+ "$ref": "#/definitions/GraphInteractionConfig",
17405
+ "description": "Interaction behavior (hover mode, select-fly, opt-in physics)."
17406
+ },
17407
+ "legend": {
17408
+ "anyOf": [
17409
+ {
17410
+ "type": "boolean"
17411
+ },
17412
+ {
17413
+ "type": "object",
17414
+ "properties": {
17415
+ "interactive": {
17416
+ "type": "boolean"
17417
+ },
17418
+ "counts": {
17419
+ "type": "boolean"
17420
+ }
17421
+ },
17422
+ "additionalProperties": false
17423
+ }
17424
+ ],
17425
+ "description": "Built-in legend. Interactive by default. Set `false` if you render your own legend, so the built-in filter UI doesn't compete with it."
17426
+ },
16573
17427
  "watermark": {
16574
17428
  "type": "boolean",
16575
17429
  "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
@@ -16628,7 +17482,11 @@
16628
17482
  },
16629
17483
  "nodeSize": {
16630
17484
  "$ref": "#/definitions/GraphEncodingChannel",
16631
- "description": "Size mapping for nodes."
17485
+ "description": "Size mapping for nodes. `scale.type` defaults to `'sqrt'` (area-perceptual, today's behavior); set `scale.type: 'linear'` for a linear radius ramp. `scale.range` sets the pixel radius extent (default `[3, 12]`)."
17486
+ },
17487
+ "nodeOpacity": {
17488
+ "$ref": "#/definitions/GraphEncodingChannel",
17489
+ "description": "Opacity mapping for nodes (VL opacity channel). Quantitative fields map linearly to `[0.25, 1]`; `scale.range` overrides.\n\nNOTE: there is deliberately no `edgeOpacity` channel — edge alpha is owned by the interaction alpha-tier machinery (hover dimming, search)."
16632
17490
  },
16633
17491
  "edgeColor": {
16634
17492
  "$ref": "#/definitions/GraphEncodingChannel",
@@ -16645,6 +17503,10 @@
16645
17503
  "nodeLabel": {
16646
17504
  "$ref": "#/definitions/GraphEncodingChannel",
16647
17505
  "description": "Label field for nodes."
17506
+ },
17507
+ "nodeLabelPriority": {
17508
+ "$ref": "#/definitions/GraphEncodingChannel",
17509
+ "description": "Label priority mapping for nodes. Maps a quantitative field to a 0-1 priority range; higher values are shown first as the user zooms in. Omit to use the default degree-based priority. `nodeOverrides[id].alwaysShowLabel` still wins as the hard override."
16648
17510
  }
16649
17511
  },
16650
17512
  "additionalProperties": false,
@@ -16664,6 +17526,42 @@
16664
17526
  "scale": {
16665
17527
  "$ref": "#/definitions/ScaleConfig",
16666
17528
  "description": "Scale configuration. Auto-derived from data if omitted."
17529
+ },
17530
+ "sort": {
17531
+ "anyOf": [
17532
+ {
17533
+ "type": "string",
17534
+ "const": "ascending"
17535
+ },
17536
+ {
17537
+ "type": "string",
17538
+ "const": "descending"
17539
+ },
17540
+ {
17541
+ "type": "null"
17542
+ },
17543
+ {
17544
+ "type": "array",
17545
+ "items": {
17546
+ "type": "string"
17547
+ }
17548
+ }
17549
+ ],
17550
+ "description": "Category ordering for the color domain and legend. Graph channels default `'ascending'` (VL-aligned, deterministic); `string[]` pins an explicit order; `null` keeps data order.\n\nNOTE: chart encoding channels keep data-order — this graph default is a deliberate divergence this release (see docs/migrating-v8.md)."
17551
+ },
17552
+ "highlight": {
17553
+ "anyOf": [
17554
+ {
17555
+ "type": "string"
17556
+ },
17557
+ {
17558
+ "type": "array",
17559
+ "items": {
17560
+ "type": "string"
17561
+ }
17562
+ }
17563
+ ],
17564
+ "description": "Category values to emphasize on load; the rest dim. Mirrors the chart `encoding.color.highlight` value list. Distinct from the `highlight()` runtime verb and `interaction.hover.mode`."
16667
17565
  }
16668
17566
  },
16669
17567
  "required": [
@@ -16717,6 +17615,35 @@
16717
17615
  "centerForce": {
16718
17616
  "type": "boolean",
16719
17617
  "description": "Whether to apply center force (default true)."
17618
+ },
17619
+ "seed": {
17620
+ "type": "number",
17621
+ "description": "Deterministic layout seed. Same spec + seed ⇒ identical settled layout within a given execution path (worker vs sync parity is not guaranteed)."
17622
+ },
17623
+ "energy": {
17624
+ "type": "string",
17625
+ "enum": [
17626
+ "gentle",
17627
+ "balanced",
17628
+ "energetic"
17629
+ ],
17630
+ "description": "Repulsion preset (plain-language, no d3 jargon): `gentle`/`balanced`/`energetic` → chargeStrength −150/−300/−600 and velocityDecay 0.5/0.4/0.3. A raw `chargeStrength` wins over the preset."
17631
+ },
17632
+ "settle": {
17633
+ "type": "string",
17634
+ "enum": [
17635
+ "quick",
17636
+ "balanced",
17637
+ "thorough"
17638
+ ],
17639
+ "description": "Settle-speed preset: `quick`/`balanced`/`thorough` → alphaDecay 0.05/0.0228/0.01. Higher = faster cool-down, less final refinement."
17640
+ },
17641
+ "warmup": {
17642
+ "type": [
17643
+ "boolean",
17644
+ "number"
17645
+ ],
17646
+ "description": "Headless settle ticks run before the first paint. `true` → 100 ticks under a 250ms budget; a number sets an explicit tick count; `false` disables.\n\nLives in `layout`, NOT `animation`, on purpose: warmup *reduces* motion, so it must survive `animation: false` (opt-out consumers get less settling chaos, not more)."
16720
17647
  }
16721
17648
  },
16722
17649
  "required": [
@@ -16752,6 +17679,157 @@
16752
17679
  "additionalProperties": false,
16753
17680
  "description": "Per-node visual overrides, keyed by node id."
16754
17681
  },
17682
+ "GraphAnimationSpec": {
17683
+ "anyOf": [
17684
+ {
17685
+ "type": "boolean"
17686
+ },
17687
+ {
17688
+ "$ref": "#/definitions/GraphAnimationConfig"
17689
+ }
17690
+ ],
17691
+ "description": "Graph motion spec.\n- omitted → full defaults (default-ON)\n- `false` → no choreography (warmup still applies)\n- object → per-phase control\n\n`prefers-reduced-motion` is always respected regardless of this value."
17692
+ },
17693
+ "GraphAnimationConfig": {
17694
+ "type": "object",
17695
+ "properties": {
17696
+ "enter": {
17697
+ "anyOf": [
17698
+ {
17699
+ "$ref": "#/definitions/AnimationPhaseConfig"
17700
+ },
17701
+ {
17702
+ "type": "boolean"
17703
+ }
17704
+ ],
17705
+ "description": "Node/edge reveal on first render. Default `{ duration: 500, ease: 'smooth', stagger: true }`."
17706
+ },
17707
+ "update": {
17708
+ "anyOf": [
17709
+ {
17710
+ "$ref": "#/definitions/AnimationPhaseConfig"
17711
+ },
17712
+ {
17713
+ "type": "boolean"
17714
+ }
17715
+ ],
17716
+ "description": "Data-update enter-fade for newly added marks. Default `{ duration: 300, ease: 'smooth' }`."
17717
+ },
17718
+ "exit": {
17719
+ "anyOf": [
17720
+ {
17721
+ "$ref": "#/definitions/AnimationPhaseConfig"
17722
+ },
17723
+ {
17724
+ "type": "boolean"
17725
+ }
17726
+ ],
17727
+ "description": "Ghost fade-out for removed marks. Default `{ duration: 300, ease: 'smooth' }`."
17728
+ },
17729
+ "camera": {
17730
+ "anyOf": [
17731
+ {
17732
+ "type": "object",
17733
+ "properties": {
17734
+ "duration": {
17735
+ "type": "number"
17736
+ },
17737
+ "ease": {
17738
+ "$ref": "#/definitions/AnimationEase"
17739
+ }
17740
+ },
17741
+ "additionalProperties": false
17742
+ },
17743
+ {
17744
+ "type": "boolean"
17745
+ }
17746
+ ],
17747
+ "description": "Camera flight easing. Default duration `'auto'` (derived from the zoom distance)."
17748
+ },
17749
+ "hover": {
17750
+ "anyOf": [
17751
+ {
17752
+ "type": "object",
17753
+ "properties": {
17754
+ "duration": {
17755
+ "type": "number"
17756
+ },
17757
+ "ease": {
17758
+ "$ref": "#/definitions/AnimationEase"
17759
+ }
17760
+ },
17761
+ "additionalProperties": false
17762
+ },
17763
+ {
17764
+ "type": "boolean"
17765
+ }
17766
+ ],
17767
+ "description": "Hover emphasis crossfade. Default `{ duration: 150, ease: 'smooth' }`."
17768
+ }
17769
+ },
17770
+ "additionalProperties": false,
17771
+ "description": "Per-phase and per-behavior motion config for a graph.\n\nDEFAULT-ON semantics (deliberately different from charts, which are opt-in): omit `animation` → the resolver returns full defaults; `animation: false` → no choreography (but `layout.warmup` still applies); a per-phase `false` disables just that phase. Graphs are default-on because physics motion already exists on load and choreography reduces the chaos."
17772
+ },
17773
+ "GraphInteractionConfig": {
17774
+ "type": "object",
17775
+ "properties": {
17776
+ "hover": {
17777
+ "type": "object",
17778
+ "properties": {
17779
+ "mode": {
17780
+ "type": "string",
17781
+ "enum": [
17782
+ "neighbors",
17783
+ "category",
17784
+ "node",
17785
+ "none"
17786
+ ]
17787
+ },
17788
+ "dimOpacity": {
17789
+ "type": "number"
17790
+ }
17791
+ },
17792
+ "additionalProperties": false,
17793
+ "description": "Hover emphasis behavior. `neighbors` (default) lights the hovered node and its adjacency; `category` lights same-category nodes; `node` lights only the hovered node; `none` disables. `dimOpacity` (default 0.15) is the node dim tier. Named `mode` (not `highlight`) to avoid colliding with the three other meanings of \"highlight\" (the encoding value list, the `highlight()` verb)."
17794
+ },
17795
+ "select": {
17796
+ "type": "object",
17797
+ "properties": {
17798
+ "flyTo": {
17799
+ "type": "boolean"
17800
+ }
17801
+ },
17802
+ "additionalProperties": false,
17803
+ "description": "Selecting a node flies the camera to it. Default `false`."
17804
+ },
17805
+ "cursorRepulsion": {
17806
+ "anyOf": [
17807
+ {
17808
+ "type": "boolean"
17809
+ },
17810
+ {
17811
+ "type": "object",
17812
+ "properties": {
17813
+ "radius": {
17814
+ "type": "number"
17815
+ },
17816
+ "strength": {
17817
+ "type": "number"
17818
+ }
17819
+ },
17820
+ "additionalProperties": false
17821
+ }
17822
+ ],
17823
+ "description": "Opt-in cursor repulsion (nodes drift away from the pointer). Disabled under `prefers-reduced-motion` and above the node-count gate."
17824
+ },
17825
+ "springyDrag": {
17826
+ "type": "boolean",
17827
+ "description": "Opt-in springy node drag (user-initiated; no reduced-motion gate)."
17828
+ }
17829
+ },
17830
+ "additionalProperties": false,
17831
+ "description": "Interaction behavior for a graph (hover mode, select-fly, opt-in physics)."
17832
+ },
16755
17833
  "SankeySpec": {
16756
17834
  "type": "object",
16757
17835
  "properties": {
@@ -17235,6 +18313,10 @@
17235
18313
  "type": "boolean",
17236
18314
  "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
17237
18315
  },
18316
+ "points": {
18317
+ "$ref": "#/definitions/MapPointsLayer",
18318
+ "description": "Optional point/symbol layer rendered above choropleth features."
18319
+ },
17238
18320
  "animation": {
17239
18321
  "$ref": "#/definitions/AnimationSpec",
17240
18322
  "description": "Animation configuration for entrance animations."
@@ -17276,7 +18358,7 @@
17276
18358
  "type": "null"
17277
18359
  }
17278
18360
  ],
17279
- "description": "Focus on specific feature(s). null clears focus from a prior story step."
18361
+ "description": "Focus the camera. A feature id (or array) fits those features; `{ features, padding }` adds breathing room; `{ points: true, padding }` fits the points layer's cluster instead of any feature (use when points occupy a small part of a large feature, so fitting the feature would leave the cluster small and off-center); `{ points: { field, value }, padding }` fits only the matching point subset, so a story can pan between sub-clusters. null clears focus from a prior story step."
17280
18362
  }
17281
18363
  },
17282
18364
  "required": [
@@ -17342,20 +18424,62 @@
17342
18424
  "features"
17343
18425
  ],
17344
18426
  "additionalProperties": false
18427
+ },
18428
+ {
18429
+ "type": "object",
18430
+ "properties": {
18431
+ "points": {
18432
+ "$ref": "#/definitions/MapPointsFocus"
18433
+ },
18434
+ "padding": {
18435
+ "type": "number"
18436
+ }
18437
+ },
18438
+ "required": [
18439
+ "points"
18440
+ ],
18441
+ "additionalProperties": false
18442
+ }
18443
+ ]
18444
+ },
18445
+ "MapPointsFocus": {
18446
+ "anyOf": [
18447
+ {
18448
+ "type": "boolean",
18449
+ "const": true
18450
+ },
18451
+ {
18452
+ "type": "object",
18453
+ "properties": {
18454
+ "field": {
18455
+ "type": "string"
18456
+ },
18457
+ "value": {
18458
+ "type": [
18459
+ "string",
18460
+ "number"
18461
+ ]
18462
+ }
18463
+ },
18464
+ "required": [
18465
+ "field",
18466
+ "value"
18467
+ ],
18468
+ "additionalProperties": false
17345
18469
  }
17346
18470
  ],
17347
- "description": "Focus target for map camera: feature id(s) to zoom/pan to."
18471
+ "description": "Fit the point layer's cluster. `true` fits every point; `{ field, value }` fits only the points whose data row has `row[field] === value` (e.g. only the campuses rated \"F\"), which lets a story pan between sub-clusters."
17348
18472
  },
17349
18473
  "MapEncoding": {
17350
18474
  "type": "object",
17351
18475
  "properties": {
17352
18476
  "key": {
17353
18477
  "$ref": "#/definitions/EncodingChannel",
17354
- "description": "Join key field in the data (required). Maps data rows to geo features via geo.idField."
18478
+ "description": "Join key field in the data. Required for choropleth; optional in basemap-only mode (points layer with empty data)."
17355
18479
  },
17356
18480
  "color": {
17357
18481
  "$ref": "#/definitions/EncodingChannel",
17358
- "description": "Color encoding (required, quantitative or nominal). Drives fill color of features."
18482
+ "description": "Color encoding (quantitative or nominal). Drives fill color of features. Optional when a points layer is present (basemap-only mode)."
17359
18483
  },
17360
18484
  "tooltip": {
17361
18485
  "anyOf": [
@@ -17372,12 +18496,65 @@
17372
18496
  "description": "Tooltip encoding (optional)."
17373
18497
  }
17374
18498
  },
18499
+ "additionalProperties": false,
18500
+ "description": "Encoding channels specific to map visualizations."
18501
+ },
18502
+ "MapPointsLayer": {
18503
+ "type": "object",
18504
+ "properties": {
18505
+ "data": {
18506
+ "type": "array",
18507
+ "items": {
18508
+ "$ref": "#/definitions/DataRow"
18509
+ },
18510
+ "description": "Tabular data for the points. Independent of the choropleth data join."
18511
+ },
18512
+ "longitude": {
18513
+ "$ref": "#/definitions/EncodingChannel",
18514
+ "description": "Field holding longitude (x coordinate)."
18515
+ },
18516
+ "latitude": {
18517
+ "$ref": "#/definitions/EncodingChannel",
18518
+ "description": "Field holding latitude (y coordinate)."
18519
+ },
18520
+ "size": {
18521
+ "$ref": "#/definitions/EncodingChannel",
18522
+ "description": "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."
18523
+ },
18524
+ "color": {
18525
+ "$ref": "#/definitions/EncodingChannel",
18526
+ "description": "Optional color encoding (nominal or quantitative). Independent scale from choropleth."
18527
+ },
18528
+ "tooltip": {
18529
+ "anyOf": [
18530
+ {
18531
+ "$ref": "#/definitions/EncodingChannel"
18532
+ },
18533
+ {
18534
+ "type": "array",
18535
+ "items": {
18536
+ "$ref": "#/definitions/EncodingChannel"
18537
+ }
18538
+ }
18539
+ ],
18540
+ "description": "Tooltip channel(s), same shape as MapEncoding.tooltip."
18541
+ },
18542
+ "key": {
18543
+ "$ref": "#/definitions/EncodingChannel",
18544
+ "description": "Stable id for event callbacks. Data-update transitions for points are not yet supported."
18545
+ },
18546
+ "opacity": {
18547
+ "type": "number",
18548
+ "description": "Fill opacity for point circles. Default 0.65. Range 0-1."
18549
+ }
18550
+ },
17375
18551
  "required": [
17376
- "key",
17377
- "color"
18552
+ "data",
18553
+ "longitude",
18554
+ "latitude"
17378
18555
  ],
17379
18556
  "additionalProperties": false,
17380
- "description": "Encoding channels specific to map visualizations."
18557
+ "description": "Point/symbol layer rendered above choropleth features."
17381
18558
  },
17382
18559
  "BarListSpec": {
17383
18560
  "type": "object",
@@ -17520,6 +18697,14 @@
17520
18697
  "$ref": "#/definitions/Chrome",
17521
18698
  "description": "Editorial chrome (title, subtitle, source, etc.)."
17522
18699
  },
18700
+ "chromeLayout": {
18701
+ "type": "string",
18702
+ "enum": [
18703
+ "subtract",
18704
+ "grow"
18705
+ ],
18706
+ "description": "How chrome height interacts with the container height budget.\n\n- `'subtract'` (default): the container height is fixed and chrome text (title/subtitle/source) is subtracted from it, shrinking the plot. This is the historical behavior.\n- `'grow'`: the container height is treated as the *plot* budget, and the rendered SVG grows taller by the measured chrome height. The plot keeps its full budget (minus axis/padding overhead) regardless of how many lines the title wraps to.\n\n`'grow'` is a no-op for faceted specs in this version."
18707
+ },
17523
18708
  "title": {
17524
18709
  "anyOf": [
17525
18710
  {
@@ -17628,7 +18813,7 @@
17628
18813
  "items": {
17629
18814
  "type": "string"
17630
18815
  },
17631
- "description": "Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed."
18816
+ "description": "Series names to hide from rendering by default. This is the authored persistence target for legend visibility. Runtime legend toggles (legend-toggle edit events) layer on top of this via ephemeral runtime sets but never modify it."
17632
18817
  },
17633
18818
  "seriesStyles": {
17634
18819
  "type": "object",