# Table headers in a data table must refer to data cells

> Canonical HTML version: https://rocketvalidator.com/accessibility-validation/axe/4.11/th-has-data-cells
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

Screen readers rely on the programmatic relationships between header cells and data cells to help users navigate and understand tabular data. As a user moves through a table, the screen reader announces the relevant column or row header for each data cell, giving context to what would otherwise be just a raw value. When a `<th>` element doesn't properly refer to any data cells, the screen reader either announces incorrect headers, skips the header entirely, or produces confusing output.

This primarily affects **blind** and **deafblind** users who depend on screen readers to interpret table structure. Without correct header associations, these users cannot understand what each piece of data represents or how it relates to other data in the table.

## Related Standards

This rule maps to **WCAG 2.0, 2.1, and 2.2 Success Criterion 1.3.1: Info and Relationships (Level A)**, which requires that information, structure, and relationships conveyed through presentation are programmatically determinable. A table header that doesn't actually reference any data cells fails to convey the intended structural relationship.

It also relates to **Section 508** requirements that row and column headers be identified for data tables, and that markup be used to associate data cells and header cells. The **Trusted Tester** guidelines similarly require that all data cells are programmatically associated with relevant headers.

## How to Fix It

1. **Use the correct `scope` value on `<th>` elements.** If the header applies to a column, use `scope="col"`. If it applies to a row, use `scope="row"`. A common mistake is using `scope="row"` on column headers or vice versa, which means the header doesn't actually point to any data cells in the intended direction.

2. **Ensure every `<th>` has data cells to reference.** A `<th>` that sits in a position where it has no associated `<td>` elements (e.g., an empty row or column) should either be restructured or converted to a `<td>`.

3. **For simple tables, use `scope` on `<th>` elements.** This is the simplest and most reliable approach when there is a single row of column headers and/or a single column of row headers.

4. **For complex tables with multiple levels of headers, use `id` and `headers` attributes.** Assign a unique `id` to each `<th>`, then reference the appropriate `id` values in the `headers` attribute of each `<td>`. Note that `<th>` elements themselves should not use the `headers` attribute to reference other `<th>` elements — keep `headers` on `<td>` elements.

5. **If using `headers` attributes, ensure the referenced `id` values point to elements with text content** that is available to screen readers.

## Examples

### Incorrect: `scope="row"` Used on Column Headers

In this example, the `<th>` elements are column headers, but they are incorrectly scoped to `row`. This means the headers don't reference the data cells below them, so screen readers cannot associate "Last Name," "First Name," or "City" with their respective columns.

```html
<table>
  <caption>Teddy bear collectors</caption>
  <tr>
    <th scope="row">Last Name</th>
    <th scope="row">First Name</th>
    <th scope="row">City</th>
  </tr>
  <tr>
    <td>Phoenix</td>
    <td>Imari</td>
    <td>Henry</td>
  </tr>
  <tr>
    <td>Zeki</td>
    <td>Rome</td>
    <td>Min</td>
  </tr>
</table>
```

### Correct: `scope="col"` Used on Column Headers

The fix is straightforward — change `scope="row"` to `scope="col"` so the headers correctly reference the data cells beneath them.

```html
<table>
  <caption>Teddy bear collectors</caption>
  <tr>
    <th scope="col">Last Name</th>
    <th scope="col">First Name</th>
    <th scope="col">City</th>
  </tr>
  <tr>
    <td>Phoenix</td>
    <td>Imari</td>
    <td>Henry</td>
  </tr>
  <tr>
    <td>Zeki</td>
    <td>Rome</td>
    <td>Min</td>
  </tr>
</table>
```

### Correct: Using Both Column and Row Headers

When a table has headers in both the first row and the first column, use `scope="col"` for the column headers and `scope="row"` for the row headers.

```html
<table>
  <caption>Quarterly sales (in thousands)</caption>
  <tr>
    <td></td>
    <th scope="col">Q1</th>
    <th scope="col">Q2</th>
    <th scope="col">Q3</th>
  </tr>
  <tr>
    <th scope="row">Widgets</th>
    <td>50</td>
    <td>80</td>
    <td>120</td>
  </tr>
  <tr>
    <th scope="row">Gadgets</th>
    <td>30</td>
    <td>45</td>
    <td>60</td>
  </tr>
</table>
```

### Correct: Complex Table Using `id` and `headers`

For tables with multiple levels of headers, use the `id` and `headers` approach to create explicit associations.

```html
<table>
  <caption>Student exam results</caption>
  <tr>
    <td></td>
    <th id="math" scope="col">Math</th>
    <th id="science" scope="col">Science</th>
  </tr>
  <tr>
    <th id="maria" scope="row">Maria</th>
    <td headers="maria math">95</td>
    <td headers="maria science">88</td>
  </tr>
  <tr>
    <th id="james" scope="row">James</th>
    <td headers="james math">72</td>
    <td headers="james science">91</td>
  </tr>
</table>
```

In this example, each `<td>` explicitly references both its row header and column header, so a screen reader can announce something like "Maria, Math, 95" as the user navigates the table.
