How to fix data cells with no table header?

td-has-header page

The issue

In a table of any size, a sighted reader glances up and across to see what a number means. A screen reader user hears only the cell unless the table says which headers apply. In a larger table, a value like “$1,200” with no header read alongside it could be any row and any column.

The cause is a table whose headers are styled to look like headers but marked up as <td>, or a table with no header cells at all. WCAG 1.3.1 requires that the relationship between a cell and its headers is available to assistive technology.

Scope

Tables with at least three rows and three columns are checked, except layout tables marked role="presentation" or role="none". Each non-empty <td> needs a <th> in its row or its column, or a headers attribute naming a <th>. If the table has no <th> at all, every non-empty data cell is reported.

How to check you have it

  • In probeo: the page reports td-has-header, “Non-empty <td> elements in larger tables must be associated with a table header”, as serious, under WCAG 1.3.1.
  • By hand: open the table in DevTools and check that its header row or column uses <th>, not a bold <td>.
  • With a tool: axe DevTools has a rule with the same name. A screen reader’s table navigation shows whether headers are announced as you move between cells.

The fix

Mark the header row and header column with <th> and a scope, so each data cell has a header in its row or column.

<table>
  <tr><th scope="col">Plan</th><th scope="col">Pages</th><th scope="col">Price</th></tr>
  <tr><th scope="row">Pro</th><td>5,000</td><td>$1,200</td></tr>
</table>

For cells that sit under more than one header, give the headers IDs and list them in each cell’s headers attribute.

Done when: every non-empty data cell has a header in its row, its column or its headers attribute, and the check above comes back clean.

Do not

  • Do not style a <td> as a header instead of using <th>.
  • Do not use a table for layout without role="presentation".
  • Do not put a header only in a caption or in text above the table.

FAQ

Why aren’t small tables checked?

Probeo checks tables of at least three rows and three columns. A smaller table is easier to follow without headers, though headers still help.

Do empty cells need headers?

No. Empty <td> cells are skipped.

Is a header in the first row enough?

Yes, for this check: a <th> in the cell’s column counts. A header column with scope="row" makes each row’s values clearer as well.

Which product detects this

Probeo checks this on every page of your site as part of Site Clarity. This page explains one instance; the product tells you every page that has it.