# All cells in a table using headers attribute must reference valid header IDs.

> Canonical HTML version: https://rocketvalidator.com/accessibility-validation/accesslint/0.16/adaptable/td-headers-attr
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

When a table cell has a `headers` attribute, each value in that attribute must be the `id` of another cell within the same table. If any referenced ID does not exist, is misspelled, points to an element outside the table, or points back to the cell itself, the explicit association between data and headers breaks. Screen readers rely on these associations to announce column and row headers as users navigate through table cells, so broken references leave users without the context they need to understand the data.

This issue affects screen reader users most directly. When navigating a complex data table, a screen reader reads out the associated header for each cell. If the `headers` attribute references an ID that doesn't exist, the screen reader either announces nothing or falls back to a less accurate heuristic, making the table difficult or impossible to interpret.

## How this relates to WCAG

This rule maps to WCAG 2 Success Criterion 1.3.1 (Info and Relationships) at Level A. That criterion requires that information and relationships conveyed through visual presentation are also available programmatically. In the context of tables, this means data cells must be programmatically associated with their headers. A `headers` attribute that references nonexistent or incorrect IDs fails to establish that programmatic relationship.

## How to fix it

1. Check every `headers` attribute value on `<td>` and `<th>` elements in the table.
2. Confirm that each space-separated token in the `headers` attribute matches the `id` of a `<th>` (or `<td>` acting as a header) within the same `<table>` element.
3. Fix any typos or mismatches between `headers` values and `id` values.
4. Make sure no cell references its own `id` in its `headers` attribute.
5. Make sure all referenced `id` values are unique within the page.

For simple tables with a single row of column headers or a single column of row headers, the `scope` attribute on `<th>` elements is a simpler and less error-prone approach. Reserve `headers` and `id` for complex tables where cells relate to multiple headers or where the header structure is irregular.

## Examples

### Incorrect: `headers` references a nonexistent ID

The `headers` attribute on the data cells references `"col1"` and `"col2"`, but the `<th>` elements have IDs `"header1"` and `"header2"`. The references are broken.

```html
<table>
  <thead>
    <tr>
      <th id="header1">Name</th>
      <th id="header2">Score</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td headers="col1">Alice</td>
      <td headers="col2">92</td>
    </tr>
  </tbody>
</table>
```

### Incorrect: cell references its own ID

A cell must not list its own `id` in its `headers` attribute. Browsers ignore self-references when assigning headers, so the association is lost.

```html
<table>
  <thead>
    <tr>
      <th id="name">Name</th>
      <th id="score">Score</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td id="cell1" headers="cell1 name">Alice</td>
      <td headers="score">92</td>
    </tr>
  </tbody>
</table>
```

### Correct: `headers` references valid IDs in the same table

Each `headers` value matches an `id` on a `<th>` in the same table.

```html
<table>
  <thead>
    <tr>
      <th id="name">Name</th>
      <th id="score">Score</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td headers="name">Alice</td>
      <td headers="score">92</td>
    </tr>
  </tbody>
</table>
```

### Correct: multiple headers on a cell spanning columns

When a cell spans multiple columns, list all relevant header IDs separated by spaces.

```html
<table>
  <thead>
    <tr>
      <th id="projects">Projects</th>
      <th id="exams">Exams</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td colspan="2" headers="projects exams">Combined: 85%</td>
    </tr>
  </tbody>
</table>
```

### Correct: simple table using `scope` instead

For straightforward tables, `scope` is easier to maintain and less prone to ID mismatch errors.

```html
<table>
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Score</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>92</td>
    </tr>
  </tbody>
</table>
```
