Skip to content
Website Accessibility

Accessible Tables: Headers, Captions and Phones

A table that looks clear on screen can sound like a jumble of numbers to a screen reader user. The header markup, captions and mobile handling that fix it.

8 sections · 9 min read
In this article · 8 sections

A gym posts its class timetable as a table. Sighted visitors glance across a row and up a column and have the answer in a second. Someone listening with a screen reader hears “9 a.m., 45, Studio B, 9:30, 60, Studio A” with no idea which number is the duration and which room belongs to which class.

Accessible tables fix that by telling assistive technology which cells are headers and what each data cell belongs to. The work is small, it is mostly done once per table, and it lives in the content as much as the code. This post covers when a table is the right tool, the markup that makes accessible HTML tables work, captions, phones and narrow screens, and what the WordPress block editor gives you out of the box.

Data tables, layout tables and when accessible tables matter

The first question is whether the content is really a table. A data table holds information that has meaning in two directions: rows and columns. Opening hours by day, prices by plan, a comparison of features, staff and their phone extensions. If you can describe a cell as “the value for this row under this column”, it is data.

Layout tables are a leftover from old web design, when tables were used to put a picture on the left and text on the right. Some older themes, email templates and copied content still do this. Screen readers may announce these as tables, with row and column counts, which is confusing when there is no data relationship at all. Layout belongs in CSS, columns blocks or the page builder’s own containers, not in table markup.

There is a third case worth naming: content that is crammed into a table because it looked neat. A list of services with one item per row and a single column is a list, not a table. Use a list and you lose nothing.

What makes accessible HTML tables work

WCAG 1.3.1 Info and Relationships asks that structure you can see is also available in the code. For a table, that means header cells are marked as headers, not just made bold.

Use th for headers

Column headers and row headers go in th elements. Data goes in td. That single change is what lets a screen reader say “Tuesday, Opens, 8 a.m.” as the user moves through the table, instead of just “8 a.m.”.

Add scope on simple tables

The scope attribute tells the browser whether a header applies to its column or its row. Many browsers can guess for simple tables, but setting it removes the guesswork, especially when a table has headers in both the first row and the first column.

<table>
  <caption>Office hours, Main Street location</caption>
  <thead>
    <tr>
      <th scope="col">Day</th>
      <th scope="col">Opens</th>
      <th scope="col">Closes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Monday</th>
      <td>8 a.m.</td>
      <td>5 p.m.</td>
    </tr>
  </tbody>
</table>

Keep header text short and real

Every header cell should have text. An empty top-left corner cell is common and usually harmless, but a header that is only an icon, or a blank header over a column of prices, leaves the user guessing. Short headers also matter because screen readers repeat them as the user moves between cells. “Price per month” is better than a sentence.

Captions and summaries

A caption is the table’s title, placed right after the opening table tag. Screen readers announce it when the user reaches the table, and many let users list tables by caption. Sighted users benefit too, since a short visible title tells them what they are looking at.

If you already have a heading directly above the table that names it, the caption can repeat that idea in fewer words, or you can make the heading the only label. Either is fine. What does not help is a caption like “Table 1” or “Pricing table”. Say what the data is: “Monthly plan prices, billed annually”.

For a table that needs explaining, such as one with abbreviations or a footnote about what an asterisk means, put the explanation in a paragraph right before the table. Older advice mentioned a summary attribute; it is obsolete in current HTML, so use visible text instead.

Complex tables: simplify before you code

Merged cells, two rows of headers, headers in the middle of the table, and cells spanning several columns all make tables harder for everyone, not only screen reader users. They can be made accessible with id and headers attributes that link each data cell to its headers, but that markup is fiddly, easy to break on the next edit, and not supported equally well by every screen reader.

Most of the time the better fix is editorial. Can one large table become two or three simple ones, each with a caption? Can a merged “Weekdays” header become a column that says “Weekday” or “Weekend” on each row? A city that publishes a fee table with sub-headers for residential and commercial permits can usually split it into two tables with their own headings, and everyone finds their fee faster.

Tables on phones and when zoomed in

Wide tables are the classic reflow problem. WCAG 1.4.10 Reflow asks that content works at 320 CSS pixels wide without scrolling in two directions, but it makes an exception for content that needs a two-dimensional layout to make sense, and data tables are the standard example. So a wide table may scroll sideways. The rest of the page should not scroll sideways because of it.

The usual pattern is to wrap the table in a container that scrolls on its own. If that container can be scrolled, keyboard users need to be able to reach and scroll it too, which often means making the wrapper focusable and giving it a name.

<div class="table-scroll" role="region" aria-label="Office hours" tabindex="0">
  <table> ... </table>
</div>

.table-scroll { overflow-x: auto; }
.table-scroll:focus { outline: 2px solid currentColor; }

Some themes and plugins turn each row into a stacked card on small screens, using CSS to hide the header row and repeat labels next to each value. This can work, but test it. Changing table display properties in CSS can, in some browser and screen reader combinations, remove the table semantics entirely, so the screen reader no longer knows it is a table. If you use a stacked pattern, check it with a screen reader on a phone. The broader zoom and resize checks are covered in our post on reflow and text resizing.

Tables in the WordPress block editor

The core Table block is a reasonable starting point. In its settings you can turn on a header section, which produces a proper header row with th cells, and you can add a caption from the block. Both are worth using every time. The block does not, as far as an editor can control, give you row headers in the first column, so a table that needs both row and column headers may need a little custom HTML or a developer’s help.

A few habits help on WordPress sites:

  1. Turn on the header section in the Table block before typing any data.
  2. Add a caption that says what the data is.
  3. Do not leave rows empty to create spacing; adjust the design instead.
  4. Avoid pasting tables from Word or Google Docs straight in. They often bring merged cells, inline formatting and missing headers with them. Paste as plain text and rebuild, or check the result.
  5. Be careful with table plugins. Some produce good markup with sortable columns and filters; others output div grids that look like tables but have no table semantics. Test before you commit.

The block editor’s other structural tools, such as headings and lists, are covered in our post on accessible content in Gutenberg.

How to check a table yourself

You do not need to read code to find most table problems.

  • Run a scanner. Free tools such as WAVE point out some table problems, such as empty header cells or a table with no header cells at all.
  • Use a screen reader. In NVDA on Windows, move into a table and use Ctrl+Alt with the arrow keys to move between cells. As you move, it should announce the header for the new row or column. If it only reads values, the headers are missing.
  • Zoom in. Set the browser to a high zoom level and check that only the table scrolls sideways, not the whole page.
  • Read it without the grid. Imagine hearing each cell alone. Does every value make sense with its row and column header? If not, the headers need rewording.

Tables also show up in documents. If your fee schedules or reports are published as PDFs, the same rules apply inside the file, which our post on PDF accessibility for websites explains. For the full set of things editors control, from images to plain wording, start at our guide to content that works for every reader. And for audits and fixes across a whole site, see our overview of website accessibility services.

Frequently asked questions

Do I need scope attributes if my table already has th cells?

For a simple table with one header row, browsers and screen readers usually work out the relationship from th alone. Adding scope removes the guesswork and helps when a table has both column and row headers. It takes seconds to add, so it is a sensible default rather than something to debate.

Is it OK for a table to scroll sideways on a phone?

Yes, for a real data table. The WCAG reflow criterion makes an exception for content that needs a two-dimensional layout, and data tables are the usual example. Put the table in its own scrolling container so the rest of the page still fits the screen, and make sure keyboard users can reach and scroll that container.

Should I use a table plugin or the core Table block?

For simple tables, the core Table block with its header section turned on and a caption is usually enough. Plugins add sorting, filtering and large data sets, but their markup varies a lot. Before choosing one, build a sample table and check it with a screen reader and a free scanner to confirm it outputs real table headers.

Can I use a table to lay out images and text side by side?

It is better not to. Layout tables can be announced as data tables by screen readers, with row and column counts that mean nothing. Use a columns block, a media and text block or your page builder’s layout containers instead. They also adapt to phone screens far better than a table does.

Building accessible tables one at a time works well on a modern theme. Tables are also one of those problems that show up again and again on older sites, often alongside layout tables and pasted content from years back. If your templates are full of them, an accessible website redesign can be the cleaner route, with real tables only where the content needs them.

Next in this series: Buttons vs Links: Which to Use for Accessibility