How to fix a headers attribute that points outside its table?
td-headers-attr page
The issue
In a complex table, the headers attribute tells a screen reader which header cells describe a data cell. When it points at an ID that isn’t in the table, the screen reader announces the value with no header, or with the wrong one. A figure like “42” means nothing without “Q3” and “Returns” read alongside it.
The cause is usually an ID that changed or was copied: the header cell was renamed, the table was split in two, or a template reuses IDs across tables. A cell that lists its own ID is the same mistake. WCAG 1.3.1 requires that the relationships shown visually, such as which header a cell sits under, are available to assistive technology.
Scope
Every visible <td> with a headers attribute is checked. Each ID it lists must belong to an element inside the same <table>, and none may be the cell’s own ID.
How to check you have it
- In probeo: the page reports
td-headers-attr, “All cells in a table that use the headers attribute must only refer to other cells in that table”, as serious, under WCAG 1.3.1. The detail lists the IDs that don’t resolve, or notes that the cell references itself. - By hand: for each
<td headers="…">, search the same table for each ID listed. - With a tool: axe DevTools has a rule with the same name.
The fix
Point each headers attribute only at the IDs of header cells in the same table.
Give every <th> the cell needs an id, then list those IDs, separated by spaces, on the <td>. For a simple table with one header row or column, scope on the <th> is enough and headers can go.
<table>
<tr><th id="q3">Q3</th><th id="returns">Returns</th></tr>
<tr><td headers="q3 returns">42</td></tr>
</table>Done when: every ID in every headers attribute matches a cell in the same table, and the check above comes back clean.
Do not
- Do not reuse header IDs across tables on the same page.
- Do not list a cell’s own ID in its
headers. - Do not use
headerson a simple table wherescopewould do.
FAQ
When is headers better than scope?
When a data cell sits under more than one level of header, such as a column header and a group header above it, or when headers aren’t in the cell’s own row or column.
Can headers point at a td?
Probeo accepts any ID inside the same table. Pointing at <th> cells is what makes the relationship meaningful.
Does an empty headers attribute fail?
No. A cell without a headers value isn’t checked by this rule.
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.