Build data tables that screen readers can navigate logically by using semantic markup, proper headers, and captions.
What it is
An accessible table is an HTML structure that conveys relationships between rows and columns to assistive technologies. The core mental model is "association": every cell must know which header describes it. Key terms include <table>, <caption>, <thead>, <tbody>, <th>, and the scope attribute.
Why it matters
- Contextual Navigation: Screen readers announce column headers when moving through cells, preventing confusion in wide datasets.
- Logical Reading Order: Proper structure ensures content flows top-to-bottom, left-to-right as intended.
- Compliance: Meets WCAG 1.3.1 (Info and Relationships) and 2.4.6 (Headings and Labels).
- Usability for All: Benefits users with cognitive disabilities who rely on clear visual and auditory cues.
Syntax or steps
- Wrap the table in a
<figure>if it has a caption, or use<caption>directly inside<table>. - Use
<thead>for header rows and<tbody>for data rows. - Replace
<td>with<th>for all header cells. - Add
scope="col"to column headers andscope="row"to row headers. - Ensure each data cell (
<td>) is associated with its corresponding headers via position or explicit IDs.
Example
<table>
<caption>Quarterly Sales Report</caption>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Q1 Revenue</th>
<th scope="col">Q2 Revenue</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Widget A</th>
<td>$500</td>
<td>$750</td>
</tr>
<tr>
<th scope="row">Widget B</th>
<td>$300</td>
<td>$400</td>
</tr>
</tbody>
</table>
Explanation: The <caption> provides a title. Column headers use scope="col" to indicate they describe the vertical stack below them. Row headers (Product names) use scope="row" to describe the horizontal line of data. When a screen reader focuses on "$500", it announces "Widget A, Q1 Revenue, $500".
Common mistakes
- Using Tables for Layout: Never use
<table>for page positioning; this creates a confusing linear reading order. - Missing Scope Attributes: Without
scope, screen readers may guess associations incorrectly, especially in complex tables. - Empty Header Cells: Every column must have a header. If a column has no label, use
<th> </th>or hide it visually but keep it accessible. - Complex Merged Cells: Avoid
colspanandrowspanunless necessary. If used, you must explicitly link cells usingheadersattributes with unique IDs.
When to use it
| Scenario | Recommended Approach |
|---|---|
| Simple Data Grid | Standard <table> with scope |
| Layout/Design | CSS Flexbox or Grid (never tables) |
| Complex Hierarchies | Consider <dl> lists or simplified tables |
Practice
Guided Exercise: Create a table listing three books with columns: Title, Author, Year. Ensure the first column uses scope="row" and others use scope="col".
Challenge: Add a summary row at the bottom showing total pages. How would you mark this up accessibly?
Hint: Use <tfoot> and ensure the summary cells are clearly labeled or associated with their respective columns.
Quick check
Question: What attribute tells a screen reader that a <th> applies to the entire column beneath it?
Answer: scope="col"
Summary
Accessible tables rely on semantic structure and explicit header associations. By using <caption>, <thead>, and correct scope values, you ensure data remains understandable regardless of how it is consumed.