Data tables, charts and data visualisation
Header cells and captions, complex table headers, accessible sort controls, text alternatives for charts, and colourblind-safe encodings.
Table structure
<table>
<caption>Quarterly revenue by region, thousands of dollars</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
<th scope="col">Q3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Europe</th>
<td>120</td><td>135</td><td>148</td>
</tr>
<tr>
<th scope="row">North America</th>
<td>98</td><td>101</td><td>104</td>
</tr>
</tbody>
</table>- Use a real
tablefor tabular data. A grid of divs cannot be navigated cell by cell and conveys no relationship between values. - A
captionnames the table and is the first thing announced. Without it, the user hears a table with no idea what it contains. scope="col"andscope="row"are enough for the vast majority of tables. Reach forheadersand ids only when the header structure is genuinely irregular.- Never use a table for layout. It is the original accessibility failure and it still happens.
<!-- a sortable column header, with the state exposed -->
<th scope="col" aria-sort="ascending">
<button type="button">
Revenue
<span aria-hidden="true">▲</span>
</button>
</th>
<!-- and a table that is scrollable on small screens needs a keyboard-reachable wrapper -->
<div role="region" aria-labelledby="rev-caption" tabindex="0">
<table aria-labelledby="rev-caption">...</table>
</div>A scrollable table container must be focusable, or a keyboard user cannot scroll it. A wrapper with tabindex="0" and an accessible name is the standard fix, and the name should say what the region contains.
Charts and visualisation
| Chart type | Minimum text alternative | Better |
|---|---|---|
| Simple bar chart | One sentence with the trend and the range | The same, plus the data table |
| Multi-series line chart | The trend per series | A table of values, or direct labels |
| Pie chart | The largest share and the total | A table; pie charts are hard for everyone |
| Map | The geographic claim being made | A ranked list of regions |
| Sparkline in a table cell | The value it represents | The number, with the sparkline decorative |
| Interactive dashboard | A written summary of what it shows | Per-panel summaries and a downloadable dataset |
<figure>
<figcaption id="revenue-caption">Revenue by region, Q1 to Q3 2026</figcaption>
<svg role="img" aria-labelledby="revenue-caption revenue-desc" viewBox="0 0 600 300">
<title>Revenue by region</title>
<desc id="revenue-desc">Europe grows from 120 to 148 thousand. North America is
almost flat, from 98 to 104. Asia doubles in Q3 to 210, becoming the largest region.</desc>
<!-- the drawing itself is invisible to assistive technology -->
</svg>
<details>
<summary>View the data as a table</summary>
<table>
<caption>Revenue by region, thousands of dollars</caption>
<thead><tr><th scope="col">Region</th><th scope="col">Q1</th><th scope="col">Q3</th></tr></thead>
<tbody><tr><th scope="row">Asia</th><td>96</td><td>210</td></tr></tbody>
</table>
</details>
</figure>- Write the summary first, in plain language, and put it in the description. Most users will read that instead of the chart.
- Offer the underlying data. A table is the most accessible visualisation, and it is also the most useful for anyone who wants to check a number.
- If the chart is interactive, every point must be reachable with the keyboard, and tooltips must not be hover-only.
- Do not encode meaning in colour alone: use direct labels, patterns, or distinct marker shapes as well.
- Test the rendering with a simulated colour-vision deficiency and in greyscale. If a series becomes indistinguishable, the encoding is wrong.
Dashboards and refreshing data
- A dashboard that refreshes silently is a dashboard a screen reader user does not know has changed. Announce updates politely, and only when the values actually changed.
- Give each panel a heading so the structure is navigable. A grid of unlabelled cards is unnavigable by any means other than tabbing.
- Do not auto-refresh while the user is reading or editing. Offer a manual refresh or pause while focus is inside a panel.
- Provide a way to export the data. It is the most reliable accessible fallback for any visualisation.
- Keep the text alternative next to the chart, not in a tooltip or behind a click.
<section aria-labelledby="panel-orders">
<h2 id="panel-orders">Orders today</h2>
<p class="value">1,284 <span class="delta">up 12 percent on yesterday</span></p>
<p role="status" aria-live="polite" class="sr-only" id="orders-live"></p>
</section>💡
A chart is a summary of data, and a summary hides detail. Providing the table is not a workaround for accessibility - it is the more useful artefact for anyone who needs to check a figure, quote it, or feed it into something else.
FAQ
Do charts need alt text if there is a table?
Yes, a short one. The table serves someone who wants the numbers; the alt text serves someone scanning the page who wants to know what the figure shows before deciding to dig in.
Is an SVG chart readable by a screen reader?
Only if it has
role="img" and a name, and the drawing is hidden. Without that, an SVG is announced as a pile of unlabelled groups and paths.Related
Images, media and alternative text Colour, contrast and accessible visual design
Last refreshed 2026-09-18.