Accessibility
The data table, keyboard navigation per archetype, announcements and forced colours, with the evidence and the known limits.
@workspace/active-charts 6.0.0Status unknowncontent differs from the recorded release 6.0.0baseline ce081dc3
This page is the package's docs/, included
unchanged from the package as it is in Foundation. Links to the package's
internal design documents keep their text.
This document is the support statement of @workspace/: what each chart family provides for people who do not use sight or a pointer, how to configure it, the evidence behind each provision, and the known limits.
Scope of the claim (W6, ADR-035). The package aims at the WCAG 2.2 level AA success criteria that a chart component can meet on its own (text alternatives, info and relationships, keyboard, focus visible, name, role and value, status messages). It makes no conformance or certification claim: a page is conformant only as a whole, and several criteria (headings, colour contrast of the surrounding interface, the host's words) belong to the host. Each provision below states its evidence level, and nothing is claimed beyond it. The earlier "WCAG 2.1 Level AAA" statement and "verified" assistive-technology matrix were withdrawn on 2026-09-25 because no recorded evidence supported them.
What every supported family guarantees (ADR-036): all plotted meaning is available through at least one non-visual path. Cartesian charts give a data table, keyboard stepping with announcements and a tooltip for every item, compound glyphs included; every standalone family gives a data table with its own structure next to its keyboard model; scalar charts give a table and a rich accessible name; the sparkline is decorative (§ 7).
If something in your application doesn't match what's described here, open an issue — accessibility regressions are treated as bugs, not enhancements.
1. What is provided, and at which evidence level#
Evidence levels: unit is a jsdom test in the package suite; browser is the real-browser suite (pnpm --filter @workspace/, Chromium for the full set and Firefox for a subset), without assistive technology; AT is a recorded human run with a screen reader (§ 8). No AT run is recorded yet. The ET-3 qualification protocol (ADR-120, BROWSER- § ET-3 qualification) and its UI Lab fixtures are in place; until ET-3C records human rows and GATE 3 passes, nothing below is qualified on a screen reader, a phone, a real Contrast theme or by a native reader. The dated rows behind every level, per browser, are in EVIDENCE..
| Provision | Where it holds | Evidence |
|---|---|---|
| Accessible name and description on the chart SVG | Every chart family except the sparkline, which is decorative (§ 7) | unit |
Data table (accessibility.) | Every family except the sparkline. Cartesian: one row per displayed datum. Standalone: a table with the family's structure (§ 3). Scalar: value, range, target and zones | unit, browser (named tables, structure, and no page widening at 390 px) |
| Values in the Cartesian table | Every series type: one column per value role for candlestick, OHLC, box plot, range area and range bar; a waterfall's drawn amount, its kind (change, subtotal, total) in words and its running balance (ADR-097); grouped rows with interval and count. Instants are listed with their time | unit, browser |
| Keyboard navigation | Per archetype (§ 2), compound glyphs included (W5) | unit; browser for Cartesian and compound stepping |
| Tooltip | Every Cartesian item, compound glyphs with every role; the standalone families' own tooltips | unit; browser (a real pointer over each compound family) |
| Live announcements | Polite live region for navigation, zoom, legend and context-menu events (the period selector conveys its state with aria-, not an announcement). Compound points announce every role, a waterfall bar its kind, its drawn amount and the balance (a total its balance), legend changes the name the legend shows. The most recent announcement speaks, so a keyboard reader hears zoom, legend, pin and menu events while stepping; every way of opening the context menu (right click, Shift+F10, the context-menu key, a long press) is announced (ADR-052) | unit; browser |
| Item selection and activation (ADR-042, ADR-102) | <Chart>, the thirteen Cartesian wrappers and, since ADR-102, the nine standalone families with selection (pie, funnel, radial bar, nightingale, radar, heatmap, treemap, sunburst, sankey), when the host turns them on. Space selects the focused item and Enter activates it (on a sankey Shift+Enter, because Enter moves between nodes and links); arrow keys move focus only, so focus is never selection. A selected item carries a filled caret beside or inside it (never an outline, since ADR-070), its name and its table cells read "…, selected", the focused item is announced with its selected state, and every change is announced. A hidden, zoomed-out, regrouped or drilled-out item is not presented as selected | unit; browser (pointer, keyboard, touch, table, announcements, forced colours; Chromium and Firefox) |
| Context menu (ADR-087) | One Base UI menu in every family except the Sparkline, the same as the Active Grid's. Opened by right click, Shift+F10, the ContextMenu key, a long press (500 ms) or the optional menu button; a keyboard opening focuses the first item; arrow keys, Home, End and typeahead move; ArrowRight (ArrowLeft in rtl) opens a submenu; Escape closes one level; Tab closes the menu; focus returns to the opener. A disabled item stays focusable and exposes its reason as aria-. Every family announces the opening. A host item receives the frozen context with the element, legend entry or chart the menu was opened on. Under a coarse pointer every item is at least 44 px high | unit; browser (Chromium, Firefox; WebKit in the clean-consumer proof) |
| Linked views (ADR-042) | In a Sync, the keyboard focus of one chart marks the same x on the others (a decorative, aria- mark); focus, announcements and the table stay with the focused chart | unit; browser |
| Focus indicator | 2px solid outline, 2px offset, the --ring token (package default when the host sets none) on the SVG and focusable descendants | unit (styles present); contrast not measured |
| Colour contrast of marks | Every default categorical colour reaches 3:1 against the page and the card, in light and dark (W7, ADR-038); the first six stay distinct under simulated dichromacy, and lines beyond six carry a dash pattern (../) | browser (asserted) |
| Text on marks | A label drawn on a mark takes the on-fill colour that reaches 4.5:1 against it, or moves off the mark or is left out (its value stays in the tooltip and table) | browser (asserted, light and dark) |
| Text size | Floors per role: ticks and in-chart labels 10 px, legend 12 px, hierarchy text 10 px at the initial fit (W8) | browser (asserted at 320, 390, 768, 1440 px) |
| Dense legends | A legend with more entries than fit folds behind a counted "Show N more" button (aria-); every entry stays a named toggle | browser, Chromium and Firefox |
| Reduced motion | Every motion class collapses to zero (entrance, update, exit, grouped slot reflow, tooltip fade and travel, locator departure, attention, colour-key marker, replay, discrete zoom; the motion resolver and the stylesheet), whatever the host configures; no option overrides it (ADR-104); the default entrance is 400 ms with at most 200 ms of stagger otherwise. Dumbbell, Lollipop, Bullet and error bars included (QR-3) | browser, under Chromium's reduced-motion emulation (asserted: nothing moves) |
| Touch (emulated) | A swipe over an unzoomed chart scrolls the page; a pinch zooms a zoom-enabled chart and not the page; a zoomed chart pans under one finger; a long press opens the context menu, which stays open; the navigator brush follows a sideways drag. Not a support claim | browser, touch emulated in Chromium only; no physical device (§ 8) |
| Forced colours | A forced- stylesheet (series strokes and patterns for the first six lines, matching legend keys, focus ring, tooltip; every chart text in Canvas on a Canvas halo, so a label stays readable on a mark that keeps its own colour, whatever the host mode, ADR-108; the unavailable state's sentence too, since CH-69). Asserted on the line, bar, candlestick and pie families and, since ADR-052, on Dumbbell, Lollipop, Bullet, error bars, range, box plot, scatter, area and the standalone families: every text in a system colour, every series with a visible mark; since ADR-100 on the scalars: needle, bar, track markers and targets in Canvas (target and value told apart by form), scale ticks in Gray | browser under Chromium's forced-colors emulation only (asserted and in screenshots) |
2. Keyboard Navigation#
Navigation is archetype-shaped: Cartesian uses a scanning model; Categorical/Hierarchical/Graph/Matrix/Temporal point aria- on the SVG at the focused item (items carry role="img"; there are no listbox or option roles). In all cases the SVG is the single tab-stop.
| Archetype | Charts | Primary keys | Activation | Exit |
|---|---|---|---|---|
| Cartesian | <Chart> (any mix of series), Line, Area, Bar, Scatter, Histogram, Candlestick, OHLC, RangeArea, RangeBar, BoxPlot, Dumbbell, Lollipop, Waterfall (a compound glyph is one item) | ← → ↑ ↓ Home End | Space → select and Enter → activate when the host turns them on (ADR-042), otherwise both open the context menu; Shift+F10 / Context → context menu; + = - _ 0 → zoom | Escape, blur |
| Categorical | Pie/Donut, Funnel, Nightingale, RadialBar, Radar | ← → ↑ ↓ Home End | Enter / Space | Escape, blur |
| Hierarchical | Treemap, Sunburst, OrgChart | ← → ↑ ↓ (siblings), Enter (drill down), Backspace (drill up) | Enter (optionally debounced via confirm) | Escape, blur |
| Graph | Sankey | Enter switches between the nodes and the links (CH-68; Tab leaves the chart), ← → ↑ ↓ step through nodes / links, Home / End jump to first / last item in the current layer | Enter / Space | Escape, blur |
| Matrix | Heatmap | ← → step column within row, ↑ ↓ step row within column, Home / End jump to first / last column of current row (row-local) | Enter / Space → on | Escape, blur |
| Temporal | Timeline | ← → step through events in data order (ascending time with time, ADR-103), ↑ ↓ jump between tracks (multi-track only; cursor lands on the nearest event in the target track), Home / End first / last event globally | Enter / Space → on | Escape, blur |
| Scalar | Gauge, LinearGauge, Bullet | — (no stepping: one value; the name and the data table carry value, range, target and zones) | — | — |
aria- on the SVG advertises the currently available key set to assistive technology, so users can discover the shortcuts without reading the docs.
3. Screen Reader Support#
Three parallel channels broadcast information:
Accessible name —
aria-on the SVG (orlabel <title>where legacy readers insist). Generated bygenerateonAria Label() <Chart>, from the chart type and name on the standalone families and from value and range on the scalar charts; overridable viaaccessibility.(an empty or blank label is no name, CH-10). A scalar's name reads its range ("0 in the range −50 to 50" wherelabel minis not 0; a value beyond an end "150, above the range 0 to 100", without a share, T7), states its target whenever there is one (0 included; under the host's name; "(above the scale)" beyond an end; a share of the target only where it means something), never a tick, a missing categorical value reads "(no value)", and a hierarchy node's DOM id comes from its hostidor its sibling position, never its label; a sankey node's DOM id encodes its hostid(any character, injectively, CH-32), so everyaria-resolves while the host id itself is unchanged.activedescendant Pre-navigation description —
<desc>element wired viaaria-. Default template:describedby "Interactive chart. {type}. {n} items. Press Tab to focus, arrow keys to navigate."(the nav hint is omitted whenkeyboard). Overridable viaNavigation: false accessibility..description aria-announcements — a polite live region under the SVG echoes hover/focus, zoom, legend-toggle and context-menu events. Suppressible per-category vialive accessibility.onannouncements <Chart>and its wrappers.Copy outcome (ADR-116) — a copy from the menu is never silent: its outcome is announced in the chart's live region after "menu closed", so it is the last thing said (
charts., orcontext Menu. copied charts.when the browser rejects the write). A copy the browser cannot run here (no clipboard API, an insecure context) is a disabled item with its reason (context Menu. copy Failed charts., read throughcontext Menu. copy Unavailable aria-), from a pointer and from Shift+F10 alike. A chart that is gone when the write settles announces nothing.description Hierarchy drill and return (ADR-121, CH-67) — on a treemap or a sunburst with
drilldown, a drill into a group announces "Drilled into {name}" and a return announces "Returned to {name}" (charts.,accessibility. drilled Into charts.), once per drill or return that actually happens, from the keyboard (accessibility. returned To Enter,Backspace) and from the pointer (a group, the breadcrumb, the sunburst's centre) alike. The name is the label of the level landed on; the top level without a label reads as the breadcrumb's root word (charts.), another group without one asdrilldown. root charts.. Nothing is said on the first render, foraccessibility. unnamed Group Backspaceat the top level, forEnteron a leaf or for a re-render or a data update. The keyboard focus ends with the old level; the next arrow lands on the first item of the new one and replaces the sentence. Not yet qualified with a person and a screen reader (ET-3C, QJ-15 and QJ-16).
Announcement modes#
On navigable archetypes (Cartesian + Categorical + Hierarchical + Graph + Matrix + Temporal) announcements come in two verbosities:
terse(default):"{series— minimal, fast. Matrix readsName}: {x Value}, {y Value}" "{col(tooltip-parity); sparse cells fall back toLabel} × {row Label}: {value}" "(no value)", and the keyboard reaches every position of the matrix, a cell with data or not, through one focus node (ADR-117). Temporal reads"{label}, {date}".verbose: adds position context —"{series. Matrix appendsName}: {x Value}, {y Value}, point {index+1} of {total}" ", cell N of M"using a 1D flat count (row * cols + col + 1ofrows * cols). Temporal appends", {category}"when the focused event has a category (no suffix on single-track timelines).
Dates in announcements are formatted through @workspace/ using the chart's resolved locale, so announcements match the surrounding UI language.
Data tables#
accessibility. (default on) renders a visually hidden table next to the chart.
Cartesian families list one row per displayed datum (W4, ADR-034). A reduced or grouped table is described (
aria-) as showing N of M rows, or as summarising intervals; a grouped row reads its x cell as the interval and has a column with its number of source rows. A compound series has one column per value role, headed "{series}: {role}"; a waterfall has its step and its running total. A cell whose host row carries no value reads "(no value)", as the tooltip says it, never a blank (ADR-111). The CSV export, not the table, carries every raw row.describedby Standalone families keep their structure (ADR-036): pie, donut and nightingale list category, value and share; funnel and radial bar category and value; radar one row per variable with a column per series; the heatmap is a matrix of row and column labels while that is no larger than the list of its present cells, else that list (row, column, value, in the displayed order) with a summary that counts the cells listed out of the plane and is the table's description; an absent cell is "(no value)" in the matrix and no row in the list, never 0 (ADR-117); treemap and sunburst list every node's path from the shown root with its summed value, and the tooltip and the announcement read that same value, "(no value)" for a node without one, never 0 (ADR-111); sankey lists source, target and flow; the org chart lists name, parent and title; the timeline lists event, the host's date text and track; gauge, linear gauge and bullet list value, range, target and zones. The caption is the chart's accessible name. Every data table, Cartesian and standalone, sits in a clipping container, so it cannot widen a narrow page (PH1-03, closed by ADR-038).
Cell values follow the same presentation as the visible chart. Numbers and dates run through the central formatters of @workspace/ with the chart's own locale input, so a reader hears the decimal separator, grouping and date order that the axis shows. This also holds for the per-series custom formatters that a few modes install (error bars): they receive the presentation as a third argument, so assistive output never disagrees with the visible axis under a non-default notation. A percentage stack reads its shares in percentage points through the same semantic formatting (W9); a hidden member of a stack has no share and an empty cell. A chart whose input breaks its contract is one role="img" element named by the host's charts. text (W12); a supported shape beyond a structural limit (a treemap, sunburst or org chart deeper than 139 levels, ADR-115, ADR-119) is the same element named by charts., so a reader and a support desk can tell "this data is wrong" from "this data is too large to show here". Picture and text alternative are present together or absent together: an unavailable chart has no data table.
4. Configuration#
Every chart accepts an accessibility prop of shape:
interface AccessibilityOptions { label?: string; // overrides aria-label description?: string; // overrides <desc> dataTable?: boolean | AccessibilityDataTableOptions; // default: true announcements?: boolean | AccessibilityAnnouncementOptions; // default: true keyboardNavigation?: boolean | AccessibilityKeyboardNavigationOptions; // default: true}
interface AccessibilityDataTableOptions { enabled?: boolean; caption?: string; xHeader?: string;}
interface AccessibilityKeyboardNavigationOptions { enabled?: boolean; announcementMode?: 'terse' | 'verbose'; wrapAround?: boolean; confirmDrillDown?: boolean; // hierarchical only}
interface AccessibilityAnnouncementOptions { enabled?: boolean; suppress?: ChartAnnouncementCategory[]; // 'zoom' | 'legend' | 'tooltip' | 'contextMenu' | 'period' | 'selection'}Example:
<LineChart data={data} xKey="date" yKey="value" xAxis={{temporalSemantic: 'calendarDate'}} height={300} accessibility={{ label: 'Monthly NAV, last 12 months', description: 'Line chart. 12 data points. Use arrow keys to inspect.', keyboardNavigation: {announcementMode: 'verbose'}, announcements: {suppress: ['zoom']}, }}/>Propagation via <Chart Defaults>#
Place shared a11y defaults on a provider:
<ChartDefaults accessibility={{keyboardNavigation: {announcementMode: 'verbose'}}}> <LineChart {...} /> <BarChart {...} /></ChartDefaults>Per-chart props always win. Merging is per-subfield (label, description, data, announcements, keyboard); a prop that supplies only one subfield inherits the rest from the context.
5. Defaults vs. overrides — quick matrix#
| Subfield | Default | Consumer opt-out | Consumer override |
|---|---|---|---|
label | Derived from chart type + series names | none: an empty or blank label falls back to the derived name on every family (CH-10, ADR-093); a Sparkline without a name is decorative | accessibility={{label: 'Custom'}} |
description | build template | — (always present) | accessibility={{description: 'Custom'}} |
data | Enabled, caption and x-header derived | accessibility={{data | accessibility={{data |
announcements | All categories enabled | accessibility={{announcements: false}} | accessibility={{announcements: {suppress: ['zoom']}}} |
keyboard | Enabled, terse, wrap | accessibility={{keyboard | accessibility={{keyboard |
Uncertainty and zones (ADR-096)#
Error bars are stated, never only drawn: the tooltip, the data table and the keyboard announcement read
value (low–high)(or the named lower or upper bound), in the series' notation; a scatter's x bounds add a table column and follow the x in the announcement. A whisker is no pointer or keyboard stop of its own.Named zones are read in the tooltip, in one table column per series with named zones and in the announcement; an unnamed zone is presentation and adds no text. No colour name is ever announced.
Soft bounds are presentation only: nothing is announced.
Hierarchy and flow (ADR-099)#
Label policy. A treemap's or sunburst's
label.andlevels label.change only what is drawn. Every node keeps its accessible name, its row in the data table, its tooltip and its announcement whatever the policy hides.secondary Treemap nested groups. Every internal node now has a drawn element behind its keyboard focus (before ADR-099 a nested group's
aria-pointed at nothing).activedescendant One measure for sight and text (ADR-112). A treemap's or sunburst's areas, arcs and shares are drawn from the same resolved measure the table, the tooltip, the keyboard reading, the announcement, Copy value and the CSV state, so a sighted reader and a screen-reader user get the same proportions. A parent value that contradicts its children is no longer drawn one way and read another: the chart shows its translated unavailable state (W12).
Heatmap coordinates (ADR-113). Each distinct coordinate (by item key:
1and"1"are two) is its own row or column for the keyboard, the table, selection and the announcement; two typed coordinates no longer collapse into one position with one of them unreachable. Two rows on one cell show the unavailable state.Sankey keyboard. ArrowLeft and ArrowRight step the nodes column by column as drawn, top to bottom; ArrowUp and ArrowDown stay in the drawn column, also under
nodeandAlignment node. The data table keeps one row per link in the order ofOrder links. Colour (link) is never the only carrier: the tooltip, the table and the announcement name both ends.Color Org chart collapse. With
collapsible, a card with reports is arole="button"witharia-; its name adds "collapsed, 3 hidden" or "expanded". Enter and Space toggle it; Shift+F10 and the ContextMenu key open its menu (Collapse, Expand, Expand all below). The keyboard walks the visible cards only. A toggle is announced through the chart's live region; when the host collapses an ancestor of the focused card, the focus moves to that ancestor and is announced. The data table lists the whole organisation, collapsed or not; an export's caption says what is collapsed.expanded Org card fields. Every
cardvalue is in the tooltip and has its own table column, whatever the card can draw.Fields Org keyboard menu (ADR-107). Shift+F10 and the ContextMenu key open the focused card's menu on every card, collapsible or not; Enter opens it on a card without collapse.
Org zoom presets (ADR-107). The zoom level is a menu button (
aria-,haspopup="menu" aria-,expanded aria-) whose name starts with the level it shows. Opening it, by click, Enter, Space or an arrow key, moves focus into the menu: to the checked preset, or to the first or last item. The arrows, Home and End move between the presets (controls menuitemradio,aria-at the current level) and Fit; Escape, Tab and a choice close the menu and return focus to the level. The toolbar keeps full strength while it holds focus, so a focused control is never dimmed; its hover changes stop under reduced motion and withchecked animation={false}(ADR-107).No room (RS3, ADR-107). Below its room a family draws no marks and says so in a caption and in its description; the name, the data table and a scalar's readout stay, so nothing it holds is lost to a reader. Without marks it has no keyboard item model either (CH-63): it is one image, not a tab stop, no arrow key steps or speaks and
aria-never appears; the table is the route to its values. When the room returns, the keyboard model returns with no focused position. Heatmap, radar, timeline and radial bar behave so since ET-3B; the sankey already did.activedescendant Sankey under forced colours. Nodes and flows keep their authored hues, as every categorical identity fill of the standalone families does (pie, treemap, sunburst); a flow's colour (source, target or a gradient between them) is node identity, never a scale, so nothing it says becomes false. Recede is off, every flow is drawn at full opacity, labels take
Canvasand the keyboard flow theText Highlightedge; source, target and value stay in the name, the tooltip and the table.
Quantitative scales (ADR-098)#
A colour measure is stated, never only painted: the treemap and sunburst tooltip, the data table (a column headed by the measure's name) and the keyboard announcement say "Funding ratio: 131" (with the class of a classed scale, "131, Covered"), or "(no value)"; the description names the measure and its scale. No colour name is announced.
A bubble's size is stated: the tooltip row is labelled with
size, the data table has a size column per bubble series, a missing size reads "(no value)". The size key is one image named in words ("Bubble size: Assets, from 0 to 1000.", withName ≤/≥sentences where values reach beyond), never a control or a focus stop.The heatmap's rows and columns are named as the gutters write them (formatter or date notation) in the tooltip, the table and the announcement, in the one displayed order the keyboard follows.
Forced colours (Chromium and Firefox emulation): the gradient bar collapses with the cells (CH-27 extended to the ramp), measure fills collapse to
Canvaswith aCanvasedge, and the size key keeps its circles inText Canvas(size needs no colour). No system-colour gradient is invented.Text
Waterfall kinds (ADR-097)#
A subtotal and a total are named, never only painted: the tooltip, the data table (a kind column,
charts.) and the keyboard announcement say "Subtotal" or "Total" with the amount the bar draws, and the balance. The word comes from the row's declared kind, never from its position, so zooming never renames a summary.accessibility. step Kind A subtotal and a total share one paint (and one forced colour,
Gray); a subtotal floats and a total stands on zero, and the words carry the difference. Every summary row is a pointer and keyboard stop, whatever its own value field holds; a change without a value is none and its table amount is empty.Text
Family singles (ADR-103)#
Histogram. The bin is named as its interval ("30 to under 40", the last bin "60 to 70 inclusive") and its amount, in every mode. With
areathe tooltip adds the drawn rate in a row of its own ("Per 10"); the label, the announcement and the table keep the count or sum, so the rate never reads as the count. The table lists the raw rows.Proportional Funnel. A context-aware formatter changes the value text where the formatter already reached: the label, the table's value cell and the announcement, one text everywhere. The share column, the tooltip (its own
value) and the CSV are unchanged.Formatter Timeline.
datestays the words a reader hears; machinetimedecides the position and, withtime, the reading order (above). No new focus stop.Axis Candlestick. The body border is drawing only; the OHLC values, the table and the announcement are unchanged. Forced colours keep a rising (hollow) body's border at
body.Stroke Width: 0 Bar.
inside-andend inside-move a label within its bar; a hidden label's value stays in the tooltip, the table and the announcement.base Sparkline.
domainchanges geometry only: the host's accessible name and the CSV keep every value, nothing is clamped in text.
Standalone activation and selection (ADR-102)#
Off by default. Without
selectionandonno standalone family changes a key, a click or a word: Enter and Space keep opening the menu (pie, funnel, radial bar, nightingale, radar, a treemap or sunburst leaf), drilling (a parent) or moving between nodes and links (sankey); the heatmap keeps them unused.Item Activate The keys. With selection on, Space toggles the current item; with a handler, Enter activates it (Shift+Enter on a sankey). Shift+F10 and the ContextMenu key open the menu on the current item, with or without selection and activation (on every standalone family with a keyboard item, the timeline included).
aria-names only the keys that apply; no tab stop is added.keyshortcuts The radar's vertex. With selection or activation on, the keyboard is on one vertex: the arrows step the variables and PageUp / PageDown the visible series at the current one. The announcement and the item's name name that one vertex ("Plan B, Risk: 7"); without the props the keyboard stays on the variable and names every series (T8-X4).
What a reader hears. No
aria-(every item isselected role="img"): a selected drawn item's name ends in ", selected", the current item is announced with that state, and every change speaks once ("Selected: …", "Deselected: …") in the chart's one live region, where the most recent source speaks. The data table reads ", selected" on the cells of a selected drawn item (a sankey node has no row of its own: the table lists flows).A request is not a fact (ADR-105). On a chart whose host holds the selection (
selected) or the collapsed cards (Items collapsed), a reader's change is a request. Nothing of it is drawn, listed, read as selected or announced until the host passes it back; the announcement then comes on the commit that applies it, once. A request the host ignores stays silent, and the next one is computed from the host's state again. The host decides when; the chart decides what is said.Ids Not drawn is not selected. A slice the legend hides, a hidden radar series, a missing radar value and a leaf outside the drilled subtree are not named, listed or counted as selected; they stay in the selection and return when drawn.
Leaves and parents. On a treemap and a sunburst only a leaf takes Space and Enter as selection and activation; a parent keeps drilling (with
confirmas before).Drill Down Forced colours (Chromium and Firefox emulation): every family's caret takes
HighlightoverCanvas; no family palette was added.Touch. A tap selects and activates like a click;
multipleneeds no modifier; a long press opens the menu and selects nothing.
Host composition (ADR-105)#
A host legend. A legend a host renders with
useacts through the chart's own legend route: every hide, show, isolate and Show all is announced in the chart's live region with the entry's label, the last visible target cannot be hidden, and a hidden target leaves the plot, the table and the keyboard order, exactly as with the built-in legend. The host owns its legend's DOM: roles (buttons withChart Legend() aria-, or a checkbox list), names, disabled states (frompressed entry.), focus order and keys. The package injects no ARIA into host markup and moves no focus.available Only applied truth is announced. A controlled selection or collapse is announced when the host applies it, never at the request (§ Standalone activation and selection).
The input changes no meaning.
pointer,touchandkeyboardname how a change started; a selection, an activation, a legend action and a menu action mean the same, are drawn the same and are announced the same whichever input made them.
Compound grouping and orientation (ADR-101)#
Keyboard order. Grouped range bars, boxes and dumbbells are stepped category by category, then series by series in series order, then (dumbbell) low before high; the stable
(datumsort decides, never the drawing order. A horizontal chart steps in the same order. One stop per range bar, box and range-area row, two per dumbbell row; grouping adds none.Index, series Index) Identity. A grouped glyph names its series in the tooltip, the announcement and the data table; several dumbbell series name the series before the end ("Plan: 2024"); one dumbbell series keeps its end names.
Orientation is geometry only. A horizontal box plot reads min, Q1, median, Q3, max in the same order and words as an upright one; a reversed axis mirrors the drawing and never a role.
Hidden series. A hidden series has no stop, no pointer target and no domain share; the others close its gap.
Forced colours (Chromium and Firefox emulation): grouped range bars keep the per-series system colours of CH-28 and their position; boxes, dumbbell markers and range-area edges stay visible; both keys of a paired dumbbell legend entry stay visible. No category palette was added.
6. Reduced motion, forced colors, and print#
@media (prefers-— the motion resolver sets every class to zero (ADR-104), so no scripted motion starts and every interaction duration the stylesheet reads isreduced- motion: reduce) 0ms; a defense-in-depth rule in the package stylesheet, which the package entry loads, drops every interaction transition as well. Asserted in Chromium and Firefox per class: no running animation and no in-between frame for an entrance, an update, a hide or show with its reflow, a replay, a tooltip, attention and the colour-key marker; everything still arrives.Motion never carries meaning (ADR-104). The current item, the tooltip's content, its accessible text,
aria-, the keyboard cue, the selection and every live-region announcement change in the commit of the event (for a controlled selection, the commit in which the host applies the request), never after a transition; a gauge replay leaves the accessible name and the table at the resting value; a grouped reflow keeps every series and item identity. The tooltip's fade and travel and the attention timing are presentation only, andactivedescendant animation: falseturns them off without losing any of it.@media (forced-— series strokes switch to system colours with a dash pattern for the first six line series (the legend keys follow), every keyboard cue and the selection mark tocolors: active) Highlight(on aCanvasunderlay), the current mark takes aHighlightedge (the only data-mark edge W-FV-1 allows; ADR-070), the tooltip background toCanvas. Evidence: assertions and screenshots under Chromium's forced-colors emulation; no run in Windows High Contrast mode is recorded.@media print— the chart prints as drawn, on its own mode's background, with its colours exact (ADR-108): every on-mark label keeps the 4.5:1 it was given against its surface, in light and dark. Interaction overlays (tooltip, crosshair, navigator, transient marks) are not printed and nothing prints dimmed. A host that wants a light print prints in the light mode. Opt out via a higher-specificity stylesheet.
7. Known limitations#
Forced colours and bars (closed by CH-26, ADR-093): bars, histogram bins and waterfall steps take system colours under forced colours (the first five bar series
Canvas,Text Link,Text Highlight,Button,Text Gray; a waterfall increaseText Canvaswith an edge, a decreaseCanvas, a subtotal and a totalText Gray), and a bar's legend key takes its bar's colour. A bar series beyond the fifth repeatsText Canvas. Since CH-28 a rising candle is hollow (Text Canvaswith aCanvasedge) and a falling one filled (Text Canvas), with solidText Canvaswicks, and range bars take the bar series colours, their legend keys too.Text Heatmap classes under forced colours (CH-27, Walt 2026-10-05): the authored class colours collapse with the cells and their legend keys; the class stays a name in the legend, the tooltip, the table and the announcement, and the marker under the current cell's class shows it without colour. A missing cell keeps no edge. A cell does not show its class in text.
Scalar archetype (Gauge, LinearGauge, Bullet): one focusable value, no arrow-key stepping (W6: stepping a single value has no item to reach). The rich
aria-(label compute) and the data table carry the value, range, target and zones. Ticks are drawing only: never in the name or the table (T7). A gauge'sScalar Aria Label replay(T7) adds no tab stop and no key: it is a hidden pointer and touch extra at the pivot, and its keyboard route is the chart menu (Shift+F10 › Replay animation) where the host keeps the item; it changes no reading, announces nothing, and under reduced motion the item is disabled with its reason.Sparkline: decorative by default (W6) and then hidden from assistive technology (
aria-); not focusable, no table and no announcements. When it carries meaning, the host names it withhidden accessibility={{label}}, which makes it one image with that name (role="img", T0), or states the meaning in the surrounding text; the package does not invent a stepping model for it.Temporal data-order — without
timethe keyboard cursor steps throughAxis datain the exact order the consumer supplies; a timeline with chronologically out-of-order events steps in that order, so sortdataby date when chronological traversal is required. Withtime(T9, ADR-103) the keyboard, the data table and the position in the announcement follow ascending machineAxis time, equal times in the host's order, and the drawn left-to-right order is the reading order; the CSV keeps the host's order.Temporal pan via keyboard — mouse pan (drag) is not available via keyboard. Timelines wider than the viewport scroll via the focus-follows pattern (the focus indicator indirectly indicates which event is active), but the visual pan-offset is unchanged by keyboard navigation.
Heatmap headers with the same text (ADR-113) — two distinct coordinates can read the same header (the number
1and the text"1"both read "1"), so a matrix reader navigating by header text hears two equal headers. The package invents no disambiguating words (that would put text in the host's data); the remedy is the axisformatter, which receives the raw value and can tell the two apart. A candidate for ET-3 qualification.Sparse heatmap table form (ADR-117) — the data table switches between the matrix and the list of present cells by a structural rule; the summary before the list says which shape the reader is in. Both forms, the switch and a "find the value at a row and column" task are ET-3 qualification items (no human run yet).
Deep hierarchies (ADR-115) — no depth throws. Treemap, sunburst and the org chart deeper than their provisional boundary of 139 levels (ADR-115, ADR-119) show the workload-unavailable state instead of a drawing and a table; keyboard navigation that deep is not a usable journey (no claim). ET-3 qualifies the workload-unavailable wording; ET-4 measures the boundary.
Matrix sort-stability — the keyboard cursor tracks
(row, col)coordinates intounique/X unique, which are derived fromY datainsertion-order. Re-sortingdatawith the same unique labels moves the cursor to whichever datum now occupies that coord — same semantics as Categorical's index-based stability. Consumers who need focus to follow a specific datum across sorts should persist the focused label themselves and re-seek after the sort.Cartesian pre-navigation
<desc>— updates on data length changes; screen readers that cached the description on first focus may need a refocus to re-read.Cartesian focus model — Cartesian charts use a physical focus + hover-pipe model: the SVG is the single tab-stop and arrow keys move a virtual cursor whose position is reflected by the standard hover highlight. Unlike the archetype hooks, Cartesian therefore intentionally emits no
aria-; per-point ids are not generated. The hook still returnsactivedescendant containerfor shape-parity but itsProps aria-is always undefined.activedescendant Several dumbbell series (corrected by ADR-101): the earlier reason for "single-series only" (that the Cartesian sort
(datumwould interleave low/high pairs across series) was wrong: the sort is stable and the plug-in emits each series' low before its high, so the order is category → series → low → high. The real gap was identity, closed by ADR-101: with several dumbbell series every end is read with its series first ("Plan: 2024") in the tooltip, the announcement and the table, and the legend keys each series once.Index, series Index) DumbbellChart voronoi emits 2N flatpoints — for
Nrows the voronoi pipeline emits2flatpoints (low first, then high, per row). Keyboard navigation therefore steps low → high → next-low → next-high. This is the only Cartesian series-type that emits multiple flatpoints per row; consumers using a customN tooltip.should expect each endpoint to surface as its own hover event.renderer Selection on the standalone families (ADR-102) — the org chart, the timeline, the gauges, the linear gauge, the bullet and the sparkline have no item selection; their Enter and Space keep their own meaning (the org chart's collapse among them). On a treemap and a sunburst only leaves are items.
The Sparkline has no context menu (ADR-087): it has no focusable item and lives in a cell or a sentence, where a menu would compete with the host's own. Its handle exports.
Areas under forced colours are drawn as outlines: the fill takes
Canvas, the edge a system colour.Selection is not linked — charts in a
Syncshare positions and ranges, not selections; a host that wants two charts to show the same selection passes both the sameGroup selected.Items ARIA 1.3
aria-— emitted for future-proofing but not yet consistently surfaced by NVDA/JAWS. Thedescription aria-+describedby <desc>pair is the primary channel.
8. Browser & assistive-technology evidence#
Supported pairs (W6): NVDA with Chrome or Edge on Windows, and VoiceOver with Safari on macOS. The manual procedure is BROWSER-. Human evidence is recorded there with date and versions, never inferred from ARIA or from tests.
| Pair | Status (2026-09-29) |
|---|---|
| NVDA + Chrome or Edge | Not run. The build environment has no NVDA installation and no human tester. |
| VoiceOver + Safari | Not run. VoiceOver needs macOS; the build environment is Windows. |
Physical phones and Windows High Contrast are not run either. The qualification that will change this table is ET-3 (ADR-120): H1 NVDA with Chrome in full, H2 NVDA with Edge as a mandatory smoke, H3 VoiceOver with Safari in full, HC1 real Contrast themes, P1 a physical iPhone with iOS Safari, N1 and N2 native Arabic and Hebrew readers. Its automated prerequisites (ET-3B, Chromium, Firefox and a WebKit screen) are screening, not qualification. What does exist is automated browser evidence without assistive technology (Chromium 153 for the full suite, Firefox 155 for a subset, and a one-off WebKit 26.6 run of a subset, environment E3, which is not Safari), recorded per row in EVIDENCE.: roles and accessible names, data-table contents for every family, keyboard stepping and announcements for the Cartesian and compound families, a real pointer over each compound glyph, no page widening by the standalone tables, and the zoomed-waterfall and instant cases; since ADR-042 also item selection by pointer and keyboard, the selected state in the table and in announcements, the selection mark under forced colours (a mark beside the item in Highlight, never an outline, W-FV-1, ADR-070), and linked marks in a Sync; since ADR-052 also the context menu by keyboard, pointer and long press, reduced motion and forced colours on the remaining wrappers, the horizontal percentage table, and emulated touch. Until the human runs are recorded, this statement is limited to that evidence.
References#
WAI-ARIA 1.2 Authoring Practices — Managing focus with
aria-(the technique of the archetype hooks)activedescendant @workspace/formatters — used for locale-aware announcement valuesactive- i18n