squirrelscan
GitHub

td headers attribute pointing at a missing th id

A headers attribute naming an id that does not exist leaves the cell unassociated. squirrel checks every reference in every table.

What the headers attribute is

In a complex table, headers on a <td> names the ids of the <th> cells that describe it. A screen reader reads those headers before the value, which is how a cell in a multi-level table announces “Revenue, Europe, Q3, 6.8M”.

Simple tables do not need it. One row of column headers and one column of row headers is fully described by scope="col" and scope="row". headers is for tables with split or stacked headers, where scope cannot express the relationship.

What squirrel checks

One check, td-headers-attr, at severity error and weight 6. For each <table> it collects the ids of every th[id] in that table, then reads every td[headers] and splits the value on whitespace.

  • Fail when a referenced id does not belong to a <th> in the same table. Report message: N invalid header reference(s) in table cells. Items read td references non-existent header "q3" (content: "6.8M..."), quoting the first 15 characters of the cell. Up to ten are shown with a count of the rest.
  • Pass with All td headers attributes reference valid th ids.
  • Info with No td elements with headers attribute found, or No tables found.

References are scoped per table, so a headers value pointing at a <th> in a different table is reported even though the id exists on the page.

How to fix it

<table>
  <tr>
    <td></td>
    <th id="q3" scope="col">Q3</th>
  </tr>
  <tr>
    <th id="eu" scope="row">Europe</th>
    <td headers="eu q3">6.8M</td>
  </tr>
</table>

Every id in the headers value has to exist on a <th> in the same table. Ids that were renamed during a refactor are the usual cause, and so are ids generated per row that do not match the ones generated per header.

If the table is simple enough for scope alone, drop the headers attributes entirely. Fewer references means fewer to keep in sync.

Rule IDa11y/td-headers-attr
CategoryAccessibility
ScopePer-page
Severityerror
Weight6/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/td-headers-attr"]

Disable all Accessibility rules

[rules]
disable = ["a11y/*"]

Enable only this rule

[rules]
enable = ["a11y/td-headers-attr"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Accessibility section of the report. Each broken reference is listed with the id it names and the cell’s opening text. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close