@cascivo/charts 0.7.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/charts.css CHANGED
@@ -1,2 +1,2 @@
1
- @layer cascivo.component{._frame_1kt6d_2{inline-size:100%;min-inline-size:0;position:relative}._frame_1kt6d_2>svg{block-size:auto;max-inline-size:100%}@media (prefers-reduced-motion:no-preference){._frame_1kt6d_2>svg{animation:_cascivo-chart-in_1kt6d_1 var(--cascivo-chart-anim-dur,.32s) var(--cascivo-chart-anim-ease,ease-out) both}@keyframes _cascivo-chart-in_1kt6d_1{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}._frame_1kt6d_2 svg :is(path,rect,circle,line)[data-series],._frame_1kt6d_2 svg path[data-link],._frame_1kt6d_2 svg path[data-depth],._frame_1kt6d_2 svg rect[data-day]{transition-property:var(--cascivo-chart-anim-props,opacity, fill, fill-opacity);transition-duration:var(--cascivo-chart-anim-dur,.24s);transition-timing-function:var(--cascivo-chart-anim-ease,ease)}._frame_1kt6d_2[data-no-anim]>svg{animation:none}._frame_1kt6d_2[data-no-anim] svg :is(path,rect,circle,line)[data-series],._frame_1kt6d_2[data-no-anim] svg path[data-link],._frame_1kt6d_2[data-no-anim] svg path[data-depth],._frame_1kt6d_2[data-no-anim] svg rect[data-day]{transition:none}}._fallback_1kt6d_67{clip-path:inset(50%);white-space:nowrap;block-size:1px;inline-size:1px;position:absolute;inset-inline-start:-9999px;overflow:hidden}._legend_zczeh_2{gap:var(--cascivo-space-2);padding-block:var(--cascivo-space-2);flex-wrap:wrap;display:flex}._item_zczeh_9{align-items:center;gap:var(--cascivo-space-1);padding:var(--cascivo-space-1) var(--cascivo-space-2);border:1px solid var(--cascivo-color-border);border-radius:var(--cascivo-radius-sm);cursor:pointer;font-size:var(--cascivo-font-size-sm);color:var(--cascivo-color-text);transition:opacity var(--cascivo-duration-150,.15s) ease;background:0 0;display:flex}._item_zczeh_9[data-state=off]{opacity:.4}._swatch_zczeh_27{border-radius:2px;flex-shrink:0;block-size:12px;inline-size:12px;display:inline-block}._kpi_1k88k_7{gap:var(--cascivo-space-2);padding:var(--cascivo-kpi-padding,var(--cascivo-space-4));border-radius:var(--cascivo-radius-surface);border:1px solid var(--cascivo-color-border);background:var(--cascivo-color-surface);flex-direction:column;min-inline-size:0;display:flex}._head_1k88k_19{justify-content:space-between;align-items:center;gap:var(--cascivo-space-2);min-inline-size:0;display:flex}._label_1k88k_27{font-size:var(--cascivo-text-sm);color:var(--cascivo-color-foreground-muted);overflow-wrap:anywhere;min-inline-size:0}._valueRow_1k88k_34{align-items:flex-end;gap:var(--cascivo-space-2);min-inline-size:0;display:flex}._value_1k88k_34{font-size:var(--cascivo-text-2xl);font-weight:var(--cascivo-font-bold);line-height:var(--cascivo-leading-none);color:var(--cascivo-color-foreground);font-variant-numeric:tabular-nums}._delta_1k88k_49{font-size:var(--cascivo-text-sm);font-weight:var(--cascivo-font-medium);white-space:nowrap;color:var(--cascivo-color-foreground-muted)}._delta_1k88k_49[data-trend=up]{color:var(--cascivo-color-success-foreground)}._delta_1k88k_49[data-trend=down]{color:var(--cascivo-color-destructive-foreground)}}
1
+ @layer cascivo.component{._frame_1kt6d_2{inline-size:100%;min-inline-size:0;position:relative}._frame_1kt6d_2>svg{block-size:auto;max-inline-size:100%}@media (prefers-reduced-motion:no-preference){._frame_1kt6d_2>svg{animation:_cascivo-chart-in_1kt6d_1 var(--cascivo-chart-anim-dur,.32s) var(--cascivo-chart-anim-ease,ease-out) both}@keyframes _cascivo-chart-in_1kt6d_1{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}._frame_1kt6d_2 svg :is(path,rect,circle,line)[data-series],._frame_1kt6d_2 svg path[data-link],._frame_1kt6d_2 svg path[data-depth],._frame_1kt6d_2 svg rect[data-day]{transition-property:var(--cascivo-chart-anim-props,opacity, fill, fill-opacity);transition-duration:var(--cascivo-chart-anim-dur,.24s);transition-timing-function:var(--cascivo-chart-anim-ease,ease)}._frame_1kt6d_2[data-no-anim]>svg{animation:none}._frame_1kt6d_2[data-no-anim] svg :is(path,rect,circle,line)[data-series],._frame_1kt6d_2[data-no-anim] svg path[data-link],._frame_1kt6d_2[data-no-anim] svg path[data-depth],._frame_1kt6d_2[data-no-anim] svg rect[data-day]{transition:none}}._fallback_1kt6d_67{clip-path:inset(50%);white-space:nowrap;block-size:1px;inline-size:1px;position:absolute;inset-inline-start:-9999px;overflow:hidden}._legend_1pign_2{gap:var(--cascivo-space-2);padding-block:var(--cascivo-space-2);flex-wrap:wrap;display:flex}._item_1pign_9{align-items:center;gap:var(--cascivo-space-1);padding:var(--cascivo-space-1) var(--cascivo-space-2);border:1px solid var(--cascivo-color-border);border-radius:var(--cascivo-radius-sm);cursor:pointer;font-size:var(--cascivo-text-sm);color:var(--cascivo-color-text);transition:opacity var(--cascivo-duration-150,.15s) ease;background:0 0;display:flex}._item_1pign_9[data-state=off]{opacity:.4}._swatch_1pign_27{border-radius:2px;flex-shrink:0;block-size:12px;inline-size:12px;display:inline-block}._kpi_nrb8p_7{gap:var(--cascivo-space-2);padding:var(--cascivo-kpi-padding,var(--cascivo-space-4));border-radius:var(--cascivo-radius-surface);border:1px solid var(--cascivo-color-border);background:var(--cascivo-color-surface);flex-direction:column;min-inline-size:0;display:flex}._head_nrb8p_19{justify-content:space-between;align-items:center;gap:var(--cascivo-space-2);min-inline-size:0;display:flex}._label_nrb8p_27{font-size:var(--cascivo-text-sm);color:var(--cascivo-color-foreground-muted);overflow-wrap:anywhere;min-inline-size:0}._valueRow_nrb8p_34{align-items:flex-end;gap:var(--cascivo-space-2);min-inline-size:0;display:flex}._value_nrb8p_34{font-size:var(--cascivo-text-2xl);font-weight:var(--cascivo-font-bold);line-height:var(--cascivo-leading-none);color:var(--cascivo-color-foreground);font-variant-numeric:tabular-nums}._delta_nrb8p_49{font-size:var(--cascivo-text-sm);font-weight:var(--cascivo-font-medium);white-space:nowrap;color:var(--cascivo-color-foreground-muted)}._delta_nrb8p_49[data-sentiment=good]{color:var(--cascivo-color-success-foreground)}._delta_nrb8p_49[data-sentiment=bad]{color:var(--cascivo-color-destructive-foreground)}}
2
2
  /*$vite$:1*/
package/dist/index.d.ts CHANGED
@@ -7,11 +7,31 @@ interface LinearScale {
7
7
  range: [number, number];
8
8
  map(value: number): number;
9
9
  invert(position: number): number;
10
- ticks(count?: number): number[];
10
+ ticks(count?: number, allowDecimals?: boolean): number[];
11
11
  }
12
12
  declare function linearScale(domain: [number, number], range: [number, number]): LinearScale;
13
- /** Extended nice-numbers: steps are 1, 2, 2.5 or 5 × 10^k covering [min, max]. */
14
- declare function niceTicks(min: number, max: number, count?: number): number[];
13
+ /**
14
+ * Extended nice-numbers: steps are 1, 2, 2.5 or 5 × 10^k covering [min, max].
15
+ *
16
+ * `count` is a **density hint**, not a tick count — the step is snapped to the nearest
17
+ * nice number, so the result may have more or fewer ticks than asked for. That is standard
18
+ * (d3 behaves the same) and is right for continuous data.
19
+ *
20
+ * It is wrong for a whole-number domain. `(max - min) / count` happily produces a
21
+ * *fractional* step when the requested density is finer than the data's own unit:
22
+ * `max=1, count=2` → rawStep 0.5 → `[0, 0.5, 1]`; `max=1, count=5` →
23
+ * `[0, 0.2, 0.4, 0.6, 0.8, 1]`. An incident-count chart with 0–3 incidents per severity
24
+ * hits this constantly, and the obvious workaround (`yTicks={max + 1}`) lands straight on
25
+ * it at `max=1` (2026-07-28 report C17a).
26
+ *
27
+ * So when every bound is an integer, the step is floored at 1 and snapped to an integer
28
+ * nice-number unless `allowDecimals` is explicitly `true`. Callers with genuinely
29
+ * continuous integer-bounded data (a 0–1 ratio axis) opt back in.
30
+ *
31
+ * @param allowDecimals Force fractional steps on (`true`) or off (`false`). Omit to
32
+ * auto-detect: integer bounds get integer ticks, anything else keeps today's behavior.
33
+ */
34
+ declare function niceTicks(min: number, max: number, count?: number, allowDecimals?: boolean): number[];
15
35
  interface BandScale<T extends string = string> {
16
36
  domain: readonly T[];
17
37
  range: [number, number];
@@ -226,7 +246,24 @@ interface BarChartSeries<Datum> {
226
246
  id: string;
227
247
  label: string;
228
248
  data: readonly Datum[];
229
- color?: string;
249
+ /**
250
+ * Bar color. A string colors the whole series; a function colors each bar from its own
251
+ * datum.
252
+ *
253
+ * The per-datum form exists for the common single-series categorical chart whose
254
+ * categories each carry meaning — incidents by severity, where SEV1 should read as
255
+ * danger and SEV4 as neutral regardless of which bar is tallest. Before it, the only
256
+ * route was one single-point series per category with `mode="grouped"`, which renders
257
+ * *wrong*: the bars overlap and only the first series' category label survives
258
+ * (2026-07-28 report C18).
259
+ *
260
+ * ```tsx
261
+ * series={[{ id: 'count', label: 'Incidents', data, color: (d) => SEVERITY_COLOR[d.x] }]}
262
+ * ```
263
+ *
264
+ * Each bar is also stamped with `data-x`, so CSS can target one category directly.
265
+ */
266
+ color?: string | ((datum: Datum, index: number) => string);
230
267
  /**
231
268
  * Per-series Y accessor. Overrides the chart-level `y` for this series only —
232
269
  * use it to plot two series from one shared `data` row against different fields
@@ -247,6 +284,20 @@ interface BarChartProps<Datum = {
247
284
  * only; to plot multiple fields from one row, give each series its own `y`.
248
285
  */
249
286
  y: (d: Datum) => number;
287
+ /**
288
+ * ⚠ **Not rendered as a visible heading.** This becomes the chart's accessible name
289
+ * (the SVG `<caption>` / `aria-label`), which is why it is required — a chart with no
290
+ * accessible name is unusable with a screen reader.
291
+ *
292
+ * For a visible heading, put a `CardTitle`/`Heading` above the chart. The prop name
293
+ * promises a heading and an adopter wrote one, saw nothing, and ended up with a
294
+ * redundant `CardTitle` on every chart anyway.
295
+ *
296
+ * It is NOT renamed to `ariaLabel` (the catalog's name for an invisible accessible name)
297
+ * because an alias would have to make both optional, which drops the compile-time
298
+ * guarantee that every chart has an accessible name. The requirement is worth more than
299
+ * the naming consistency; this warning is the compensation.
300
+ */
250
301
  title: string;
251
302
  description?: string;
252
303
  /**
@@ -267,16 +318,63 @@ interface BarChartProps<Datum = {
267
318
  */
268
319
  width?: number;
269
320
  height?: number;
321
+ /**
322
+ * Approximate number of ticks on the x-axis.
323
+ *
324
+ * ⚠ **Follows SCREEN position, so its meaning swaps with `orientation`.** On a vertical
325
+ * chart the x-axis is the category axis; on a horizontal one it is the VALUE axis. Prefer
326
+ * {@link BarChartProps.valueAxisTicks} / {@link BarChartProps.categoryAxisTicks}, which
327
+ * name the axis by role and never swap.
328
+ *
329
+ * @defaultValue `5`
330
+ * @deprecated Use `valueAxisTicks` / `categoryAxisTicks`.
331
+ */
270
332
  xTicks?: number;
271
333
  /**
272
334
  * Approximate number of ticks on the y-axis.
273
335
  *
336
+ * ⚠ **Follows SCREEN position, so its meaning swaps with `orientation`** — see
337
+ * {@link BarChartProps.xTicks}.
338
+ *
274
339
  * @defaultValue `5`
275
- * @see the component manifest
340
+ * @deprecated Use `valueAxisTicks` / `categoryAxisTicks`.
276
341
  */
277
342
  yTicks?: number;
278
- /** Show every Nth category label (and always the last) to thin a crowded x-axis. */
343
+ /**
344
+ * Approximate number of ticks on the **value** axis, whichever way the chart is turned.
345
+ *
346
+ * This is the prop you want. `xTicks`/`yTicks` are named for where the axis is *drawn*,
347
+ * so on `orientation="horizontal"` the value axis moves from screen-y to screen-x and the
348
+ * controlling prop moves with it — `yTicks={1}` silently does nothing while `xTicks={1}`
349
+ * works. Meanwhile `xLabelEvery` does NOT swap: it always strides the category axis. Two
350
+ * conventions in one component, with nothing in the types to say so (2026-07-28 report
351
+ * C17b). Wins over `xTicks`/`yTicks` when both are given.
352
+ *
353
+ * @defaultValue `5`
354
+ */
355
+ valueAxisTicks?: number;
356
+ /**
357
+ * Approximate number of ticks on the CATEGORY axis, on both orientations. Role-named twin
358
+ * of valueAxisTicks.
359
+ *
360
+ * @defaultValue `5`
361
+ * @see the component manifest
362
+ */
363
+ categoryAxisTicks?: number;
364
+ /**
365
+ * Show every Nth category label (and always the last) to thin a crowded axis.
366
+ *
367
+ * Always strides the **category** axis (the `x` field of each datum), on both
368
+ * orientations — unlike `xTicks`/`yTicks`, which follow screen position.
369
+ * {@link BarChartProps.categoryLabelEvery} is the unambiguous name; this is kept for
370
+ * compatibility.
371
+ */
279
372
  xLabelEvery?: number;
373
+ /**
374
+ * Show every Nth category label (and always the last). Role-named twin of
375
+ * `xLabelEvery`; wins when both are given.
376
+ */
377
+ categoryLabelEvery?: number;
280
378
  legend?: boolean;
281
379
  tooltip?: boolean;
282
380
  /** Custom tooltip formatter. Stacked default lists "label · total" + per-layer values. */
@@ -307,6 +405,14 @@ interface BarChartProps<Datum = {
307
405
  fill?: FillKind;
308
406
  /** Pattern motif when `fill="pattern"`. */
309
407
  patternKind?: PatternKind;
408
+ /**
409
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
410
+ * a string, or a `Date`, whichever the series carries.
411
+ *
412
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
413
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
414
+ */
415
+ format?: (value: number | string | Date) => string;
310
416
  }
311
417
  declare function BarChart<Datum = {
312
418
  x: string;
@@ -323,7 +429,10 @@ declare function BarChart<Datum = {
323
429
  height,
324
430
  xTicks,
325
431
  yTicks,
432
+ valueAxisTicks,
433
+ categoryAxisTicks,
326
434
  xLabelEvery,
435
+ categoryLabelEvery,
327
436
  legend,
328
437
  tooltip,
329
438
  tooltipFormat,
@@ -333,7 +442,8 @@ declare function BarChart<Datum = {
333
442
  labels,
334
443
  onSelect,
335
444
  fill,
336
- patternKind
445
+ patternKind,
446
+ format: xFormat
337
447
  }: BarChartProps<Datum>): import("react").JSX.Element;
338
448
  /** One layer of a stacked bar at a category. */
339
449
  interface StackedSegment {
@@ -942,19 +1052,38 @@ declare function leftMarginForLabels(leftAxisLabels: readonly string[], plain: b
942
1052
  */
943
1053
  declare function rightMarginForLabels(options?: {
944
1054
  /** Labels of a right-hand value axis, if the chart has one. */rightAxisLabels?: readonly string[]; /** Labels of the bottom axis — only the last one's overhang matters. */
945
- bottomAxisLabels?: readonly string[];
1055
+ bottomAxisLabels?: readonly string[]; /** Whether the right axis also draws a rotated title outside its tick labels. */
1056
+ rightAxisTitle?: boolean;
946
1057
  plain?: boolean | undefined;
947
1058
  }): number;
1059
+ /**
1060
+ * Approximate block size (px) of one axis label at the 11px axis font — the line box
1061
+ * plus a little separation. What crowds labels stacked down a y-axis is their *height*,
1062
+ * not the length of the text.
1063
+ */
1064
+ declare const AXIS_LINE_PX = 14;
948
1065
  /**
949
1066
  * Stride for a crowded categorical (band) axis: render every Nth label so they stop
950
1067
  * colliding (e.g. 14 `Jul 1`…`Jul 14` dates in a narrow chart). Returns `undefined`
951
1068
  * when every label fits — callers pass that straight to `Axis.labelEvery` (all shown).
952
1069
  * An explicit `xLabelEvery` from the caller always overrides this.
953
1070
  *
1071
+ * `direction` says which way the labels are laid out along the axis, and therefore which
1072
+ * dimension of the label competes for the band:
1073
+ *
1074
+ * - `'horizontal'` (a bottom category axis) — labels sit side by side, so the constraint
1075
+ * is text *width*, estimated from the character count.
1076
+ * - `'vertical'` (a horizontal bar chart's category axis, which runs down the y-axis) —
1077
+ * labels stack, so the constraint is line *height*, which is the same for every label.
1078
+ *
1079
+ * Measuring the vertical case against text width is the bug this parameter fixes: seven
1080
+ * categories down a 240px axis were strided away as "crowded" because one of them was
1081
+ * eight characters long, which is not a fact about vertical space at all.
1082
+ *
954
1083
  * `Axis` always draws the final label, and drops the strided label before it when the two
955
1084
  * would collide — so a stride that doesn't divide the domain evenly is safe.
956
1085
  */
957
- declare function autoLabelStride(labels: readonly string[], axisLength: number): number | undefined;
1086
+ declare function autoLabelStride(labels: readonly string[], axisLength: number, direction?: 'horizontal' | 'vertical'): number | undefined;
958
1087
  /**
959
1088
  * Pure index-window math shared by the DataZoom slider and the in-plot wheel/drag
960
1089
  * zoom-pan. A "window" is an inclusive `[startIndex, endIndex]` over a series of
@@ -1037,6 +1166,21 @@ interface AxisProps {
1037
1166
  * see `autoLabelStride`, which computes this for you. Pass explicitly only to override.
1038
1167
  */
1039
1168
  labelEvery?: number | undefined;
1169
+ /**
1170
+ * Axis title, drawn outside the tick labels — rotated for a vertical axis.
1171
+ *
1172
+ * On a dual-axis chart this is the whole mechanism for saying which series belongs to
1173
+ * which scale. `secondAxis.label` was typed on `AreaChart`/`LineChart` for months and
1174
+ * rendered nothing, so a two-series chart was unreadable without a legend workaround.
1175
+ */
1176
+ title?: string | undefined;
1177
+ /**
1178
+ * Distance from the axis line to the title, in px. Should clear the widest tick label;
1179
+ * callers already compute that width for their margins.
1180
+ *
1181
+ * @defaultValue `36`
1182
+ */
1183
+ titleOffset?: number | undefined;
1040
1184
  transform?: string;
1041
1185
  }
1042
1186
  declare function Axis({
@@ -1046,6 +1190,8 @@ declare function Axis({
1046
1190
  format,
1047
1191
  tickCount,
1048
1192
  labelEvery,
1193
+ title,
1194
+ titleOffset,
1049
1195
  transform
1050
1196
  }: AxisProps): import("react").JSX.Element;
1051
1197
  interface GridLinesProps {
@@ -1236,6 +1382,12 @@ interface LineChartSeries<Datum> {
1236
1382
  id: string;
1237
1383
  label: string;
1238
1384
  data: readonly Datum[];
1385
+ /**
1386
+ * Series colour. **Omit it** — the Nth series automatically takes `--cascivo-chart-N`,
1387
+ * eight distinct hues per theme in both light and dark, so a multi-series chart
1388
+ * differentiates itself with no configuration. Set this only to override the position,
1389
+ * e.g. to keep "errors" red wherever it lands in the array.
1390
+ */
1239
1391
  color?: string;
1240
1392
  /** Which y-axis this series is measured against. Default 'left'. */
1241
1393
  axis?: 'left' | 'right';
@@ -1259,6 +1411,20 @@ interface LineChartProps<Datum = {
1259
1411
  * plot multiple fields from one row, give each series its own `y`.
1260
1412
  */
1261
1413
  y: (d: Datum) => number;
1414
+ /**
1415
+ * ⚠ **Not rendered as a visible heading.** This becomes the chart's accessible name
1416
+ * (the SVG `<caption>` / `aria-label`), which is why it is required — a chart with no
1417
+ * accessible name is unusable with a screen reader.
1418
+ *
1419
+ * For a visible heading, put a `CardTitle`/`Heading` above the chart. The prop name
1420
+ * promises a heading and an adopter wrote one, saw nothing, and ended up with a
1421
+ * redundant `CardTitle` on every chart anyway.
1422
+ *
1423
+ * It is NOT renamed to `ariaLabel` (the catalog's name for an invisible accessible name)
1424
+ * because an alias would have to make both optional, which drops the compile-time
1425
+ * guarantee that every chart has an accessible name. The requirement is worth more than
1426
+ * the naming consistency; this warning is the compensation.
1427
+ */
1262
1428
  title: string;
1263
1429
  description?: string;
1264
1430
  /**
@@ -1290,6 +1456,25 @@ interface LineChartProps<Datum = {
1290
1456
  * @see the component manifest
1291
1457
  */
1292
1458
  yTicks?: number;
1459
+ /**
1460
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a
1461
+ * string, or a `Date`, whichever the series carries.
1462
+ *
1463
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for
1464
+ * a time series) renders as `1,785,217,000,000`. Passing `Date` objects instead switches
1465
+ * the axis to a time scale, but that format is fixed, so every bucket narrower than a day
1466
+ * collapses to the same label — worse than the epoch number, which at least differed
1467
+ * between buckets. This threads through `Axis`'s existing `format`, mirroring what
1468
+ * `secondAxis.format` already offers on the right (2026-07-28 report C16).
1469
+ *
1470
+ * ```tsx
1471
+ * <LineChart
1472
+ * series={series}
1473
+ * format={(x) => new Date(Number(x)).toLocaleTimeString([], { timeStyle: 'short' })}
1474
+ * />
1475
+ * ```
1476
+ */
1477
+ format?: (value: number | string | Date) => string;
1293
1478
  legend?: boolean;
1294
1479
  tooltip?: boolean;
1295
1480
  formatTooltip?: (datum: Datum, series: LineChartSeries<Datum>) => string;
@@ -1386,6 +1571,7 @@ declare function LineChart<Datum = {
1386
1571
  height,
1387
1572
  xTicks,
1388
1573
  yTicks,
1574
+ format: xFormat,
1389
1575
  legend,
1390
1576
  tooltip,
1391
1577
  formatTooltip,
@@ -1415,6 +1601,12 @@ interface AreaChartSeries<Datum> {
1415
1601
  id: string;
1416
1602
  label: string;
1417
1603
  data: readonly Datum[];
1604
+ /**
1605
+ * Series colour. **Omit it** — the Nth series automatically takes `--cascivo-chart-N`,
1606
+ * eight distinct hues per theme in both light and dark, so a multi-series chart
1607
+ * differentiates itself with no configuration. Set this only to override the position,
1608
+ * e.g. to keep "errors" red wherever it lands in the array.
1609
+ */
1418
1610
  color?: string;
1419
1611
  /** Which y-axis this series is measured against (ignored when `stacked`). Default 'left'. */
1420
1612
  axis?: 'left' | 'right';
@@ -1443,6 +1635,20 @@ interface AreaChartProps<Datum = {
1443
1635
  * plot multiple fields from one row, give each series its own `y`.
1444
1636
  */
1445
1637
  y: (d: Datum) => number;
1638
+ /**
1639
+ * ⚠ **Not rendered as a visible heading.** This becomes the chart's accessible name
1640
+ * (the SVG `<caption>` / `aria-label`), which is why it is required — a chart with no
1641
+ * accessible name is unusable with a screen reader.
1642
+ *
1643
+ * For a visible heading, put a `CardTitle`/`Heading` above the chart. The prop name
1644
+ * promises a heading and an adopter wrote one, saw nothing, and ended up with a
1645
+ * redundant `CardTitle` on every chart anyway.
1646
+ *
1647
+ * It is NOT renamed to `ariaLabel` (the catalog's name for an invisible accessible name)
1648
+ * because an alias would have to make both optional, which drops the compile-time
1649
+ * guarantee that every chart has an accessible name. The requirement is worth more than
1650
+ * the naming consistency; this warning is the compensation.
1651
+ */
1446
1652
  title: string;
1447
1653
  description?: string;
1448
1654
  stacked?: boolean;
@@ -1543,6 +1749,16 @@ interface AreaChartProps<Datum = {
1543
1749
  decimate?: boolean | AreaDecimateOptions;
1544
1750
  /** Render a toolbox (PNG/SVG export, data-view toggle, restore). `true` enables all tools. */
1545
1751
  toolbox?: boolean | ToolboxOptions;
1752
+ /**
1753
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
1754
+ * or a `Date`, whichever the series carries.
1755
+ *
1756
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
1757
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
1758
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
1759
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
1760
+ */
1761
+ format?: (value: number | string | Date) => string;
1546
1762
  }
1547
1763
  declare function AreaChart<Datum = {
1548
1764
  x: number;
@@ -1575,7 +1791,8 @@ declare function AreaChart<Datum = {
1575
1791
  tooltipMode,
1576
1792
  secondAxis,
1577
1793
  decimate,
1578
- toolbox
1794
+ toolbox,
1795
+ format: xFormat
1579
1796
  }: AreaChartProps<Datum>): import("react").JSX.Element;
1580
1797
  interface PieChartDatum {
1581
1798
  id: string;
@@ -1714,6 +1931,16 @@ interface ScatterChartProps {
1714
1931
  visualMap?: VisualMapOptions;
1715
1932
  /** Render a toolbox (PNG/SVG export, data-view toggle, restore). `true` enables all tools. */
1716
1933
  toolbox?: boolean | ToolboxOptions;
1934
+ /**
1935
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
1936
+ * or a `Date`, whichever the series carries.
1937
+ *
1938
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
1939
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
1940
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
1941
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
1942
+ */
1943
+ format?: (value: number | string | Date) => string;
1717
1944
  }
1718
1945
  declare function ScatterChart({
1719
1946
  series,
@@ -1733,7 +1960,8 @@ declare function ScatterChart({
1733
1960
  glyph,
1734
1961
  renderer,
1735
1962
  visualMap,
1736
- toolbox
1963
+ toolbox,
1964
+ format
1737
1965
  }: ScatterChartProps): import("react").JSX.Element;
1738
1966
  interface SparklineBaseProps {
1739
1967
  data: readonly number[];
@@ -1840,6 +2068,19 @@ interface KpiProps {
1840
2068
  * it receives the raw number and owns the entire string, sign included.
1841
2069
  */
1842
2070
  deltaFormat?: 'number' | 'percent' | ((delta: number) => string);
2071
+ /**
2072
+ * Which direction is *good* for this metric — the colour, independent of the arrow.
2073
+ *
2074
+ * The arrow follows the sign of `delta`; this says whether that movement is welcome.
2075
+ * They coincide only for metrics where up is better, and assuming that made every
2076
+ * error-rate, latency, cost and churn tile render its worst news in green. Negating
2077
+ * `delta` to fix the colour also flips the arrow, so there was no correct answer.
2078
+ *
2079
+ * `'neutral'` keeps the arrow and drops the sentiment colour.
2080
+ *
2081
+ * @defaultValue `up`
2082
+ */
2083
+ goodDirection?: 'up' | 'down' | 'neutral';
1843
2084
  deltaLabel?: string;
1844
2085
  icon?: ReactNode;
1845
2086
  sparkline?: readonly number[];
@@ -1850,6 +2091,7 @@ declare function Kpi({
1850
2091
  label,
1851
2092
  delta,
1852
2093
  deltaFormat,
2094
+ goodDirection,
1853
2095
  deltaLabel,
1854
2096
  icon,
1855
2097
  sparkline,
@@ -1879,6 +2121,16 @@ interface HistogramProps {
1879
2121
  * @see the component manifest
1880
2122
  */
1881
2123
  plain?: boolean;
2124
+ /**
2125
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2126
+ * or a `Date`, whichever the series carries.
2127
+ *
2128
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2129
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2130
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2131
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2132
+ */
2133
+ format?: (value: number | string | Date) => string;
1882
2134
  }
1883
2135
  declare function Histogram({
1884
2136
  data,
@@ -1889,7 +2141,8 @@ declare function Histogram({
1889
2141
  width: fixedWidth,
1890
2142
  height,
1891
2143
  className,
1892
- plain
2144
+ plain,
2145
+ format
1893
2146
  }: HistogramProps): import("react").JSX.Element;
1894
2147
  interface BoxplotSeries {
1895
2148
  id: string;
@@ -1918,6 +2171,14 @@ interface BoxplotProps {
1918
2171
  * @see the component manifest
1919
2172
  */
1920
2173
  plain?: boolean;
2174
+ /**
2175
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
2176
+ * a string, or a `Date`, whichever the series carries.
2177
+ *
2178
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
2179
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
2180
+ */
2181
+ format?: (value: number | string | Date) => string;
1921
2182
  }
1922
2183
  declare function Boxplot({
1923
2184
  series,
@@ -1926,7 +2187,8 @@ declare function Boxplot({
1926
2187
  width: fixedWidth,
1927
2188
  height,
1928
2189
  className,
1929
- plain
2190
+ plain,
2191
+ format
1930
2192
  }: BoxplotProps): import("react").JSX.Element;
1931
2193
  interface BubbleDatum {
1932
2194
  x: number;
@@ -1962,6 +2224,16 @@ interface BubbleChartProps {
1962
2224
  plain?: boolean;
1963
2225
  /** Point glyph shape — a fixed shape, or a function to encode a category by shape. Defaults to a circle. */
1964
2226
  glyph?: GlyphShape | ((d: BubbleDatum, seriesName: string) => GlyphShape);
2227
+ /**
2228
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2229
+ * or a `Date`, whichever the series carries.
2230
+ *
2231
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2232
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2233
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2234
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2235
+ */
2236
+ format?: (value: number | string | Date) => string;
1965
2237
  }
1966
2238
  declare function BubbleChart({
1967
2239
  series,
@@ -1972,7 +2244,8 @@ declare function BubbleChart({
1972
2244
  tooltip,
1973
2245
  className,
1974
2246
  plain,
1975
- glyph
2247
+ glyph,
2248
+ format
1976
2249
  }: BubbleChartProps): import("react").JSX.Element;
1977
2250
  interface ComboChartBar {
1978
2251
  label: string;
@@ -2032,6 +2305,16 @@ interface ComboChartProps {
2032
2305
  plain?: boolean;
2033
2306
  /** Reference lines, bands, and markers. `y` maps to the bar value axis. */
2034
2307
  annotations?: readonly Annotation[];
2308
+ /**
2309
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2310
+ * or a `Date`, whichever the series carries.
2311
+ *
2312
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2313
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2314
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2315
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2316
+ */
2317
+ format?: (value: number | string | Date) => string;
2035
2318
  }
2036
2319
  declare function ComboChart({
2037
2320
  bars,
@@ -2048,7 +2331,8 @@ declare function ComboChart({
2048
2331
  tooltip,
2049
2332
  className,
2050
2333
  plain,
2051
- annotations
2334
+ annotations,
2335
+ format
2052
2336
  }: ComboChartProps): import("react").JSX.Element;
2053
2337
  interface HeatmapDatum {
2054
2338
  x: string;
@@ -2081,6 +2365,14 @@ interface HeatmapProps {
2081
2365
  visualMap?: VisualMapOptions;
2082
2366
  /** Render a toolbox (PNG/SVG export, data-view toggle, restore). `true` enables all tools. */
2083
2367
  toolbox?: boolean | ToolboxOptions;
2368
+ /**
2369
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
2370
+ * a string, or a `Date`, whichever the series carries.
2371
+ *
2372
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
2373
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
2374
+ */
2375
+ format?: (value: number | string | Date) => string;
2084
2376
  }
2085
2377
  declare function Heatmap({
2086
2378
  data,
@@ -2091,7 +2383,8 @@ declare function Heatmap({
2091
2383
  className,
2092
2384
  plain,
2093
2385
  visualMap,
2094
- toolbox
2386
+ toolbox,
2387
+ format
2095
2388
  }: HeatmapProps): import("react").JSX.Element;
2096
2389
  interface TreemapDatum {
2097
2390
  id: string;
@@ -2380,6 +2673,14 @@ interface StreamProps {
2380
2673
  * @see the component manifest
2381
2674
  */
2382
2675
  plain?: boolean;
2676
+ /**
2677
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
2678
+ * a string, or a `Date`, whichever the series carries.
2679
+ *
2680
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
2681
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
2682
+ */
2683
+ format?: (value: number | string | Date) => string;
2383
2684
  }
2384
2685
  /** A streamgraph — stacked areas on a flowing (centered) baseline. */
2385
2686
  declare function Stream({
@@ -2394,7 +2695,8 @@ declare function Stream({
2394
2695
  legend,
2395
2696
  tooltip,
2396
2697
  className,
2397
- plain
2698
+ plain,
2699
+ format
2398
2700
  }: StreamProps): import("react").JSX.Element;
2399
2701
  interface SunburstProps {
2400
2702
  /** Root of the hierarchy. Leaves carry `value`; parents sum their children. */
@@ -2471,12 +2773,12 @@ declare function Sankey({
2471
2773
  className,
2472
2774
  plain
2473
2775
  }: SankeyProps): import("react").JSX.Element;
2474
- interface CalendarDatum {
2776
+ interface CalendarHeatmapDatum {
2475
2777
  day: string | Date;
2476
2778
  value: number;
2477
2779
  }
2478
- interface CalendarProps {
2479
- data: readonly CalendarDatum[];
2780
+ interface CalendarHeatmapProps {
2781
+ data: readonly CalendarHeatmapDatum[];
2480
2782
  title: string;
2481
2783
  description?: string;
2482
2784
  /** Range start/end (ISO string or Date). Defaults to the data's min/max day. */
@@ -2505,7 +2807,7 @@ interface CalendarProps {
2505
2807
  visualMap?: VisualMapOptions;
2506
2808
  }
2507
2809
  /** A calendar heatmap — a week-column grid of day cells colored by value. */
2508
- declare function Calendar({
2810
+ declare function CalendarHeatmap({
2509
2811
  data,
2510
2812
  title,
2511
2813
  description,
@@ -2517,7 +2819,7 @@ declare function Calendar({
2517
2819
  className,
2518
2820
  plain,
2519
2821
  visualMap
2520
- }: CalendarProps): import("react").JSX.Element;
2822
+ }: CalendarHeatmapProps): import("react").JSX.Element;
2521
2823
  interface CandlestickDatum {
2522
2824
  /** Period label (date string or index). */
2523
2825
  t: string;
@@ -2586,6 +2888,16 @@ interface CandlestickProps {
2586
2888
  * @see the component manifest
2587
2889
  */
2588
2890
  tooltipMode?: 'item' | 'axis';
2891
+ /**
2892
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2893
+ * or a `Date`, whichever the series carries.
2894
+ *
2895
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2896
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2897
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2898
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2899
+ */
2900
+ format?: (value: number | string | Date) => string;
2589
2901
  }
2590
2902
  declare function Candlestick({
2591
2903
  data: rawData,
@@ -2605,7 +2917,8 @@ declare function Candlestick({
2605
2917
  dataZoom,
2606
2918
  zoom,
2607
2919
  syncId,
2608
- tooltipMode
2920
+ tooltipMode,
2921
+ format: xFormat
2609
2922
  }: CandlestickProps): import("react").JSX.Element;
2610
2923
  interface PolarDatum {
2611
2924
  label: string;
@@ -2740,4 +3053,4 @@ declare function Gauge({
2740
3053
  className,
2741
3054
  plain
2742
3055
  }: GaugeProps): import("react").JSX.Element;
2743
- export { AXIS_CHAR_PX, AggOp, AggregateSpec, AngleBand, Annotation, AnnotationContext, AnnotationScale, AreaChart, AreaChartProps, AreaChartSeries, AreaDecimateOptions, Axis, AxisProps, BandScale, BarChart, BarChartProps, BarChartSeries, Bin, BoundStream, BoxStats, Boxplot, BoxplotProps, BoxplotSeries, Brush, BrushProps, BubbleChart, BubbleChartProps, BubbleDatum, BubbleSeries, Bullet, BulletProps, Calendar, CalendarDatum, CalendarProps, Candlestick, CandlestickDatum, CandlestickProps, CanvasLayer, CanvasLayerProps, CanvasPaint, CanvasSize, CategoryDatum, CategoryMapping, ChartDefs, ChartDefsProps, ChartFrame, ChartFrameProps, ChartSize, ComboChart, ComboChartBar, ComboChartPoint, ComboChartProps, Curve, DEFAULT_MARGINS, DataLabel, DataLabelProps, DataZoom, DataZoomProps, DecimateMethod, DecimateOptions, DefSeries, EncodeMapping, EncodedResult, EncodedSeries, FillKind, Funnel, FunnelProps, FunnelStage, Gauge, GaugeProps, GaugeThreshold, Glyph, GlyphProps, GlyphShape, GridLines, GridLinesProps, Heatmap, HeatmapDatum, HeatmapProps, HierNode, Histogram, HistogramBin, HistogramProps, Kpi, KpiProps, LabelOptions, LaidLink, LaidNode, Legend, LegendProps, LegendSeries, LineChart, LineChartProps, LineChartSeries, LinearScale, LogScale, Meter, MeterProps, MeterThresholds, PLAIN_MARGINS, PartitionedNode, PatternKind, PieChart, PieChartDatum, PieChartProps, Point, Polar, PolarDatum, PolarProps, Pt, Radar, RadarProps, RadarSeries, RadialBar, RadialBarDatum, RadialBarProps, RampKind, Rect, RegressionResult, RegressionType, ResolvedLabelOptions, Row, Sankey, SankeyLayout, SankeyLink, SankeyNode, SankeyOptions, SankeyProps, ScatterChart, ScatterChartProps, ScatterChartSeries, ScatterDatum, Sparkline, SparklineProps, StackedRow, StackedSegment, Stream, StreamDecimate, StreamOffset, StreamProps, StreamSeries, StreamSeriesOptions, StreamSource, Sunburst, SunburstProps, SyncGroup, Text, TextProps, TimeScale, Toolbox, ToolboxOptions, ToolboxProps, Treemap, TreemapDatum, TreemapNode, TreemapProps, TreemapRect, Vec, VisualChannel, VisualMap, VisualMapOptions, VisualMapProps, VisualMode, VisualResult, ZoomConfig, _syncGroupCount, aggregate, angleBand, annotationSummary, arcPath, areaPath, autoLabelStride, bandScale, bin, binValues, bindStream, boxStats, cellPath, decimate$1 as decimate, divergingRamp, download, encode, encodeCategory, extent, fillFor, filter, getSyncGroup, glyphPath, gradientId, isZoomed, leftMarginForLabels, linePath, linearScale, linkPath, logScale, lttb, mapVisual, maxDepth, minmax, nearestIndex, niceTicks, panWindow, partition, patternId, pieceIndex, polarPoint, quantize, radiusScale, rampLightness, rampOf, rampStops, regression, releaseSyncGroup, renderAnnotation, renderAnnotations, resolveColor, resolveLabels, rightMarginForLabels, sankeyLayout, sequentialRamp, serializeSvg, sort, splitDefined, sqrtScale, squarify, stackSeries, streamExtent, streamLayout, sumValue, svgToPngBlob, timeScale, toStackedSeries, useChartSize, useStreamSeries, visualVisible, voronoiCells, voronoiFind, wrapText, zoomWindow };
3056
+ export { AXIS_CHAR_PX, AXIS_LINE_PX, AggOp, AggregateSpec, AngleBand, Annotation, AnnotationContext, AnnotationScale, AreaChart, AreaChartProps, AreaChartSeries, AreaDecimateOptions, Axis, AxisProps, BandScale, BarChart, BarChartProps, BarChartSeries, Bin, BoundStream, BoxStats, Boxplot, BoxplotProps, BoxplotSeries, Brush, BrushProps, BubbleChart, BubbleChartProps, BubbleDatum, BubbleSeries, Bullet, BulletProps, CalendarHeatmap, CalendarHeatmapDatum, CalendarHeatmapProps, Candlestick, CandlestickDatum, CandlestickProps, CanvasLayer, CanvasLayerProps, CanvasPaint, CanvasSize, CategoryDatum, CategoryMapping, ChartDefs, ChartDefsProps, ChartFrame, ChartFrameProps, ChartSize, ComboChart, ComboChartBar, ComboChartPoint, ComboChartProps, Curve, DEFAULT_MARGINS, DataLabel, DataLabelProps, DataZoom, DataZoomProps, DecimateMethod, DecimateOptions, DefSeries, EncodeMapping, EncodedResult, EncodedSeries, FillKind, Funnel, FunnelProps, FunnelStage, Gauge, GaugeProps, GaugeThreshold, Glyph, GlyphProps, GlyphShape, GridLines, GridLinesProps, Heatmap, HeatmapDatum, HeatmapProps, HierNode, Histogram, HistogramBin, HistogramProps, Kpi, KpiProps, LabelOptions, LaidLink, LaidNode, Legend, LegendProps, LegendSeries, LineChart, LineChartProps, LineChartSeries, LinearScale, LogScale, Meter, MeterProps, MeterThresholds, PLAIN_MARGINS, PartitionedNode, PatternKind, PieChart, PieChartDatum, PieChartProps, Point, Polar, PolarDatum, PolarProps, Pt, Radar, RadarProps, RadarSeries, RadialBar, RadialBarDatum, RadialBarProps, RampKind, Rect, RegressionResult, RegressionType, ResolvedLabelOptions, Row, Sankey, SankeyLayout, SankeyLink, SankeyNode, SankeyOptions, SankeyProps, ScatterChart, ScatterChartProps, ScatterChartSeries, ScatterDatum, Sparkline, SparklineProps, StackedRow, StackedSegment, Stream, StreamDecimate, StreamOffset, StreamProps, StreamSeries, StreamSeriesOptions, StreamSource, Sunburst, SunburstProps, SyncGroup, Text, TextProps, TimeScale, Toolbox, ToolboxOptions, ToolboxProps, Treemap, TreemapDatum, TreemapNode, TreemapProps, TreemapRect, Vec, VisualChannel, VisualMap, VisualMapOptions, VisualMapProps, VisualMode, VisualResult, ZoomConfig, _syncGroupCount, aggregate, angleBand, annotationSummary, arcPath, areaPath, autoLabelStride, bandScale, bin, binValues, bindStream, boxStats, cellPath, decimate$1 as decimate, divergingRamp, download, encode, encodeCategory, extent, fillFor, filter, getSyncGroup, glyphPath, gradientId, isZoomed, leftMarginForLabels, linePath, linearScale, linkPath, logScale, lttb, mapVisual, maxDepth, minmax, nearestIndex, niceTicks, panWindow, partition, patternId, pieceIndex, polarPoint, quantize, radiusScale, rampLightness, rampOf, rampStops, regression, releaseSyncGroup, renderAnnotation, renderAnnotations, resolveColor, resolveLabels, rightMarginForLabels, sankeyLayout, sequentialRamp, serializeSvg, sort, splitDefined, sqrtScale, squarify, stackSeries, streamExtent, streamLayout, sumValue, svgToPngBlob, timeScale, toStackedSeries, useChartSize, useStreamSeries, visualVisible, voronoiCells, voronoiFind, wrapText, zoomWindow };