Difficulty: Intermediate
Making tables accessible means ensuring that users who rely on screen readers and other assistive technologies can understand the structure and relationships within your tabular data. A sighted user can visually scan a table and understand which header applies to which data cell, but a screen reader user navigates tables cell by cell and depends on proper markup to announce the relevant headers for each cell.
The <caption> element provides a title or description for the table. It must be the first child of the <table> element. Screen readers announce the caption before the user enters the table, giving them context about what data the table contains. Think of it as a heading for the table. You can visually hide the caption with CSS if you want screen-reader-only context, but never omit it entirely from accessible tables.
The scope attribute on <th> elements explicitly declares whether a header applies to a column (scope="col"), a row (scope="row"), a column group (scope="colgroup"), or a row group (scope="rowgroup"). While modern screen readers can often infer scope for simple tables, adding it explicitly removes ambiguity, especially in complex tables with both row and column headers.
The headers attribute on <td> elements provides an explicit association between a data cell and its header cells by referencing the id values of the relevant <th> elements. This is the most robust approach for complex tables where the automatic header-inference algorithms of assistive technologies might produce incorrect results. For simple tables, scope is sufficient; reserve the headers attribute for tables with multi-level headers or irregular spanning.
The summary attribute on the <table> element was used in older HTML versions to provide a description of the table's structure for screen reader users. It has been deprecated in HTML5. Instead, use <caption> for a visible description, or provide a paragraph of descriptive text before the table and associate it using aria-describedby. For complex data tables, a brief description of how the table is organized (such as 'rows represent departments, columns represent quarters') helps users form a mental model before navigating the cells.
<table>
<caption>Q1 2026 Sales Report by Region</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Revenue</th>
<th scope="col">Growth</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">North</th>
<td>$1.2M</td>
<td>+8%</td>
</tr>
<tr>
<th scope="row">South</th>
<td>$900K</td>
<td>+12%</td>
</tr>
<tr>
<th scope="row">East</th>
<td>$1.5M</td>
<td>+5%</td>
</tr>
</tbody>
</table>
The <caption> gives the table a descriptive title. Column headers use scope="col" and row headers use scope="row", making it clear which headers apply to which direction. A screen reader would announce 'Region: North, Revenue: $1.2M' as the user navigates.
<table>
<caption>Employee Skills Matrix</caption>
<thead>
<tr>
<th id="name">Name</th>
<th id="html">HTML</th>
<th id="css">CSS</th>
<th id="js">JavaScript</th>
</tr>
</thead>
<tbody>
<tr>
<th id="alice" headers="name">Alice</th>
<td headers="alice html">Expert</td>
<td headers="alice css">Intermediate</td>
<td headers="alice js">Beginner</td>
</tr>
<tr>
<th id="bob" headers="name">Bob</th>
<td headers="bob html">Intermediate</td>
<td headers="bob css">Expert</td>
<td headers="bob js">Expert</td>
</tr>
</tbody>
</table>
Each <td> has a headers attribute listing the id values of its associated header cells, separated by spaces. For Alice's HTML skill level, headers="alice html" tells the screen reader that this cell is at the intersection of the 'Alice' row and 'HTML' column.
<p id="table-desc">
The following table compares pricing plans.
Rows represent features and columns represent plan tiers.
</p>
<table aria-describedby="table-desc">
<caption>Pricing Plan Comparison</caption>
<thead>
<tr>
<th scope="col">Feature</th>
<th scope="col">Free</th>
<th scope="col">Pro</th>
<th scope="col">Enterprise</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Storage</th>
<td>5 GB</td>
<td>100 GB</td>
<td>Unlimited</td>
</tr>
<tr>
<th scope="row">Support</th>
<td>Community</td>
<td>Email</td>
<td>24/7 Phone</td>
</tr>
<tr>
<th scope="row">Users</th>
<td>1</td>
<td>10</td>
<td>Unlimited</td>
</tr>
</tbody>
</table>
The aria-describedby attribute links the table to an external paragraph that explains its structure. This is the modern replacement for the deprecated summary attribute. Combined with <caption>, it gives screen reader users both a title and a structural description.
scope, caption, summary, headers Attribute, Accessible Tables