Use the scope attribute correctly in data tables
Content available in English only.
Accessibility isn’t just about avoiding violations — it’s about ensuring that everyone can use your product with confidence. This guide explains each rule’s intent, highlights common issues, and shows how to fix them according to WCAG and the European Accessibility Act (EAA).
These guidelines do not replace the official WCAG standards. They’re concise, developer-focused notes to help you identify and fix issues effectively.
If it labels a row or column, it belongs in a <th> with the correct scope.
Why this matters and how to fix it
Why this matters
Screen readers rely on table header associations to announce column and row context while navigating cells. If `scope` is missing, incorrect, or placed on the wrong elements, users will not hear which header applies to which data cell. This makes tables difficult or impossible to understand for non-visual users.
How to fix this issue
Use `scope="col"` on column header `<th>` elements and `scope="row"` on row header `<th>` elements. Only apply `scope` to `<th>`, never to `<td>`. For complex tables (e.g., with nested headers), consider using explicit `headers` / `id` associations instead of relying on `scope` alone.
Developer guidance
Most accessibility issues come from developers styling `<td>` elements to 'look like headers' instead of using `<th>`. In your component library, create a TableHeader component that always outputs `<th>` with the correct scope automatically. Avoid using scope="colgroup" or scope="rowgroup" unless you understand group semantics.
Code examples
Incorrect Implementation
<table>
<tr><td scope="col">Product</td><td scope="col">Price</td></tr>
<tr><td>Apple</td><td>$1</td></tr>
</table>Correct Implementation
<table>
<tr><th scope="col">Product</th><th scope="col">Price</th></tr>
<tr><td>Apple</td><td>$1</td></tr>
</table>Real-World Examples
Before
<table>
<tr class="header-row"><td>Name</td><td>Hours</td></tr>
<tr><td>Alex</td><td>40</td></tr>
</table>
<!-- Looks fine visually, but screen reader users do not hear column labels. -->After
<table>
<tr><th scope="col">Name</th><th scope="col">Hours</th></tr>
<tr><td>Alex</td><td>40</td></tr>
</table>
<!-- Screen reader: 'Hours, 40' → clear meaning. -->CSS Example (Guidance)
/* Don't style <td> to 'look' like a header. Use <th> instead. */
th {
font-weight: 600;
text-align: left;
}Manual testing
- 1. Identify all <table> elements on the page.
- 2. Inspect header cells:
- • Expected: <th scope="col"> for column headers
- • Expected: <th scope="row"> for row headers (first cell in each row)
- 3. Confirm that no <td> elements contain the scope attribute.
- 4. Turn on a screen reader and use table navigation commands (e.g., NVDA: Ctrl+Alt+Arrow Keys).
- • Expected: When moving across cells, the screen reader announces the correct header before the cell value.
- 5. For complex tables with multiple levels of headers:
- • Verify correct associations using `headers` + `id` instead of relying solely on `scope`.
Trusted by organizations across Europe working toward WCAG and EAA conformance