Vega-Lite Accessible Output
Permalink to "Vega-Lite Accessible Output"Vega-Lite describes charts declaratively in JSON, and the Vega runtime renders them. Since Vega 5.11 and Vega-Lite 4.14, rendered SVG includes ARIA attributes: the chart gets role="graphics-document" with an aria-label from the spec’s description, axes and legends get roles and labels, and marks can carry aria-roledescription and aria-label through description encodings. Because the spec contains the data and its meaning, it is also an ideal source for generated summaries and tables.
This guide belongs to chart library accessibility.
Spec reference
Permalink to "Spec reference"description(top-level) — becomes the chart’s accessible label.aria(mark and config property) — setfalseto hide decorative marks; true by default for many marks.descriptionencoding channel — per-mark accessible description, e.g. a template combining fields.- ARIA graphics roles —
graphics-document,graphics-object,graphics-symbolused in output; support varies across screen readers. - Bindings —
bindon params renders HTML input widgets (range, select, radio) that can be labelled. - Relevant criteria: SC 1.1.1, 4.1.2 (A).
When to use — and when not to
Permalink to "When to use — and when not to"Use Vega-Lite for analytics, notebooks and dashboards where specs are generated or authored declaratively. Set description, axis titles and a description encoding for points when users need per-mark details.
Do not expose every mark of a dense chart through ARIA; thousands of graphics-symbol nodes slow screen readers and add little. Set aria: false on dense mark layers and provide a table.
The misapplication: specs without description, rendered with the canvas renderer, published as the only representation of the data.
Annotated code example
Permalink to "Annotated code example"{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Quarterly revenue by region, 2026. North grew fastest (+18%); South declined in Q3.",
"title": "Revenue by region, 2026",
"data": { "url": "data/revenue.json" },
"mark": { "type": "line", "point": true },
"encoding": {
"x": { "field": "quarter", "type": "ordinal", "title": "Quarter" },
"y": { "field": "revenue", "type": "quantitative", "title": "Revenue (million euros)" },
"color": { "field": "region", "type": "nominal", "title": "Region" },
"strokeDash": { "field": "region", "type": "nominal" },
"description": { "signal": "datum.region + ', ' + datum.quarter + ': ' + format(datum.revenue, '.1f') + ' million euros'" }
}
}
// Render with SVG and generate a table from the same data
const result = await vegaEmbed('#chart', spec, { renderer: 'svg', actions: { export: true, source: false } });
const rows = result.view.data('source_0'); // the dataset used by the chart
renderTable('#chart-table', rows, ['region', 'quarter', 'revenue']);
strokeDash mapped to region gives each line a distinct dash pattern (SC 1.4.1).
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Element | Exposed as | Notes |
|---|---|---|
| Chart | graphics-document with description | Read in browse mode |
| Axes | Labelled with titles and ranges | Some readers read axis groups |
| Marks with description | graphics-symbol with label | Reachable in browse mode, not by Tab |
| Bound inputs | Native inputs with labels | Keyboard operable |
| Table | Native table | Primary route for details |
Integration context
Permalink to "Integration context"Generated text alternatives follow generating accessible text alternatives for D3 charts, whose approach applies to Vega’s data. Observable Plot, a related declarative library, is covered in Observable Plot aria attributes. Brushing alternatives for Vega-Lite interval selections are in brush selection alternatives for range filtering.
Gotchas
Permalink to "Gotchas"Canvas renderer. Canvas output has no mark-level ARIA. Use SVG unless performance requires canvas, and then rely on tables.
Interactive selections. Point and interval selections are pointer-driven by default; bind them to inputs or provide alternatives.
Notebook environments. Jupyter and Observable may wrap charts; check that descriptions survive embedding.
Worked example
Permalink to "Worked example"A data team published weekly KPI reports as Vega-Lite charts in an internal portal, rendered on canvas with no descriptions. A blind analyst relied on colleagues to read them. The team updated their spec templates to include description (computed from the data), human-readable axis titles, strokeDash per series and description encodings, switched to SVG, and added a generated table under each chart. Because the templates were shared, all 60 report charts improved in one change.
Spec templates
Permalink to "Spec templates"Teams that generate many charts should bake accessibility into templates rather than individual specs: a base config with aria defaults and SVG rendering, a required description built from the data (for example, the largest change), axis titles with units, a strokeDash or shape encoding whenever color encodes a series, and aria: false for layers above a mark count. Lint specs in CI for a missing description or missing axis titles; both are easy to detect in JSON.
Testing it with a screen reader
Permalink to "Testing it with a screen reader"- Browse to the chart; confirm the description is read.
- Explore marks in browse mode; confirm descriptions with units (where enabled).
- Check greyscale; confirm dash patterns distinguish series.
- Use bound inputs by keyboard; confirm labels.
- Read the generated table.
Common mistakes
Permalink to "Common mistakes"- Missing description.
- Canvas renderer by default.
- Exposing thousands of marks.
- Pointer-only selections.
Adoption checklist for teams
Permalink to "Adoption checklist for teams"- Add
description, titles and axis titles to spec templates. - Default to the SVG renderer.
- Add
strokeDashorshapeencodings for series. - Set
aria: falseon dense mark layers. - Generate a table from the view’s data for every chart.
FAQ
Permalink to "FAQ"Does Vega-Lite produce accessible SVG?
It produces SVG with ARIA roles and labels when descriptions are provided. Screen reader support for graphics roles varies.
Can I hide decorative marks?
Yes, with aria: false on the mark or in config.
How do I generate summaries?
Compute them from the same data (max, min, change) and set them as description; Vega expressions or your own code can do it.
Are bound inputs accessible?
They are native HTML inputs with labels from the binding name; set clear names.
How do I make tooltips accessible?
Tooltips are pointer-driven; provide descriptions on marks and a table for keyboard and screen reader users.
What about Altair (Python)?
Altair generates Vega-Lite specs, so the same properties apply: description, titles and encodings.
Can screen reader users interact with selections?
Not by default. Bind parameters to inputs or add keyboard alternatives.
How do I keep descriptions in sync with data?
Compute them at build or render time from the same dataset, never by hand.
Does Vega-Lite support high-contrast themes?
You can supply a config with high-contrast colours and stroke widths; test it alongside forced colours.
Related
Permalink to "Related"- Observable Plot aria attributes — a related declarative library
- Generating text alternatives for D3 charts — summaries from data
- Brush selection alternatives — interactive selections