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 table for tabular data. A grid of divs cannot be navigated cell by cell and conveys no relationship between values.
  • A caption names the table and is the first thing announced. Without it, the user hears a table with no idea what it contains.
  • scope="col" and scope="row" are enough for the vast majority of tables. Reach for headers and 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">&#9650;</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 typeMinimum text alternativeBetter
Simple bar chartOne sentence with the trend and the rangeThe same, plus the data table
Multi-series line chartThe trend per seriesA table of values, or direct labels
Pie chartThe largest share and the totalA table; pie charts are hard for everyone
MapThe geographic claim being madeA ranked list of regions
Sparkline in a table cellThe value it representsThe number, with the sparkline decorative
Interactive dashboardA written summary of what it showsPer-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>
  1. Write the summary first, in plain language, and put it in the description. Most users will read that instead of the chart.
  2. 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.
  3. If the chart is interactive, every point must be reachable with the keyboard, and tooltips must not be hover-only.
  4. Do not encode meaning in colour alone: use direct labels, patterns, or distinct marker shapes as well.
  5. 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.

Images, media and alternative text Colour, contrast and accessible visual design

Last refreshed 2026-09-18.