Data tables include semantic markup
Why it matters
A data table with <td> elements in the header row instead of <th> elements loses its entire semantic structure for screen readers. Without <th scope="col"> headers, screen readers announce cell content without any column or row context — a user navigating a pricing comparison table hears '49, 99, 199' with no indication of which tier each price belongs to. WCAG 2.2 SC 1.3.1 (Info and Relationships) requires that relationships conveyed visually (like column header to data cell) be available programmatically. Section 508 2018 Refresh 502.3.5 covers information and relationships. Complex tables with both row and column headers require scope="col", scope="row", and in multi-header scenarios, headers attributes on data cells referencing multiple id values.
Severity rationale
Low because missing table headers create context loss for screen reader users in tabular data without blocking access to the underlying cell values.
Remediation
Replace <td> header rows with <th> elements carrying scope attributes. Add a <caption> describing the table's content. In React/TSX components that render tables:
<table>
<caption>Monthly subscription pricing by plan</caption>
<thead>
<tr>
<th scope="col">Plan</th>
<th scope="col">Price / month</th>
<th scope="col">Storage</th>
<th scope="col">Users</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Starter</th> {/* Row header */}
<td>$9</td>
<td>10 GB</td>
<td>3</td>
</tr>
<tr>
<th scope="row">Pro</th>
<td>$29</td>
<td>100 GB</td>
<td>Unlimited</td>
</tr>
</tbody>
</table>
For complex tables where a cell is described by both a column and a row header, use id on each header and headers on the data cell:
<th id="q1" scope="col">Q1</th>
<th id="revenue" scope="row">Revenue</th>
<td headers="q1 revenue">$120,000</td>
Search for <table> elements in your component library. Ensure data tables (src/components/ui/table.tsx or similar) emit <th> with scope in <thead> rows.
Detection
-
ID:
data-table-markup -
Severity:
low -
What to look for: Enumerate every relevant item. Check for
<table>elements in the application. Tables should include<thead>for headers,<tbody>for body rows, andscope="col"orscope="row"on header cells. Caption or visible title should describe the table. Complex tables should have a summary. -
Pass criteria: Tables use proper semantic markup with
<thead>,<tbody>, and<tfoot>(if present). Header cells havescopeattribute. Tables have a caption or title. -
Fail criteria: Tables use plain
<td>for headers. No header scope attributes. No caption or title. -
Skip (N/A) when: The application has no data tables (layout tables are exempt, though CSS layout is preferred).
-
Detail on fail: Describe table markup issues. Example:
"Results table uses <td> for header row instead of <th>. No scope attributes on headers. No caption describing the table." -
Remediation: Use semantic table markup:
<table> <caption>2024 Sales by Quarter</caption> <thead> <tr> <th scope="col">Quarter</th> <th scope="col">Revenue</th> <th scope="col">Growth</th> </tr> </thead> <tbody> <tr> <th scope="row">Q1</th> <td>$100K</td> <td>5%</td> </tr> </tbody> </table>
External references
- wcag:2.2 · 1.3.1 — Info and Relationships
- section-508:2018-refresh · 502.3.5 — Object Information and Relationships
Taxons
History
- 2026-04-18·v1.0.0·Initial import from accessibility-basics·automated