Accessibility

a11y/table-headers

Data tables mark their header cells with th (with scope when needed); layout tables carry role="presentation".

seriousstaticWCAG 1.3.1 · Afe-a11y-tables

Why

Screen readers read a cell together with its column and row headers ("Price, Basic: Free"). With bold <td> cells instead of <th>, a table of numbers becomes a list of values with no meaning.

Fix

Use <th scope="col"> for column headers (in <thead>) and <th scope="row"> for the first cell of each row when it names the row. Add a <caption>. For tables used only to lay out an email or a legacy page, set role="presentation".

Examples

<!-- bad -->
<table><tr><td><b>Plan</b></td><td><b>Price</b></td></tr><tr><td>Basic</td><td>Free</td></tr></table>
<!-- good -->
<table>
  <caption>Plans</caption>
  <thead><tr><th scope="col">Plan</th><th scope="col">Price</th></tr></thead>
  <tbody><tr><th scope="row">Basic</th><td>Free</td></tr></tbody>
</table>

Sources

Facts

Id
a11y/table-headers
Severity
serious
Check
static checked in the source by audit
Applies to
html angular vue svelte astro jsx
WCAG 2.2
1.3.1, level A
Same problem in
axe-core td-has-header, axe-core th-has-data-cells
Taught by the skill
fe-a11y-tables Data tables
Since version
0.1.0

Rule file on GitHub

Read it in the terminal

npx github:dawidolko/frontend-skills explain a11y/table-headers