HyChart
Usage examples on this page are written for Lit / plain HTML (
<hy-chart>). The same component ships asHyChartin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A token-themed, framework-agnostic charting component. Renders SVG directly with modular D3 (math only) inside its own shadow root, themed entirely from --hy-* design tokens.
Chart types
v1 ships a deliberately diverse set off three renderer families:
line— continuous/time scale, path geometry (default)area— filled line (area + line stroke; hover still works)sparkline— chrome-less embedded line (no axes, points, or legend)bar— categorical band scale, grouped rect geometrypie/donut— radial arc layout (the categorical palette ring)
stacked-bar and scatter are in the type union but not drawn yet: the renderer has no stacking math and no points-only branch, so stacked-bar falls through to the grouped bar geometry and scatter to a connected line. Both land per demand — until then, do not offer them to consumers.
Key Features
- Pure
render/core.tsscene math → SSR-safe, deterministic, Lit↔React shared - Series colors from the design system's chart family, or another categorical group via
category-group(the hy-avatar idiom) - Interactive: hover readout + legend show/hide, all on the shared hit-test
- Keyboard: the data is one Tab stop — the arrow keys move between points (bars, slices), Home / End go to the ends, each stop is named
<series>, <category>: <value>(a slice<series>: <value>) and shows its readout while focused, Enter / Space select it, Escape hides the readout - Accessible: a labeled
role="group"of named points (role="img"when there is no data), visually-hidden data-table fallback, HC/forced-colors
When to use
- A trend over time, with
type="line". - A comparison between a few categories, with
type="bar". - The share of a whole in up to five parts, with
type="donut"ortype="pie".
Do
- Name the measure and its unit in the label.
- Keep a line or bar chart to a few series.
When not to use
- Figures people need to read exactly or copy:
hy-table, orhy-datagridfor a large set to sort and filter. - One value moving toward completion:
hy-progress. One reading within a range, such as storage used:hy-meter. - Parts of a whole with more than about five parts:
hy-chart type="bar", not pie or donut. - Stacked bars or a scatter plot: not drawn yet.
stacked-barfalls back to grouped bars andscatterto a connected line, so usebarorline.
Accessibility: your part
- Set
labelto what the chart shows, such as "Revenue by quarter, 2026", not that it is a chart. - Give every series a
label. The legend, the tooltip and the hidden data table read it. - State the main takeaway in text next to the chart, and keep the legend so color is not the only key.
Examples
Basic line chart
<hy-chart type="line" label="Revenue"></hy-chart>
<script>
document.querySelector('hy-chart').data = [
{
id: 'a',
label: 'Series A',
points: [
{ x: 0, y: 10 },
{ x: 1, y: 30 },
],
},
];
</script>Bar chart (categorical x)
<hy-chart type="bar" label="Revenue by quarter"></hy-chart>
<script>
document.querySelector('hy-chart').data = [
{
id: 'rev',
points: [
{ x: 'Q1', y: 10 },
{ x: 'Q2', y: 30 },
{ x: 'Q3', y: 20 },
],
},
];
</script>Donut with the legend beside the plot
The legend-position attribute (bottom default, plus end, start, top, none) controls placement; legend-marker picks the marker shape (square default, or dot). end + dot is the pie/donut convention.
<hy-chart
type="donut"
legend-position="end"
legend-marker="dot"
label="Traffic by source"
></hy-chart>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
type | type | 'line' | 'area' | 'sparkline' | 'bar' | 'stacked-bar' | 'scatter' | 'pie' | 'donut' | 'line' | The chart type: line | area | sparkline | bar | pie | donut in v1. |
data | — | ChartSeries[] | [] | The data series. JS property only (arrays via hyProps), never an attribute. |
options | — | ChartOptions | {} | Curated axis/display options. JS property only. |
categoryGroup | category-group | string | '' | Categorical group whose ramp colors the series (the hy-avatar contract). Unset, the series read the design system's chart family. |
aspectRatio | aspect-ratio | string | '' | SSR dimension source, e.g. 16/9 or 2 (= 2/1) — fixes the viewBox without measurement. |
innerRadius | inner-radius | number | undefined | — | Donut hole as a fraction [0, 1) of the outer radius (pie/donut). 0 = full pie. |
label | label | string | '' | Accessible name for the chart (the label of its role="group" of points, or of role="img" with no data). |
defaultLabel | default-label | string | '{type} chart with {count} series' | The accessible name used while label is unset: {type} is the chart type's name (typeNames, else its keyword), {count} the number of series — a plural choice on it picks the word's form in the locale. Use for internationalization. |
typeNames | — | Partial<Record<ChartType, string>> | {} | The name each chart type is spoken by in defaultLabel's {type}, e.g. { line: 'Linien', donut: 'Ring' }. A type left out is spoken by its keyword (line, donut, …). JS property only. Use for internationalization. |
legendLabel | legend-label | string | 'Series' | Accessible name of the legend's group of series toggles. Use for internationalization. |
tooltipText | tooltip-text | string | '{series}: {value}' | The tooltip's text: {series} is the series label, {value} the hovered value (drawn in the value's weight). Use for internationalization. |
legendPosition | legend-position | 'top' | 'bottom' | 'start' | 'end' | 'none' | 'bottom' | Where the legend sits relative to the plot: bottom (default, stacked under), end / start (beside the plot as a vertical list — the pie/donut convention), top (stacked above), or none (hidden). Layout-only; the attribute drives the placement via CSS. |
legendMarker | legend-marker | 'square' | 'dot' | 'square' | Shape of the legend color marker: square (default, rounded square) or dot (circle — common for pie/donut). Layout-only; the attribute drives the shape via CSS. |
Events
| Event | Detail | Description |
|---|---|---|
hover | — | Pointer over a point, or keyboard focus on one. Detail: { seriesId, datum } | null |
select | — | A point was clicked, or Enter / Space pressed on it. Detail: { seriesId, datum } |
legend-toggle | — | A series was shown/hidden. Detail: { seriesId, hidden } |
CSS Parts
| Part | Description |
|---|---|
base | The wrapper element |
svg | The chart canvas |
axis | An axis group |
plot | The series-marks group |
legend | The series legend |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-chart-series-1-foreground-default-rest | var(--hy-background-chart-1-rest) | Color of the first series (through --hy-chart-series-10-foreground-default-rest). These 10 hooks override per-series color. Unset, series N reads the design system's chart family, --hy-background-chart-N-rest — the colors of the Visualization group picked in Semantics ▸ Color ▸ Chart, cycled, or the default ink when the system has none; charts never borrow brand / feedback role colors. Set category-group to read another group. The series count is NOT capped at 10 — charts with more series cycle back through the palette so every mark still paints. |
--hy-chart-border-subtle-rest | var(--hy-border-subtle-rest) | Gridline color |
--hy-chart-border-default-rest | var(--hy-border-default-rest) | Axis baseline color |
--hy-chart-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Tick/axis label color |
--hy-chart-legend-foreground-default-rest | var(--hy-foreground-default-rest) | Legend entry text color |
--hy-chart-stroke-md | var(--hy-stroke-md) | Series line thickness |