Accessible Sankey Diagrams
Permalink to "Accessible Sankey Diagrams"Sankey diagrams show how quantities flow between stages: budget from departments to cost categories, users from acquisition channels through funnel steps, energy from sources to uses. The width of each band is the quantity. They are popular in dashboards because they compress a lot into one picture — and almost entirely inaccessible by default, because band widths and colour-coded paths are the only encoding. Screen reader users get nothing; colour-blind users struggle to follow which band leads where.
This guide belongs to accessible network & graph diagrams.
Spec reference
Permalink to "Spec reference"- SC 1.1.1 Non-text Content (A) — the diagram needs a text alternative; a flow table plus a summary is the practical equivalent.
- SC 1.3.1 Info and Relationships (A) — flows are relationships (source → target, value) and must be available as data.
- SC 1.4.1 Use of Color (A) — bands coloured by source cannot be the only way to trace a flow; label nodes and provide focus highlighting.
- SC 1.4.11 Non-text Contrast (AA) — bands and nodes need 3:1 against the background where they convey information.
- SC 2.1.1 Keyboard (A) — if hovering a band reveals its value, focus must too.
When to use — and when not to
Permalink to "When to use — and when not to"Use a Sankey when the flow between stages is the point, and pair it with a flow table and summary. For funnel data with a single path, a simple bar chart of step counts with conversion rates is often clearer for everyone.
Do not use a Sankey when users need precise comparisons; band widths are hard to compare even for sighted users. The misapplication: a Sankey with hover-only tooltips for values and no labels on bands — no one can read exact numbers without a mouse.
Annotated code example
Permalink to "Annotated code example"<figure aria-labelledby="sk-title" aria-describedby="sk-sum">
<h3 id="sk-title">2026 budget: departments to cost categories</h3>
<p id="sk-sum">Total 11.0 million EUR. Salaries take 54%, mostly from Engineering (4.2 M) and Sales (1.2 M).
Cloud costs come entirely from Engineering. <a href="#sk-table">Table of all flows</a>.</p>
<svg viewBox="0 0 20 20" role="group" aria-label="Budget flow diagram">
<defs><pattern id="hatch-eng" width="6" height="6" patternUnits="userSpaceOnUse"><path d="M0,6 L6,0" stroke="#111714"/></pattern></defs>
<!-- Each band focusable with a full name (SC 2.1.1, 1.3.1) -->
<path class="band" tabindex="0" role="img"
aria-label="Engineering to Salaries: 4.2 million EUR, 70 percent of Engineering"
d="…" fill="url(#hatch-eng)"/> <!-- pattern, not only colour (SC 1.4.1) -->
<text x="…" y="…">Engineering 6.0 M</text> <!-- direct labels on nodes -->
</svg>
</figure>
<table id="sk-table">…</table>
// Highlight a focused band's path and dim others — not by colour alone: thicker stroke + labels
svg.addEventListener('focusin', (e) => {
const band = e.target.closest('.band'); if (!band) return;
svg.classList.add('has-focus'); band.classList.add('focused'); // CSS: outline stroke 3px, others at reduced opacity
});
svg.addEventListener('focusout', (e) => { e.target.closest('.band')?.classList.remove('focused'); svg.classList.remove('has-focus'); });
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Event | Expected announcement | AT deviations |
|---|---|---|
| Tab to a band | “Engineering to Salaries: 4.2 million EUR, 70 percent of Engineering” | SVG role=img on paths is read reliably in Chrome and Firefox |
| Tab through bands | Source-ordered list of flows | Many bands mean many tab stops; consider arrow keys within the diagram |
| Reach the table | “Budget flows, table, 12 rows” | — |
| Summary | Read before the diagram | — |
Integration context
Permalink to "Integration context"The flow table follows adjacency tables as network graph alternatives, with value and share columns. Patterns for distinguishing bands without colour are in patterns and shapes for colour-independent charts. For many focusable bands, use the arrow-key model from adding keyboard navigation to SVG line charts.
Gotchas
Permalink to "Gotchas"Tiny bands. Very small flows are impossible to target with a pointer or see. Group them into “Other” with a breakdown in the table.
Circular flows. Sankeys assume acyclic flows; if your data has cycles, a Sankey misrepresents it. Use a network diagram with a table instead.
Multi-stage totals. Users want “how much of the source reached the final stage”. Include end-to-end totals in the summary or a second table.
Common mistakes
Permalink to "Common mistakes"- Values only in hover tooltips.
- Bands coloured by source with no labels or patterns.
- No table, so precise values are visual estimates.
- Every band a tab stop in random order.
Framework notes
Permalink to "Framework notes"d3-sankey computes node and link layouts; you add tabindex, role="img" and aria-label to the rendered paths, and generate the table from graph.links. ECharts Sankey series support its aria option for a generated description and decal patterns; check whether bands are keyboard focusable in your version. Highcharts Sankey works with its accessibility module, which adds keyboard navigation and descriptions — configure point descriptions to include share.
links.forEach(l => path.append('path').attr('tabindex', 0).attr('role', 'img')
.attr('aria-label', `${l.source.name} to ${l.target.name}: ${fmt(l.value)}, ${pct(l.value / l.source.value)} of ${l.source.name}`));
Worked example
Permalink to "Worked example"A product analytics dashboard used a Sankey to show users moving from sign-up channels through onboarding steps. Values appeared only on hover, and channels were distinguished by six similar colours. The redesign kept the Sankey but added node labels with totals, a summary (“62% of sign-ups complete onboarding; paid search users drop off most at step 2”), focusable bands with full names, and a flow table with “share of source” and “share of total” columns. Product managers started using the table in planning meetings because it was easier to compare.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Provide a FlowChart wrapper that renders the Sankey, its table and its summary from one dataset, with band naming and pattern fills built in. Discourage raw Sankey usage without the wrapper.
FAQ
Permalink to "FAQ"Are Sankey diagrams accessible at all?
With a summary, a flow table and focusable bands, they can be. Without them, they are not.
Should each band be a tab stop?
For small diagrams, it is acceptable. For more than about fifteen bands, use one tab stop and arrow keys, and rely on the table for exhaustive reading.
Is share of source or share of total more useful?
Both answer different questions; include both in the table.
How do I handle colour?
Use colour plus labels and, where bands overlap, patterns; ensure each band reaches 3:1 against the background.
Should the table be sorted?
By source then target by default, and sortable by value.
Is a funnel chart better for conversion data?
For a single path, often yes: bars with counts and conversion rates are easier to read for everyone.
How do I summarise a multi-stage Sankey?
Summarise each stage transition in one sentence (“From channel to sign-up: paid search is largest at 42%”), then the end-to-end result (“62% of all visitors reach activation”). Users can then drill into the table for details.
Related
Permalink to "Related"- Adjacency tables — flow tables are a variant
- Patterns and shapes — distinguishing bands
- Accessible stacked bar charts — another part-to-whole chart