Table Accessibility Basics — The scope Attribute
A table that looks clear visually may be confusing to a screen reader user navigating cell by cell. The scope attribute tells assistive technology which cells a header applies to — column or row.
Why tables need accessibility help
Section titled “Why tables need accessibility help”A sighted user reads a table by glancing at the column headers, then reading down. A screen reader announces each cell individually. Without explicit connections, the reader may announce “Monday, 9:00 AM” without explaining that “Monday” is a day and “9:00 AM” is a time — because those column headers are not linked to the data cells.
The scope attribute makes that connection explicit.
The scope attribute
Section titled “The scope attribute”scope is added to <th> elements. It takes two main values:
| Value | Meaning |
|---|---|
scope="col" | This header applies to all cells below it in the same column |
scope="row" | This header applies to all cells to the right of it in the same row |
Column headers
Section titled “Column headers”Most tables have column headers in the first row. Use scope="col" on each <th>:
<table> <thead> <tr> <th scope="col">Day</th> <th scope="col">Time</th> <th scope="col">Location</th> </tr> </thead> <tbody> <tr> <td>Monday</td> <td>9:00 AM</td> <td>Room 12</td> </tr> <tr> <td>Wednesday</td> <td>11:00 AM</td> <td>Room 7</td> </tr> </tbody></table>A screen reader navigating this table now knows that “Monday” belongs under the “Day” header, “9:00 AM” belongs under “Time”, and so on.
Row headers
Section titled “Row headers”Some tables have headers in the first column — each row has a label. Use scope="row" on those <th> cells:
<table> <tbody> <tr> <th scope="row">Easy</th> <td>Under 5 miles</td> <td>Minimal elevation change</td> </tr> <tr> <th scope="row">Moderate</th> <td>5–10 miles</td> <td>Some elevation gain</td> </tr> <tr> <th scope="row">Strenuous</th> <td>Over 10 miles</td> <td>Significant elevation gain</td> </tr> </tbody></table>Here “Easy”, “Moderate”, and “Strenuous” are row headers — they label what the rest of each row describes.
A complete table with both
Section titled “A complete table with both”A table can have both column and row headers:
<table> <thead> <tr> <th scope="col">Rating</th> <th scope="col">Distance</th> <th scope="col">Elevation</th> </tr> </thead> <tbody> <tr> <th scope="row">Easy</th> <td>Under 5 miles</td> <td>Minimal</td> </tr> <tr> <th scope="row">Moderate</th> <td>5–10 miles</td> <td>Some gain</td> </tr> </tbody></table>The column headers describe what each column contains. The row headers label each row. Together they give assistive technology everything it needs to describe any cell in context.
What scope does not change
Section titled “What scope does not change”scope has no visual effect whatsoever. The table looks identical with or without it. It is purely for assistive technologies and semantic correctness — but that is exactly why it matters.
Exercise
Section titled “Exercise”Open index.html in VS Code.
Find your <table> from the previous lessons. Add scope attributes to your <th> elements.
- For each
<th>in your header row inside<thead>, addscope="col". - If any column in your table uses
<th>as a row label (first column), addscope="row"to those cells.
Before:
<thead> <tr> <th>Rating</th> <th>Typical Distance</th> </tr></thead>After:
<thead> <tr> <th scope="col">Rating</th> <th scope="col">Typical Distance</th> </tr></thead>- Save the file. The page looks the same — but the table is now properly accessible.
scope="col"on a<th>tells screen readers that this header applies to the column below it.scope="row"on a<th>tells screen readers that this header applies to the row to its right.scopehas no visual effect — it only helps assistive technologies understand the table’s structure.- Use
scopeon every<th>element in your tables as standard practice.