Learn how to use the <th> element and its scope attribute to create accessible data tables that screen readers can interpret correctly.
What it is
The <th> (table header) element defines a cell as a header for a group of table cells. Unlike standard data cells (<td>), headers are typically bold and centered by default, but their primary purpose is semantic: they establish relationships between rows and columns. The scope attribute explicitly tells assistive technologies which data cells a header applies to, ensuring users with visual impairments understand the context of each value.
Why it matters
- Screen Reader Navigation: When a user moves through a table, the screen reader announces the column or row header before reading the cell content, providing immediate context.
- Accessibility Compliance: Proper use of
scopehelps meet WCAG (Web Content Accessibility Guidelines) standards for meaningful sequence and programmatic determination of structure. - Clarity in Complex Tables: In tables with merged cells or multiple header levels, explicit scoping prevents ambiguity about which header belongs to which data point.
- Semantic HTML: It separates presentation from meaning, allowing CSS to handle styling while HTML handles structure.
Syntax or steps
To create an accessible table header, replace <td> with <th> inside the first row of your table body or within a <thead>. Add the scope attribute with one of two values:
scope="col": Indicates the header applies to all cells in the current column.scope="row": Indicates the header applies to all cells in the current row.
Example
<table>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Price</th>
<th scope="col">Stock</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Laptop</th>
<td>$1200</td>
<td>5</td>
</tr>
<tr>
<th scope="row">Mouse</th>
<td>$25</td>
<td>100</td>
</tr>
</tbody>
</table>
In this example, the top row uses scope="col" because "Product," "Price," and "Stock" describe the vertical columns below them. The first cell of each data row uses scope="row" because "Laptop" and "Mouse" describe the horizontal data points next to them. A screen reader will announce "Column Header: Price, Row Header: Laptop, Value: $1200."
Common mistakes
- Using
<td>for headers: Styling a<td>to look like a header does not make it accessible. Always use<th>. - Omitting
scope: While some browsers infer scope, explicit declaration is required for reliable accessibility across different assistive technologies. - Misusing
headersattribute: Do not confusescopeon<th>with theheadersattribute on<td>. Usescopefor simple tables; useheadersonly when complex merging makesscopeinsufficient. - Empty headers: Every
<th>must contain text describing the data. Never leave a header cell empty.
When to use it
| Scenario | Recommended Approach |
|---|---|
| Simple grid (rows and columns) | Use <th> with scope="col" and scope="row". |
| Complex layout (merged cells) | Use <th> with id attributes and link data cells using headers="id". |
| Presentation-only table | Avoid tables entirely; use CSS Grid or Flexbox instead. |
Practice
Guided Exercise: Create a table listing three students and their grades in Math and Science. Ensure the student names act as row headers and the subject names act as column headers.
Challenge: Modify your table so that the "Math" and "Science" headers span two columns each (e.g., Midterm and Final). Hint: You may need to switch from scope to the headers attribute on the data cells if the structure becomes too irregular for simple scoping.
Quick check
Question: If a header cell describes the entire row it is in, what value should the scope attribute have?
Answer: scope="row"
Summary
The <th> element combined with the scope attribute is essential for creating inclusive web pages. By explicitly defining the relationship between headers and data, you ensure that all users, regardless of ability, can navigate and understand tabular information effectively.