Accessible Tables
Accessible data tables with proper semantic markup, headers, captions, and scope attributes to ensure screen reader users can understand table relationships and navigate efficiently.
Key Accessibility Features
- Caption Element: Describes the table's purpose for all users
- Table Headers: Uses <th> elements with scope attributes
- Semantic Structure: Proper <thead>, <tbody>, and <tfoot> elements
- Scope Attributes: Clarifies header relationships (col, row, colgroup, rowgroup)
- Responsive Design: Horizontal scroll on small screens to preserve table structure
Basic Data Table
Simple table with column headers and a caption:
| Name | Department | Phone | |
|---|---|---|---|
| Jane Smith | Engineering | jane.smith@example.com | 555-0101 |
| John Doe | Design | john.doe@example.com | 555-0102 |
| Maria Garcia | Marketing | maria.garcia@example.com | 555-0103 |
HTML
<table class="ads-table">
<caption>Employee Directory</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Department</th>
<th scope="col">Email</th>
<th scope="col">Phone</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jane Smith</td>
<td>Engineering</td>
<td>jane.smith@example.com</td>
<td>555-0101</td>
</tr>
<tr>
<td>John Doe</td>
<td>Design</td>
<td>john.doe@example.com</td>
<td>555-0102</td>
</tr>
</tbody>
</table>
CSS
.ads-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.ads-table caption {
text-align: left;
font-weight: 500;
padding: 1rem;
background: var(--ads-bg-secondary);
border: 1px solid var(--ads-border-color);
border-bottom: none;
}
.ads-table thead {
background: var(--ads-bg-secondary);
}
.ads-table th {
padding: 0.75rem;
text-align: left;
border: 1px solid var(--ads-border-color);
font-weight: 600;
}
.ads-table td {
padding: 0.75rem;
border: 1px solid var(--ads-border-color);
}
.ads-table tbody tr:nth-child(even) {
background: var(--ads-bg-secondary);
}
/* Responsive wrapper */
.ads-table-wrapper {
overflow-x: auto;
}
@media (max-width: 768px) {
.ads-table {
font-size: 0.875rem;
}
.ads-table th,
.ads-table td {
padding: 0.5rem;
}
}
Table with Row Headers
When the first column contains header information for each row, use scope="row":
| Region | January | February | March |
|---|---|---|---|
| North | $125,000 | $138,000 | $142,000 |
| South | $98,000 | $105,000 | $112,000 |
| East | $156,000 | $162,000 | $178,000 |
| Total | $379,000 | $405,000 | $432,000 |
HTML
<table class="ads-table">
<caption>Q1 Sales by Region</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">January</th>
<th scope="col">February</th>
<th scope="col">March</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">North</th>
<td>$125,000</td>
<td>$138,000</td>
<td>$142,000</td>
</tr>
<tr>
<th scope="row">South</th>
<td>$98,000</td>
<td>$105,000</td>
<td>$112,000</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">Total</th>
<td>$379,000</td>
<td>$405,000</td>
<td>$432,000</td>
</tr>
</tfoot>
</table>
Best Practices
- Always include a <caption> element to describe the table's purpose
- Use <th> elements for all header cells, not <td>
- Add scope="col" for column headers and scope="row" for row headers
- Use <thead>, <tbody>, and <tfoot> to group related rows
- Provide adequate color contrast (4.5:1 minimum) for all text
- Don't use tables for layout - only for tabular data
- On mobile, wrap tables in a scrollable container rather than hiding columns
- For complex tables, consider using headers attribute with id values