Chart
Headless, accessible, SSR-stable chart components built on chart-scale and chart-shape.
|
|
| Package |
@vizejs/ui/chart |
| Maturity |
stable |
| Own the source |
vize lib pull chart |
| Requires |
announcer, chart-scale, chart-shape, context, controllable-state, id |
| Aliases |
chart, line chart, bar chart, pie chart, data visualization, graph |
| Covers |
WAI-ARIA graphics roles, Highcharts accessibility module, Recharts, visx, Unovis |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Chart, ChartRoot, ChartArea, ChartAxis, ChartBars, ChartCrosshair, ChartDataTable, ChartGrid, ChartLegend, ChartLine, ChartPie, ChartPoints, ChartTooltip } from "@vizejs/ui/chart";
Or copy the source into your project with vize lib pull chart (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Line chart with keyboard-navigable points, axes, a tooltip, and a data-table fallback. -->
<script setup lang="ts">
import {
Chart,
ChartAxis,
ChartDataTable,
ChartGrid,
ChartLine,
ChartPoints,
ChartTooltip,
} from "@vizejs/ui/chart";
import type { ChartAxisScale, ChartTableColumn } from "@vizejs/ui/chart";
interface Visits {
readonly month: string;
readonly count: number;
}
const visits: readonly Visits[] = [
{ month: "Jan", count: 1200 },
{ month: "Feb", count: 1850 },
{ month: "Mar", count: 1600 },
{ month: "Apr", count: 2400 },
{ month: "May", count: 2100 },
];
// Plot area of a 480×240 chart with the default margins: 424×192.
const months = visits.map((row) => row.month);
const monthScale: ChartAxisScale<string> = Object.assign(
(month: string) => (months.indexOf(month) * 424) / (months.length - 1),
{ range: [0, 424], ticks: () => [...months] },
);
const countScale: ChartAxisScale<number> = Object.assign(
(count: number) => 192 - (count / 3000) * 192,
{
range: [192, 0],
ticks: () => [0, 1000, 2000, 3000],
},
);
const x = (row: Visits) => monthScale(row.month);
const y = (row: Visits) => countScale(row.count);
const label = (row: Visits) => `${row.month}: ${row.count} visits`;
const columns: readonly ChartTableColumn<Visits>[] = [
{ key: "month", header: "Month", rowHeader: true, value: (row) => row.month },
{ key: "count", header: "Visits", value: (row) => row.count },
];
</script>
<template>
<Chart
title="Monthly visits"
description="Site visits from January to May."
:width="480"
:height="240"
>
<ChartGrid :scale="countScale" />
<ChartAxis :scale="monthScale" />
<ChartAxis :scale="countScale" orientation="left" />
<ChartLine :data="visits" :x :y name="Visits" />
<ChartPoints :data="visits" :x :y :label name="Visits" />
<template #overlay>
<ChartTooltip v-slot="{ datum }" :data="visits">{{ label(datum) }}</ChartTooltip>
</template>
<template #after>
<ChartDataTable :data="visits" :columns caption="Monthly visits" />
</template>
</Chart>
</template>
API
Chart
Source: src/families/charts/chart/chart-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned chart id. null and undefined select a deterministic fallback. |
width |
number |
undefined |
Fixed width in CSS pixels. undefined makes the chart responsive: it renders defaultWidth on the server and then follows its container. |
height |
number |
360 |
Height in CSS pixels. |
defaultWidth |
number |
640 |
Width used for server rendering and before the container is measured. |
margin |
Partial<ChartMargin> |
{ top: 16, right: 16, bottom: 32, left: 40 } |
Space around the plot area for axes. Missing sides keep their defaults. |
title (required) |
string |
required |
Accessible chart title, rendered as the SVG <title>. |
description |
string |
undefined |
Longer accessible summary, rendered as the SVG <desc>. |
hiddenSeries |
readonly string[] |
undefined |
Controlled hidden series names (v-model:hiddenSeries). |
defaultHiddenSeries |
readonly string[] |
[] |
Initially hidden series names for uncontrolled use. |
Events
| Event |
Payload |
Description |
update:hiddenSeries |
[names: readonly string[]] |
Fired with the next hidden series names when the legend toggles a series. |
activeChange |
[point: ChartActivePoint | null, reason: ChartActiveReason] |
Fired when keyboard focus or pointer hover changes the active data point. |
Slots
| Slot |
Slot props |
Description |
default |
ChartSlotState |
SVG chart parts (axes, grids, series, points), rendered inside the plot area. |
overlay |
ChartSlotState |
HTML layered over the chart, such as ChartTooltip. |
after |
ChartSlotState |
HTML after the chart, such as ChartLegend and ChartDataTable. |
Exposed
| Member |
Type |
Description |
active |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
height |
— |
|
innerHeight |
— |
|
innerWidth |
— |
|
margin |
— |
|
setActive |
— |
|
toggleSeries |
— |
|
width |
— |
|
ChartRoot
Source: src/families/charts/chart/chart-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned chart id. null and undefined select a deterministic fallback. |
width |
number |
undefined |
Fixed width in CSS pixels. undefined makes the chart responsive: it renders defaultWidth on the server and then follows its container. |
height |
number |
360 |
Height in CSS pixels. |
defaultWidth |
number |
640 |
Width used for server rendering and before the container is measured. |
margin |
Partial<ChartMargin> |
{ top: 16, right: 16, bottom: 32, left: 40 } |
Space around the plot area for axes. Missing sides keep their defaults. |
title (required) |
string |
required |
Accessible chart title, rendered as the SVG <title>. |
description |
string |
undefined |
Longer accessible summary, rendered as the SVG <desc>. |
hiddenSeries |
readonly string[] |
undefined |
Controlled hidden series names (v-model:hiddenSeries). |
defaultHiddenSeries |
readonly string[] |
[] |
Initially hidden series names for uncontrolled use. |
Events
| Event |
Payload |
Description |
update:hiddenSeries |
[names: readonly string[]] |
Fired with the next hidden series names when the legend toggles a series. |
activeChange |
[point: ChartActivePoint | null, reason: ChartActiveReason] |
Fired when keyboard focus or pointer hover changes the active data point. |
Slots
| Slot |
Slot props |
Description |
default |
ChartSlotState |
SVG chart parts (axes, grids, series, points), rendered inside the plot area. |
overlay |
ChartSlotState |
HTML layered over the chart, such as ChartTooltip. |
after |
ChartSlotState |
HTML after the chart, such as ChartLegend and ChartDataTable. |
Exposed
| Member |
Type |
Description |
width |
number |
Outer SVG width. |
height |
number |
Outer SVG height. |
innerWidth |
number |
Plot area width (width minus horizontal margins). |
innerHeight |
number |
Plot area height (height minus vertical margins). |
margin |
ChartMargin |
Resolved margins. |
element |
HTMLElement | null |
Rendered figure element. |
active |
ChartActivePoint | null |
Active data point. |
setActive |
(point: ChartActivePoint | null) => void |
Set or clear the active data point. |
toggleSeries |
(name: string, hidden?: boolean) => boolean |
Show or hide a series by name. |
ChartArea<T>
Source: src/families/charts/chart/chart-area.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows to draw, in drawing order. |
x (required) |
(datum: T, index: number) => number |
required |
Plot-area x coordinate of a row. |
y (required) |
(datum: T, index: number) => number |
required |
Plot-area y coordinate of the area's top edge. |
y0 |
number | ((datum: T, index: number) => number) |
undefined |
Plot-area y coordinate of the baseline. Defaults to the bottom of the plot area. |
defined |
(datum: T, index: number) => boolean |
undefined |
Rows for which this returns false break the area into segments. |
curve |
ChartCurve |
"linear" |
Curve interpolation. |
name |
string |
undefined |
Series name for legends and data tables. |
ChartAxis<Tick>
Source: src/families/charts/chart/chart-axis.vue
Props
| Prop |
Type |
Default |
Description |
scale (required) |
ChartAxisScale<Tick> |
required |
Scale positioning the ticks, such as a linear, time, or band scale. |
orientation |
ChartAxisOrientation |
"bottom" |
Side of the plot area the axis is drawn on. |
tickCount |
number |
5 |
Approximate number of ticks requested from continuous scales. |
tickValues |
readonly Tick[] |
undefined |
Explicit tick values that replace scale.ticks(). |
format |
(tick: Tick, index: number) => string |
undefined |
Tick label formatter. Defaults to the scale-aware Intl formatter. |
locale |
string |
"en-US" |
Locale of the default formatter. Set it explicitly for server rendering. |
tickSize |
number |
6 |
Length of tick marks in CSS pixels. |
tickPadding |
number |
3 |
Gap between a tick mark and its label. |
label |
string |
undefined |
Axis title rendered beside the ticks. |
Slots
| Slot |
Slot props |
Description |
tick |
ChartAxisTick<Tick> |
Custom tick label content. Receives the tick value, label, and offset. |
ChartBars<T, Category>
Source: src/families/charts/chart/chart-bars.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows to draw as bars. |
category (required) |
(datum: T, index: number) => Category |
required |
Category of a row. |
value (required) |
(datum: T, index: number) => number |
required |
Value of a row. |
categoryScale (required) |
BarCategoryScale<Category> |
required |
Band scale positioning categories. |
valueScale (required) |
(value: number) => number |
required |
Continuous scale positioning values. |
orientation |
BarOrientation |
"vertical" |
Direction bars grow in. |
baseline |
number | ((datum: T, index: number) => number) |
0 |
Value bars start from, or a per-row baseline for stacked bars. |
radius |
number |
0 |
Corner radius applied to every bar. |
label |
(datum: T, index: number) => string |
undefined |
Accessible description of a bar. When provided, bars join keyboard navigation. |
name |
string |
undefined |
Series name for legends, tooltips, and navigation. |
ChartCrosshair
Source: src/families/charts/chart/chart-crosshair.vue
Props
| Prop |
Type |
Default |
Description |
axis |
"both" | "x" | "y" |
"x" |
Which guide lines to draw through the active point. |
series |
string |
undefined |
Only follow this series name. undefined follows any active point. |
ChartDataTable<T>
Source: src/families/charts/chart/chart-data-table.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows of the chart. |
columns (required) |
readonly ChartTableColumn<T>[] |
required |
Columns read from each row. |
caption (required) |
string |
required |
Table caption, usually the chart title. |
visuallyHidden |
boolean |
true |
Keep the table available to assistive technology but visually hidden. Set false to show it, for example behind a "Show data" disclosure. |
locale |
string |
"en-US" |
Locale of the default cell formatter. |
ChartGrid<Tick>
Source: src/families/charts/chart/chart-grid.vue
Props
| Prop |
Type |
Default |
Description |
scale (required) |
ChartAxisScale<Tick> |
required |
Scale positioning the grid lines. |
direction |
"horizontal" | "vertical" |
"horizontal" |
"horizontal" draws lines across the plot for a y scale; "vertical" for an x scale. |
tickCount |
number |
5 |
Approximate number of lines requested from continuous scales. |
tickValues |
readonly Tick[] |
undefined |
Explicit values that replace scale.ticks(). |
ChartLegend
Source: src/families/charts/chart/chart-legend.vue
Props
| Prop |
Type |
Default |
Description |
toggleable |
boolean |
true |
Render each entry as a toggle button that shows or hides its series. |
ariaLabel |
string |
"Legend" |
Accessible name of the legend list. |
Slots
| Slot |
Slot props |
Description |
default |
{ /** Registered series. */ readonly series: ChartSeriesInfo; } |
Entry content, for example a swatch and the series name. |
ChartLine<T>
Source: src/families/charts/chart/chart-line.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows to draw, in drawing order. |
x (required) |
(datum: T, index: number) => number |
required |
Plot-area x coordinate of a row, usually (row) => xScale(row.date). |
y (required) |
(datum: T, index: number) => number |
required |
Plot-area y coordinate of a row. |
defined |
(datum: T, index: number) => boolean |
undefined |
Rows for which this returns false break the line into segments. |
curve |
ChartCurve |
"linear" |
Curve interpolation. |
name |
string |
undefined |
Series name for legends and data tables. Named series can be hidden by the legend. |
ChartPie<T>
Source: src/families/charts/chart/chart-pie.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows to draw as slices. |
value (required) |
(datum: T, index: number) => number |
required |
Slice value of a row. |
label |
(datum: T, index: number) => string |
undefined |
Accessible description of a slice. When provided, slices join keyboard navigation. |
innerRadius |
number |
0 |
Inner radius; greater than zero draws a donut. |
outerRadius |
number |
undefined |
Outer radius. Defaults to half the shorter plot dimension. |
padAngle |
number |
0 |
Padding between slices in radians. |
cornerRadius |
number |
0 |
Rounded slice corners. |
sort |
"descending" | "none" | ((left: T, right: T) => number) |
"descending" |
Slice drawing order. |
name |
string |
undefined |
Series name for legends, tooltips, and navigation. |
Slots
| Slot |
Slot props |
Description |
default |
{ /** Layout of this slice. */ readonly slice: PieSlice<T>; /** Centroid x, relative to the pie center. */ readonly x: number; /** Centroid y, relative to the pie center. */ readonly y: number; /** Whether this slice is active. */ readonly active: boolean; } |
Optional slice label content, positioned at the slice centroid. |
ChartPoints<T>
Source: src/families/charts/chart/chart-points.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows to mark, in keyboard navigation order. |
x (required) |
(datum: T, index: number) => number |
required |
Plot-area x coordinate of a row. |
y (required) |
(datum: T, index: number) => number |
required |
Plot-area y coordinate of a row. |
label (required) |
(datum: T, index: number) => string |
required |
Accessible description of a row, announced when it receives focus. |
name (required) |
string |
required |
Series name, used as the group label and for legends and tooltips. |
radius |
number |
4 |
Point radius in CSS pixels. |
capturePointer |
boolean |
true |
Activate the nearest point while the pointer moves anywhere over the plot area. |
Events
| Event |
Payload |
Description |
select |
[datum: T, index: number, nativeEvent: Event] |
Fired when a point is activated with Enter or Space, or clicked. |
Slots
| Slot |
Slot props |
Description |
default |
{ /** Row of this point. */ readonly datum: T; /** Index of the row. */ readonly index: number; /** Whether this point is active. */ readonly active: boolean; /** Plot-area x coordinate. */ readonly x: number; /** Plot-area y coordinate. */ readonly y: number; } |
Optional decoration drawn inside each point group. Receives the datum and state. |
Source: src/families/charts/chart/chart-tooltip.vue
Props
| Prop |
Type |
Default |
Description |
data (required) |
readonly T[] |
required |
Rows of the series the tooltip describes, so the slot receives a typed datum. |
series |
string |
undefined |
Only show for this series name. undefined follows any active point. |
Slots
| Slot |
Slot props |
Description |
default |
{ /** Active row. */ readonly datum: T; /** Index of the active row. */ readonly index: number; /** Active point geometry and label. */ readonly point: ChartActivePoint; } |
Tooltip content for the active point. |
Behavior
Normative state x input -> outcome table for the @vizejs/ui/chart parts:
chart-root.vue, chart-axis.vue, chart-grid.vue, chart-line.vue,
chart-area.vue, chart-bars.vue, chart-pie.vue, chart-points.vue,
chart-tooltip.vue, chart-crosshair.vue, chart-legend.vue, and
chart-data-table.vue. Every row is proven by the named test in
chart.test.ts or chart-ssr.test.ts; compile-only guarantees live in
chart.types.test-d.ts.
The chart is a <figure> holding an SVG with role="group",
aria-roledescription="chart", and a <title>/<desc> pair. Axes, grids,
lines, and areas are aria-hidden. Data are reachable in two ways:
- Navigable marks.
ChartPoints, and ChartBars/ChartPie when given a
label, render focusable role="img" marks. Roving focus moves between
them, and the chart's polite live region announces each one.
- Data table fallback.
ChartDataTable renders a real <table> with a
caption and column and row headers.
Every part is generic over the row type T, so accessors, tooltip slots, and
table columns infer the row type from data.
| ID |
State |
Input |
Outcome |
Evidence |
| C1 |
default |
render |
labelled SVG group, plot translation, centered band ticks, grid lines, d3-identical line/area paths |
renders a labelled SVG chart with plot geometry, axes, grid, and series paths |
| C2 |
points |
focus, arrows, Home/End, Escape |
one tab stop, focus follows arrows, live announcements, tooltip and crosshair follow the active mark |
points use roving focus, arrow keys, announcements, tooltip, and crosshair |
| C3 |
pointer over the plot |
pointermove, pointerleave |
the nearest point by x activates without focus or announcement and clears on leave |
pointer hover activates the nearest point without moving focus |
| C4 |
legend |
toggle click, controlled prop |
aria-pressed toggles hide series and remove hidden marks from the tab order; controlled waits |
the legend toggles series visibility with pressed buttons |
| C5 |
data table |
render |
captioned, visually hidden table with column and row headers and localized cells |
the data table fallback exposes every row with headers and localized cells |
| C6 |
labelled bars and pie |
arrows, focus |
bars and slices become navigable marks; unlabelled ones stay decorative; one active mark per chart |
bars and pie slices join keyboard navigation when labelled |
| C7 |
responsive root |
ResizeObserver |
renders defaultWidth first, then follows the container width and recomputes the plot size |
responsive charts render the default width and follow ResizeObserver |
| C8 |
time, formatted, slotted axes |
render |
zone-aware Intl time labels, custom formatters, tick slot, orientation transforms |
time axes format ticks in the scale's zone and support custom formats and tick slots |
| C9 |
missing provider |
setup |
parts fail closed with the shared context diagnostic |
chart parts require a ChartRoot provider |
| C10 |
SSR on hosts in other zones |
render twice |
byte-identical markup, closed tooltip, first point as tab stop |
renders byte-identical chart markup across requests and host time zones |
| C11 |
hydration |
mount over server HTML |
every part hydrates without warnings |
hydrates the full chart without mismatch warnings |
No styles ship with the family. Marks expose data-active, data-series,
data-hidden, and data-index. The tooltip exposes
--vize-chart-tooltip-x and --vize-chart-tooltip-y for positioning.