Structure & headings
A Table Cell's Headers Attribute Points to the Wrong Cells
When a cell's `headers` list names cells that aren't headers in the same table, screen reader users don't hear which row or column the data belongs to.
td-headers-attrWCAG 2.0Introduced in WCAG 2.0.Level AA — the minimum. Missing it blocks people outright.
- Status
- SeriousSerious — Significant friction, or information that is lost.CurrentScanners run this rule today.
- Owner
- DevContentTest
- Audit
- Automated+ManualA scanner finds it; a person confirms the fix.
Why it matters
In complex tables, the headers attribute links each data cell to its headings by their id. A screen reader reads those headings aloud before the data, so people know what a number means.
If the id doesn't exist, or belongs to another table or a plain data cell, the link breaks. People hear a lone number, like "42", with no idea what it measures.
Who it affects
| Who | What they experience | Impact |
|---|---|---|
| Screen reader users | They hear the data, like "$120", but not the row and column headings that explain it. | High impact |
| Low-vision users (magnification) | Affected only if they also use a screen reader; zoom users can still see the headings. | Partial impact |
Business & product impact
| Area | Why | Impact |
|---|---|---|
| Legal | It fails WCAG 1.3.1 at Level A, the minimum. Australia's Disability Discrimination Act and EN 301 549 both point to WCAG. | High impact |
| Conversion | Pricing, timetable and comparison tables are useless if people can't tell which column a figure sits in. | Moderate impact |
| SEO | Search engines don't use `headers` links. No real search benefit. | Low impact |
| Brand trust | Data-heavy pages, like reports and fees, are often where people need trust the most. | Moderate impact |
| Cost to fix | Usually a quick fix of mismatched `id` values. Generated tables need a fix in the template. | High impact |
Fixing it in code
A headers attribute that points to a missing id
Don't
<table>
<tr><th id="plan">Plan</th><th id="price">Price</th></tr>
<tr><th id="basic">Basic</th><td headers="basic cost">$10</td></tr>
</table>A screen reader says: “$10”
Do
<table>
<tr><th id="plan">Plan</th><th id="price">Price</th></tr>
<tr><th id="basic">Basic</th><td headers="basic price">$10</td></tr>
</table>A screen reader says: “Basic, Price, $10”
Every id in headers now matches a th in the same table.
A headers attribute that points to a data cell
Don't
<tr><td id="q1">Q1</td><td headers="q1">$4,000</td></tr>A screen reader says: “$4,000”
Do
<tr><th id="q1" scope="row">Q1</th><td headers="q1">$4,000</td></tr>A screen reader says: “Q1, $4,000”
headers must point to header cells, so make the heading a th.
Designer's handoff note
Keep tables simple where you can — one row of column headings and one column of row headings. Simple tables don't need headers at all.
If a table must have grouped or nested headings, mark which heading cells label each data cell in the handover.
Quick test
Tool
NVDA (opens in a new tab) (free on Windows)
Checklist
- Pass
- You hear the right row and column headings before each value.
- Fail
- You hear only the value, or headings that don't match what you see.
What a green scan still misses
A scanner checks that each id points to a header in the same table. It can't tell if it's the right header — for that, listen cell by cell.
Reference
- Rule ID
td-headers-attr- Raised by
- axe-core: “Table cell headers attributes must refer to other <th> elements in the same table”
- WCAG
- 1.3.1 Info and Relationships (opens in a new tab) · Level A · since WCAG 2.0
- Other standards
- EN 301 549 9.1.3.1 · RGAA 5.7.4
- Last reviewed
- 14 October 2026