TanStack
Guides

Accessibility

Accessibility is part of the chart contract, not a final annotation pass. TanStack Charts provides named SVG and Canvas surfaces plus shared focus primitives; the application still owns the surrounding explanation, controls, and exact-value alternative.

Name the chart

Every DOM host and framework adapter requires ariaLabel:

tsx
<Chart
  definition={definition}
  ariaLabel="Weekly downloads for three packages"
/>

Use ariaDescription when the reader needs context that is not already visible nearby:

tsx
<Chart
  definition={definition}
  ariaLabel="Weekly downloads for three packages"
  ariaDescription="Values are seven-day totals. Missing weeks are rendered as gaps."
/>

The SVG renderer emits an image role, a chart roledescription, and a <desc> when a description is supplied. The Canvas renderer places the same image role, name, roledescription, description, and tab index on its root while keeping all five canvas descendants aria-hidden. Do not put instructions, conclusions, and all underlying data into one enormous accessible name.

Preserve semantic context outside the surface

A chart should usually be accompanied by:

  • a visible heading;
  • a short statement of what is being compared;
  • units and time range;
  • a source note when relevant;
  • a summary or table when exact values matter.

Use normal HTML for this content. It is easier to navigate, select, translate, and print than text embedded in SVG.

Keyboard focus and selection

Native focus supports pointer and keyboard navigation over chart points. Leave keyboard enabled when a chart is interactive. Focus callbacks expose the same typed ChartPoint data regardless of input method.

If the application replaces native focus with a brush, zoom, editor, or rich overlay:

  • provide semantic buttons, sliders, inputs, or table rows;
  • keep visible focus styles;
  • expose current state in text;
  • support Escape when an interaction can be cancelled;
  • use live announcements sparingly for meaningful committed changes;
  • make touch targets at least 44 CSS pixels where practical.

The Interactions and Selections guide defines the ownership boundary.

interactiveColorLegend uses native browser buttons with aria-pressed, a named group, 44-pixel minimum targets, and stable focus across controlled updates. Static SVG output keeps only a visual fallback, so pair exported charts with text or a table when series visibility must remain actionable.

brushX exposes its ordered handles as horizontal sliders when values is provided. Arrow keys move one authored value, Home and End move to the allowed range edge, and Escape restores the range from the active pointer gesture. Give both handles distinct names and format their semantic values. The DOM overlay works with SVG and Canvas hosts; static SVG and React Native need an application-owned range input for equivalent operation.

continuousCursor paints an unsnapped pointer guide but does not expose that presentation-only overlay as a keyboard task. Pair its controlled x/y position with named sliders or inputs and visible status text. Escape clears a visible cursor through the behavior; the semantic controls should update the same controlled position. Static SVG and React Native retain accepted guide paint but still need those application-owned controls for operation.

handleX exposes one named horizontal slider. Left or Down moves to the previous authored value, Right or Up moves to the next, and Home and End move to the candidate endpoints. Its format result becomes aria-valuetext; the pointer target is 44 pixels high by default. Escape restores the gesture origin while a pointer or touch drag is active. Static SVG and React Native retain the accepted handle paint but need an application-owned semantic input.

zoomX exposes one named plot surface with visible focus. Plus and minus zoom, the arrow keys pan, Home resets, and Escape cancels an active gesture. Wheel input is captured only while that surface is focused; an unfocused chart must not trap page scrolling. Keep a visible Reset button and current-window text bound to the same controlled window. Static SVG and React Native render the accepted window but need application-owned semantic zoom controls.

Never rely on color alone

Pair semantic color with at least one other signal:

  • direct labels;
  • shape or stroke pattern;
  • ordering;
  • position;
  • text in a legend or linked table;
  • selected outlines and aria-pressed state.

Check contrast against the actual application background in both light and dark modes. The theme cannot infer whether an arbitrary data color is accessible.

Motion

The default SVG tween and optional motion() renderer respect prefers-reduced-motion by default. Keep respectReducedMotion: true unless an application has a stronger accessible motion policy. Reduced motion snaps entrance, update, and focus-state changes to final geometry without scheduling animation frames.

Motion should explain continuity between states. It should not:

  • delay access to current values;
  • loop without a pause control;
  • move the focus target away from keyboard users;
  • encode the only evidence of a change.

See Dynamic Data and Animation for the lightweight tween and optional spring renderer boundaries.

Tooltips are supplemental

The native tooltip exposes its structured rows through a polite status region. It is not a replacement for a label, axis, legend, or data table. Essential information must remain available without hovering.

For complex framework content, use the adapter's tooltip-body composition surface. Its transient body is inert, so a display-only nested chart can remain visible but does not enter the focus or accessibility order. Render controls only while pinned is true. The pinned surface has non-modal dialog semantics, Escape and dismiss() close it, and focus returns to the chart when dismissal starts inside the body. Controls still need an intentional tab order and accessible names.

Use tooltip.visibility: 'pinned' when no transient surface should be announced. Focus and pinned mark styling remain available, while the tooltip element and framework body mount only after activation.

Linked table pattern

For analytical and operational charts, a linked table gives readers exact values and a familiar navigation surface. Selection state should be shared semantically, rather than inferred from DOM nodes.

Use keyedSelection and whenSelected for chart activation and selected geometry. Keep the semantic HTML table, status announcement, and clear control in the application, bound to the same controlled selected key.

Testing checklist

Test the finished application, not only the SVG or Canvas element:

  1. Navigate the page and chart with a keyboard.
  2. Confirm visible focus and a logical navigation order.
  3. Operate selection, pinning, zoom, and editing without a pointer.
  4. Verify focus and selection persist correctly after data updates.
  5. Enable reduced motion.
  6. Test light, dark, forced-colors, and increased text size.
  7. Inspect the accessible name and description.
  8. Confirm exact values are available outside hover-only UI.
  9. Confirm status and errors are not encoded only by color.
  10. Use a screen reader on the complete task flow.

Automated checks can catch missing names and invalid roles, but they cannot decide whether the representation communicates the right thing.