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) — set false to hide decorative marks; true by default for many marks.
  • description encoding channel — per-mark accessible description, e.g. a template combining fields.
  • ARIA graphics roles — graphics-document, graphics-object, graphics-symbol used in output; support varies across screen readers.
  • Bindings — bind on params renders HTML input widgets (range, select, radio) that can be labelled.
  • Relevant criteria: SC 1.1.1, 4.1.2 (A).
From Vega-Lite spec to accessible output Layers: the JSON spec with description, titles and encodings; the Vega runtime rendering SVG with ARIA roles; mark-level descriptions; generated summaries and tables from the same data; bound input widgets for interaction. From Vega-Lite spec to accessible outputSpecdescription, titles, encodings, aria flagsRendered SVGgraphics-document with aria-label; axes and legends labelledMark descriptionsdescription encoding per markGenerated textsummary and table from the spec's dataBound inputslabelled HTML widgets for parameters
The spec is the single source for visual and text output.

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.

Bare spec versus accessible spec Comparison: a bare spec renders an unnamed graphic with unlabelled marks; an accessible spec adds a description, axis titles, mark descriptions with units, aria flags for dense layers and a generated table. Bare spec versus accessible spec✗ Bare specNo description: unnamed graphicAxis titles default to field namesMarks without descriptionsDense marks all exposedNo table✓ Accessible specdescription: chart purpose and findingHuman-readable axis titles with unitsdescription encoding with values and unitsaria: false on dense layersTable generated from the same 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
Graphics role support across readers Matrix of NVDA, JAWS, VoiceOver and TalkBack against reading the graphics-document label, navigating marks in browse mode, and reading axis groups. Graphics role support across readersDocument labelMarks in browse modeAxis groupsNVDA + ChromeYesAs images or groupsPartialJAWS + ChromeYesPartialPartialVoiceOver + SafariYesInteract to explorePartialTalkBack + ChromeYesSwipe throughLimited
Labels are read widely; mark navigation varies — keep the table.

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"
  1. Browse to the chart; confirm the description is read.
  2. Explore marks in browse mode; confirm descriptions with units (where enabled).
  3. Check greyscale; confirm dash patterns distinguish series.
  4. Use bound inputs by keyboard; confirm labels.
  5. 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"
  1. Add description, titles and axis titles to spec templates.
  2. Default to the SVG renderer.
  3. Add strokeDash or shape encodings for series.
  4. Set aria: false on dense mark layers.
  5. 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.

Permalink to "Related"

← Back to Chart Library Accessibility