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.
Low because missing table headers create context loss for screen reader users in tabular data without blocking access to the underlying cell values.
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.
ID: accessibility-basics.forms-interactive.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, and scope="col" or scope="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 have scope attribute. 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>