Purpose
Tables present structured data in rows and columns. Accessible tables use proper header associations so screen readers can announce the column and/or row context for each data cell.
Usage Guidance
- Use tables only for tabular data, not for layout
- Every table needs a
<caption>or an associated heading - Use
<th>for header cells and<td>for data cells - Use
scope="col"orscope="row"on all header cells
Keyboard Support
Tables are not interactive by default. If a table supports sorting, the sort control (a <button> in the header) should follow button keyboard behavior. If cells are focusable, a grid pattern may be appropriate.
Screen Reader Behavior
Screen readers announce the table caption, then read each cell with its associated headers. In a simple table with <th scope="col">, the column header is announced before each cell value.
ARIA Requirements
- Use
<caption>for the table name (preferred overaria-label) scope="col"on column headersscope="row"on row headersaria-sort="ascending|descending|none"on sortable column headersrole="grid"only when cells are interactive and need arrow-key navigation
Common Mistakes
- Using
<td>instead of<th>for header cells - Missing
scopeattribute on header cells in complex tables - Using layout tables without
role="presentation" - Adding
aria-labelto cells instead of using header associations
Live Example
| Issue ID | WCAG Criterion | Severity | Status |
|---|---|---|---|
| A-001 | 1.1.1 Non-text Content | Critical | Open |
| A-002 | 1.4.3 Contrast (Minimum) | High | In Progress |
| A-003 | 2.1.1 Keyboard | Critical | Resolved |
| A-004 | 4.1.2 Name, Role, Value | Medium | Open |
Code Example
<div class="table-wrapper">
<table>
<caption>Audit Findings Summary</caption>
<thead>
<tr>
<th scope="col">Issue</th>
<th scope="col">Criterion</th>
<th scope="col">Severity</th>
<th scope="col" aria-sort="none">
<button type="button">Status</button>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Missing alt text</td>
<td>1.1.1</td>
<td>Critical</td>
<td>Open</td>
</tr>
</tbody>
</table>
</div>
.table-wrapper { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th { background: #0d2b4e; color: #fff; padding: 0.75rem 1rem; text-align: left; }
td { padding: 0.75rem 1rem; border-bottom: 1px solid #e8ecf0; }
th[aria-sort="ascending"]::after { content: " ↑"; }
th[aria-sort="descending"]::after { content: " ↓"; }