Loading...
Master every aspect of building tables that work for everyone
The foundation of accessible tables is using native HTML elements. Screen readers rely on semantic markup to understand table structure, announce row/column counts, and enable navigation between cells.
Users can navigate between cells using arrow keys, jump to headers, and understand relationships between data.
Native table elements support keyboard navigation out of the box, with no JavaScript required.
Screen readers announce "Table with 5 rows and 4 columns" and read column headers with each cell.
❌ VoiceOver reads: "Alice Johnson, Frontend Developer, Engineering" — no context about columns!
<!-- ❌ INACCESSIBLE -->
<div class="table">
<div class="row header">
<div class="cell">Name</div>
<div class="cell">Role</div>
</div>
<div class="row">
<div class="cell">Alice</div>
<div class="cell">Developer</div>
</div>
</div>
<!-- Screen reader has NO idea this is tabular data --><!-- ✅ ACCESSIBLE -->
<table>
<caption>Employee Directory</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Role</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>Developer</td>
</tr>
</tbody>
</table>| Element | Purpose | Screen Reader Benefit |
|---|---|---|
| <table> | Container for tabular data | Announces "Table with X rows, Y columns" |
| <caption> | Title/description of the table | Read first, gives users context before navigating |
| <thead> | Groups header rows | Distinguishes headers from data rows |
| <th scope="col"> | Column header cell | Read with each cell in that column |
| <th scope="row"> | Row header cell | Read with each cell in that row |
| <tbody> | Groups data rows | Separates data from headers and footers |
className="sr-only" to hide it visually while keeping it accessible. Never skip the caption—it's crucial for screen reader users to understand what the table contains.