Accessibility
Charts are keyboard-accessible and screen-reader labeled by default. The plot area, legend, and interactive series are tab stops; the keyboard drives the same tooltip, focus, and filtering as the mouse; and assistive tech hears roles, names, and live value announcements instead of a thicket of unlabeled shapes. It all works out of the box — the accessibility config section tunes it, localizes its labels, or turns it off.
Try it: Tab to the plot area of this chart, press Enter, and step through the categories with the arrow keys. Tab again to reach the legend and filter a series with Enter:
This example's config names its own tab stops — a screen reader announces the plot area as "Weekly signup values" and the legend as "Signup types":
accessibility: {
plotLabel: 'Weekly signup values',
legendLabel: 'Signup types'
}Keyboard map
The plot area is a single tab stop whenever the tooltip or crosshair is enabled:
| Key | Action |
|---|---|
| Enter / Space | open the tooltip; press again to close |
| ← ↑ → ↓ | step the shown category (opens the tooltip if closed) |
| Home / End | jump to the first / last category |
| Esc | close the tooltip |
Reopening returns to the last category you were viewing. On single-category charts (a pie or donut), the arrow keys are inert and Enter/Space still toggles the tooltip.
With both the tooltip and the crosshair disabled, the chart has no keyboard or screen-reader route to its values — the remaining tab stops filter and focus series but never read numbers. If you disable both, provide the values another way, such as a data table or text summary near the chart.
An open tooltip is part of the tab order. With tooltip.showControls on, its ‹ / › / mode controls are ordinary buttons (the ends report aria-disabled instead of dropping out of the tab order), and the tooltip's rows are keyboard-reachable whenever clicking them does something — per the controls' current mode, or the focusCategoryOnClick / focusSeriesOnClick / filterSeriesOnClick config. Like legend items they form a single tab stop with a roving focus: arrows and Home/End move between rows, Enter/Space acts exactly like a click, and a keyboard-focused row highlights the same way a hovered one does. Esc anywhere inside the tooltip closes it and returns focus to the plot area.
Legend items are keyboard-reachable whenever clicking them does something (legend.filterOnClick or legend.focusOnClick). They form a single tab stop with a roving focus: Tab enters the legend, the arrow keys and Home/End move between items, and Enter/Space acts exactly like a click — filtering or focusing the series. A keyboard-focused item highlights its series the same way hovering it does.
Pie and donut slices work the same way when they are interactive (the series has focusOnClick or the chart has an onSliceClick callback): one tab stop, arrow keys moving between slices in config order, and Enter/Space acting as a click at the slice's center.
Cartesian series follow the same pattern when clicking them does something (the series has focusOnClick or the chart has an onSeriesClick callback): one roving tab stop over the series, arrow keys moving between them in config order, and Enter/Space acting as a whole-series click — onSeriesClick reports categoryIndex: -1, as a line or area path click does. Follower series (followSeries) stay pointer-only; their clicks belong to their leader. The interaction guide's callbacks example doubles as a live keyboard demo: Tab to a series, press Enter, and its event log shows the whole-series onSeriesClick payload.
What screen readers hear
The chart svg is announced as a chart (via aria-roledescription) and named from title.text; an untitled chart falls back to accessibility.chartLabel. Decorative geometry — axes, grid lines, series shapes, the crosshair — is aria-hidden, so a screen reader lands on the meaningful stops: the plot area button, the legend, and the tooltip.
Keyboard navigation speaks. Opening or stepping the tooltip announces its content through a visually-hidden live region — "Mon: Trial: 18, Paid: 6" — mirroring exactly what the tooltip shows, including per-series value formatting. Legend items — and tooltip series rows, when clicking them filters — expose their filtered state as a toggle-button aria-pressed (pressed means the series is shown), and interactive pie slices are named with their series title and current share.
The focus ring
The visible keyboard focus ring ships in the optional stylesheet:
import '@mochart/core/mochart.css';It draws a 2px currentColor outline on the focused tab stop, only for keyboard focus (:focus-visible) — mouse clicks stay ring-free — and inset on the plot rect so it stays clear of the axis labels. Without the import, charts fall back to the browser's default focus outline; keyboard access itself works either way.
The ring rules are scoped to a mochart-accessible class that the chart puts on its root element only while accessibility.enabled is true — so a chart with accessibility disabled keeps browser-default outlines on its native controls (the tooltip's buttons, a linked title) even with the stylesheet imported.
Reduced motion
When the user's system requests reduced motion, the chart applies every update instantly instead of animating, and the preference is watched live. This is on by default and controlled by accessibility.respectReducedMotion — see Reduced motion in the animation guide.
Localizing the labels
Every built-in accessibility string is a config key:
const config = {
// ...
accessibility: {
chartLabel: 'Diagramm', // svg name when the title has no text
chartRoleDescription: 'Diagramm',
plotLabel: 'Diagrammwerte', // the plot-area tab stop
legendLabel: 'Legende', // the legend group
tooltipPreviousLabel: 'Vorherige Kategorie', // the tooltip controls' ‹ button
tooltipNextLabel: 'Nächste Kategorie' // … and its › button
}
};Series and category announcements are built from your data and titles, so they need no extra translation. The one visible string in the set is the tooltip controls' mode button, which is chart UI rather than a screen-reader label — its words localize through tooltip.filterModeText and tooltip.focusModeText.
Turning it off
Set accessibility.enabled to false to render the chart with none of the above — no tab stops, key handlers, roles, labels, aria-hidden markers, or live region — for example when the host page provides its own accessible alternative to the chart. Pointer interactions are unaffected, and respectReducedMotion is deliberately not gated by this switch.
Decorative charts
enabled: false still leaves the chart's text content (title, axis and data labels) exposed to screen readers. For a chart that is purely decorative — say a sparkline repeating a value already shown as text — set accessibility.hidden to true instead. The chart's container is marked aria-hidden so assistive tech skips it entirely, and every keyboard tab stop is removed with it, so keyboard users cannot land on content screen readers cannot see. Only do this when the surrounding page already conveys what the chart shows.
Exports
A downloaded SVG is a static image, so exporting removes the interactive semantics — the tab stops and their role, aria-label, aria-expanded, and aria-pressed attributes — and marks the root svg role="img". The chart's own aria-label (its title, or chartLabel) is left in place, so the exported image is still announced by the chart's name.