Do not mis-wire headers and id attributes in tables
A data cell points at its header by id, and points at the wrong one, or at an id that is not in the table any more. F90 is the failure technique covering that, so a match is a defect rather than a technique. The usual cause is maintenance rather than design. Somebody duplicated a row, the ids came along with it, and nobody went back to update them. Two shapes come out of that. A reference to an id that does not exist, which any validator finds, and a reference to a real header describing a different column, which nothing but a person will ever find. Often the wiring is the mistake in itself. Headers and ids exist for tables with more than one level of heading, so a simple table wants scope, and reaching for headers and id on one is how this failure gets built.
How we find it in an audit
Broken references flag instantly, because a headers value pointing at an id nowhere in the table is a mechanical check. The valid-but-wrong case needs a reviewer, who reads each data cell alongside the header it claims and asks whether that pairing is true. Grade tables and pricing grids get the most time, since repeated sub-headings like 1, 2 and Final read identically under two different parents and a swap between them changes nothing on the screen. We flag tables using headers and id where scope would do as well, because that setup is what this failure needs to exist.
How affected users experience it
Table navigation in a screen reader means hearing the header announced as you move, which is the entire reason the wiring exists. When the wiring is wrong, the announcement stays confident and becomes false. A score gets read out under Exams when it belongs under Projects, and nothing signals that anything went wrong. That is worse than silence. Silence at least tells a user to go and check something.
Passes vs. fails
Passes
<th id="q3">Q3</th> ...
<td headers="q3">$40,000</td>
<!-- on a single-level table, drop headers and id and put scope="col" on the th -->Fails
<th id="q3">Q3</th> ...
<td headers="q4">$40,000</td> <!-- no cell in this table has id="q4" -->How this gets tested
The W3C publishes test rules that define what a checker looks for here.
- ARIA attribute is defined in WAI-ARIAA tool can check this
- ARIA state or property has valid valueA tool can check this
- Element with role attribute has required states and propertiesA tool can check this
- Form field has non-empty accessible nameA tool can check this
- Headers attribute specified on a cell refers to cells in the same table elementA tool can check this
- Role attribute has valid valueA tool can check this
- ARIA global properties not used where prohibitedA tool can check this
- ARIA required context roleA tool can check this
- ARIA required owned elementsA tool can check this
- ARIA state or property is permittedA tool can check this
- Table header cell has assigned cellsA tool can check this
The other techniques filed under this rule
52 guides on this site are filed under 1.3.1 Info and Relationships. W3C documents this one as a failure of that rule, so it describes a way the rule gets broken rather than a way to pass it.
- ARIA11sufficientAdd landmarks so users can skip around
- ARIA12sufficientUse role=heading when h1-h6 is impossible
- ARIA13sufficientName repeated landmarks so they are distinguishable
- ARIA16sufficientName controls from visible text with aria-labelledby
- ARIA17sufficientGroup related form fields with ARIA roles
- ARIA20sufficientMark leftover page areas with role=region
This guide is our interpretation of W3C technique F90: Failure of Success Criterion 1.3.1 for incorrectly associating table headers and content via the headers and id attributes. W3C publishes its techniques as guidance rather than as the standard, and says so on every one of them. The success criterion is what conformance is measured against, and a technique is one documented way to meet it.