Skip to content

A11Y Cheat Sheet

Powered byA11y University

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 it affects, and how much
WhoWhat they experienceImpact
Screen reader usersThey 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

Business & product impact, with how much each claim weighs
AreaWhyImpact
LegalIt 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
ConversionPricing, timetable and comparison tables are useless if people can't tell which column a figure sits in.Moderate impact
SEOSearch engines don't use `headers` links. No real search benefit.Low impact
Brand trustData-heavy pages, like reports and fees, are often where people need trust the most.Moderate impact
Cost to fixUsually 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
Other standards
EN 301 549 9.1.3.1 · RGAA 5.7.4
Last reviewed
14 October 2026