A monitor showing an accessible data visualisation with a focused bar and a screen-reader announcement of its values — the visual marker for accessible data-viz tooling.
Image description: A monitor showing an accessible data visualisation with a focused bar and a screen-reader announcement of its values — the visual marker for accessible data-viz tooling.

Engineering primer · A11y data-viz stack

Accessible data-visualisation tooling in 2026: a working stack

An engineering primer scoring Vega-Lite, Plotly, Observable Plot, Apache ECharts, and D3 against accessibility defaults — SVG/ARIA, colour-blind palettes, keyboard-navigable data points, screen-reader hierarchy, and alternative table view — with concrete picks by use case.

Accessible data-visualisation tooling in 2026:
a working stack

Five libraries dominate the modern data-visualisation conversation, but only some of them treat a screen-reader as a first-class consumer. This is a working engineer’s score-sheet, written for teams shipping charts to production in 2026.

5
libraries scored
5
a11y axes evaluated
approx. 8%
of the population has CVD
10 min read
Updated May 2026

1. The five axes that decide whether a chart is accessible

The phrase “accessible chart” hides a stack of quietly different requirements. A bar chart can be rendered as SVG with perfect colour contrast and still be unreachable to a keyboard user. It can be keyboard-navigable and still announce nothing useful to a screen reader. It can announce values cleanly and still drop a sighted low-vision user at the first tooltip. To compare libraries fairly, we evaluate each one against five independent axes that map directly onto how a real assistive-technology user experiences a visualisation.

These five axes are not a personal preference list. They are the practical translation of WCAG 2.2 success criteria (1.4.11 non-text contrast, 2.1.1 keyboard, 4.1.2 name role value), the ARIA Authoring Practices guidance on charts and graphs, and the W3C Research Questions Task Force draft “Data Visualization Accessibility” note that has been circulating since 2023. Every charting library produces SVG; every library renders some kind of legend; every library has some opinion about colour. What separates them is the defaults — the chart you get when you write the smallest sensible amount of code.

approx. 8%
of men have a form of red-green colour-vision deficiency — the default reason categorical palettes need to be CVD-safe out of the box (NIH).
2.1.1
WCAG success criterion: all functionality, including chart inspection, must be operable through a keyboard interface alone.
4.1.2
WCAG: every interactive control — including each data point a user can focus — must expose name, role, and value to assistive technology.
The five axes

1. SVG with semantic ARIA. Does the library output SVG (not canvas), and does that SVG carry meaningful roles, labels, and group structure rather than anonymous <g> nests?

2. Colour-blind-safe palettes by default. Are the categorical and sequential palettes CVD-tested out of the box, or do you have to know to override them?

3. Keyboard-navigable data points. Can a sighted keyboard-only user tab into the chart, arrow between marks, and read each mark’s value?

4. Screen-reader description hierarchy. Is there a title, a one-sentence summary, and per-series / per-point announcements — not just a single alt-text dump?

5. Alternative table view. Is the underlying data available as an HTML table linked from, or rendered next to, the chart for users who prefer tabular consumption?

”A chart that ships with perfect contrast and a colour-blind-safe palette but no keyboard model is a chart you have rendered for half your audience.”

— Disability World engineering desk

2. The five libraries on the table

Five libraries cover the overwhelming majority of new charting work in 2026: Vega-Lite, Plotly, Observable Plot, Apache ECharts, and D3 with custom code. They occupy different points on the abstraction axis — Vega-Lite is the most declarative, D3 is the most imperative — and each one carries a different posture toward accessibility. We treat D3 separately because “D3 + custom” is a fundamentally different engineering proposition: the accessibility you get is the accessibility you write.

None of these libraries is hostile to accessibility. All of them produce SVG (Plotly and ECharts can also emit canvas; we evaluate the SVG mode). All of them accept arbitrary colour palettes. The question is what you get when you write the smallest sensible amount of code, and how much rewiring it takes to get from that default to a chart that actually clears WCAG 2.2 AA.

Vega-Lite
Declarative grammar of interactive graphics (UW)
Strong fit for dashboards and analyst-authored charts
OutputSVG (canvas optional)
A11y defaults
Plotly.js
Open-source charting toolkit (Plotly Inc.)
Strong fit for scientific and BI-style dashboards
OutputSVG (canvas for WebGL traces)
A11y defaults
Observable Plot
Concise mark-based API (Observable / Mike Bostock)
Strong fit for editorial and exploratory charts
OutputSVG
A11y defaults
Apache ECharts
Enterprise-grade charting (Apache Software Foundation)
Strong fit for high-density operational dashboards
OutputCanvas (SVG renderer available)
A11y defaults
D3 + custom
Low-level data-binding primitives (Mike Bostock)
Strong fit for bespoke editorial and product charts
OutputSVG (whatever you write)
A11y defaults
D3 caveat

The “zero dots” rating for D3 is not a knock on the library — it is the honest description of what you get from a vanilla D3 build. D3 is primitives. Accessibility in a D3 chart is whatever the author writes. A D3 chart authored by an engineer who knows ARIA can be the most accessible chart on the page; a D3 chart authored without that knowledge is almost always the least accessible chart on the page.


3. The scoring matrix: library by accessibility feature

The five axes from section one, scored against the five libraries from section two. “Yes” means the default behaviour clears the axis; “Partial” means the library exposes the right hooks but does not turn them on by default; “Manual” means the engineer has to write the relevant code from scratch.

Vega-LitePlotly.jsObservable PlotApache EChartsD3 + custom
SVG output with semantic ARIAYes (SVG, titled groups)Yes (SVG, ARIA labels)Yes (SVG, mark roles)Partial (canvas default; SVG renderer opt-in)Manual
Colour-blind-safe palettes by defaultYes (Tableau 10 + viridis)Partial (Plotly default; CVD palette opt-in)Yes (Observable categorical10)Partial (default scheme not CVD-tested)Manual
Keyboard-navigable data pointsPartial (focus on legend; marks need config)Yes (arrow-key navigation in 2.x)Partial (tip plugin gives focus; marks manual)Partial (a11y module opt-in)Manual
Screen-reader description hierarchyYes (description spec property)Partial (single title; per-point opt-in)Yes (ariaLabel + ariaDescription marks)Partial (a11y module emits per-series alt)Manual
Alternative table viewPartial (data table easy to render)Partial (export to CSV; no in-DOM table)Partial (data() helper, no auto table)Partial (toolbox supports data view)Manual
How to read the matrix

Vega-Lite and Observable Plot lead on declarative defaults. Plotly leads on built-in keyboard navigation. ECharts has the most thorough opt-in accessibility module of any library on the list — but only if you enable it. D3 gives you nothing and everything: every cell is “manual” because the library has no opinion. None of these libraries is a one-line solution; all of them are workable with intent.


4. Good chart, bad chart: the same data, two ways

The matrix shows what each library exposes; this section shows what a working engineer actually writes. Same data, two implementations. The “bad” version ships fast and looks fine on a 27-inch monitor. The “good” version takes 12 more lines of code and clears every axis on the matrix.

Bad chart — fast but unreachable
// Vega-Lite — defaults only
{
"data": { "url": "complaints.csv" },
"mark": "bar",
"encoding": {
  "x": { "field": "category", "type": "nominal" },
  "y": { "field": "count", "type": "quantitative" },
  "color": { "field": "category" }
}
}

Renders. Looks fine. No chart title for the SR. No description. No keyboard model on the marks. Default colour scheme not CVD-tested at the count of categories you actually have. No fallback table.

Good chart — 12 lines that matter
// Vega-Lite — accessible defaults
{
"title": "Complaints by surface, 2024",
"description":
  "Bar chart of 4,605 web-accessibility complaints, ranked by surface. Highest: forms (1,940).",
"data": { "url": "complaints.csv" },
"mark": { "type": "bar", "ariaRoleDescription": "bar" },
"encoding": {
  "x": { "field": "category", "type": "nominal",
         "axis": { "labelAngle": -30 } },
  "y": { "field": "count", "type": "quantitative",
         "title": "Complaints" },
  "color": {
    "field": "category",
    "scale": { "scheme": "tableau10" },
    "legend": { "title": "Surface" }
  },
  "tooltip": [
    { "field": "category", "title": "Surface" },
    { "field": "count", "title": "Complaints" }
  ]
},
"usermeta": { "embedOptions": { "ariaLabel": "Complaints chart" } }
}

Title, description, CVD-safe palette, named axis, named tooltip fields, ARIA role description on the mark. Paired with a <table> rendered from the same dataset, this clears every axis on the matrix without leaving the declarative grammar.

The good chart is not a different chart. It is the same chart with the implicit defaults made explicit, the title written down, the palette named, the per-mark role spelled out, and the data also offered as a table. That is the entire art.

The alternative-table pattern

None of the five libraries ships a default “render this chart as a table” mode on its own. The working pattern is: bind the same data to two components — the chart and an HTML <table> below it, often hidden visually but exposed to assistive technology with a “Show data table” toggle that flips a hidden attribute. This pattern costs approx. 20 lines of framework code per chart and pays for itself within the first user-research session.


5. Concrete picks, by use case

Library choice in 2026 is mostly about workflow fit. The five libraries on the table are all workable. The question is which one matches the kind of charts you are actually shipping. Five common use cases, five picks, with the second-best alternative named.

1

Editorial / data-journalism charts (one chart, polished)

Pick: Observable Plot, with Vega-Lite as a close second. Plot’s mark-based API gives you per-mark ARIA labels for free, the categorical palette is CVD-tested, and the SVG output reads cleanly. Vega-Lite is the second-best here because the description property is the cleanest single-attribute screen-reader summary in any library — but Plot wins on default ergonomics for one-off editorial pieces.

2

Analyst-authored dashboards (many charts, declarative)

Pick: Vega-Lite, with Observable Plot as a close second. Vega-Lite’s specification grammar lets analysts compose 30 charts in one notebook without writing JavaScript, and the schema’s title + description properties give you the screen-reader hierarchy without extra plumbing. Pair every chart with a Vega-rendered data table to clear the alternative-table axis.

3

Scientific / BI dashboards (interactive exploration)

Pick: Plotly.js, with ECharts as a close second. Plotly is the only library on the list that ships keyboard arrow-key navigation between marks as a default in the 2.x line. If your audience expects to hover, zoom, and drill in, Plotly’s built-in keyboard model is the deciding factor. ECharts catches up if you enable the aria module — but you have to enable it.

4

High-density operational dashboards (hundreds of points, performance-critical)

Pick: Apache ECharts with SVG renderer + aria module on, with Plotly as a close second. ECharts is the strongest performance story in this group for very dense charts and the aria module produces per-series alt text that screen readers handle competently. Switch off the canvas renderer; canvas is faster but the accessibility tree disappears.

5

Bespoke editorial charts that no library renders (custom, one-of-a-kind)

Pick: D3 with a hand-written accessibility layer. The hand-written layer is non-negotiable: a <title> + <desc> at the SVG root, per-mark role=“img” with aria-label, a focus model on each focusable mark, and a sibling <table> rendered from the same dataset. D3 is the right tool when the chart genuinely does not exist anywhere else; it is the wrong tool when the chart is a bar chart and someone reached for D3 out of habit.

What none of these libraries fix

The chart inside a chart-library is rarely the only thing on the page. Tooltips that hover-only and never appear on focus, legends that are <div> not <ul>, focus rings overridden in the page’s reset stylesheet, colour swatches with insufficient non-text contrast against the page background — these are page-level failures that no charting library will repair for you. The library gives you the marks; the page gives you the rest.


Conclusion: the working stack is the one you write down

None of the five libraries on the table is the wrong answer. All of them clear most axes with a small amount of intent. The single most reliable predictor of an accessible chart in 2026 is not the library on the import line — it is whether the team has written down, in the same place as the design system, what “accessible chart” means at this organisation. Title. Description. Palette. Keyboard model. Alternative table. Five lines in a CONTRIBUTING.md; the difference between a chart that ships and a chart that lands.

Pick the library that fits the workflow, turn its accessibility defaults on, pair every chart with a data table, and audit the page-level chrome around the chart as carefully as the chart itself. The default chart in any of these libraries can be made accessible. The default chart in none of these libraries is accessible without intent.

”The library gives you the marks; the page gives you the rest.”

— Disability World engineering desk