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.