# Table header cells should have visible text.

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

Table header cells (`<th>`) convey the meaning of a column or row to all users, but they are especially important for screen reader users. When a screen reader encounters a data cell, it announces the associated header text so the user understands what the value refers to. If a `<th>` element is empty, the screen reader either announces nothing or says something like "blank," leaving the user without context for every cell in that column or row.

This rule flags `<th>` elements that contain no visible text. An empty header creates a gap in the table's structure that affects anyone who relies on programmatic relationships between headers and data cells, including screen reader users and users of other assistive technologies that parse table semantics.

## Why this matters

Tables use header cells to label their axes. A sighted user can often infer meaning from position or surrounding content, but a screen reader user navigates cell by cell. At each data cell, the screen reader reads the associated header. When that header is empty, the user hears the data value with no label, which makes the table difficult or impossible to understand.

This relates to WCAG success criterion 1.3.1 (Info and Relationships), Level A. That criterion requires information and relationships conveyed through presentation to also be available programmatically. A visible column label that exists only as a visual convention (like a column of checkboxes that "obviously" doesn't need a header) still needs a programmatic label so assistive technology can communicate its purpose.

## How to fix it

The fix depends on why the header is empty.

If the column or row has a meaningful label, add that text to the `<th>`. This is the most straightforward fix.

If the header is intentionally left without visible text (for example, a corner cell in a table that has both row and column headers, or a column that contains action buttons), there are two options:

1. Change the `<th>` to a `<td>`. If the cell genuinely has no header function, it should not be marked up as one.
2. Keep the `<th>` and add a visually hidden label inside it. This preserves the header semantics for screen readers while keeping the visual design unchanged. Use a CSS class that hides text visually but leaves it accessible to assistive technology.

Do not use `aria-label` on a `<th>` as a substitute for visible text unless you have confirmed that the target screen readers support it in table context. Visually hidden text is more broadly supported.

## Examples

### Empty table header (triggers the issue)

```html
<table>
  <tr>
    <th></th>
    <th>Name</th>
    <th>Email</th>
  </tr>
  <tr>
    <td><input type="checkbox"></td>
    <td>Ada Lovelace</td>
    <td>ada@example.com</td>
  </tr>
</table>
```

The first `<th>` is empty. A screen reader user navigating to the checkbox cell will hear no header announced for it.

### Fixed: add visually hidden text to the header

```html
<table>
  <tr>
    <th>
      <span class="visually-hidden">Select</span>
    </th>
    <th>Name</th>
    <th>Email</th>
  </tr>
  <tr>
    <td><input type="checkbox" aria-label="Select Ada Lovelace"></td>
    <td>Ada Lovelace</td>
    <td>ada@example.com</td>
  </tr>
</table>
```

```css
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
```

The header now announces "Select" to screen reader users, and the checkbox itself has an accessible name through `aria-label`.

### Fixed: change the empty header to a regular cell

If the cell has no header function at all (for example, a blank corner cell), change it to a `<td>`:

```html
<table>
  <tr>
    <td></td>
    <th>Q1</th>
    <th>Q2</th>
  </tr>
  <tr>
    <th>Revenue</th>
    <td>$1,000</td>
    <td>$1,200</td>
  </tr>
</table>
```

The empty corner cell is now a `<td>`, so assistive technology does not expect it to label anything.

### Fixed: add visible text to the header

When the column does have a meaningful label, just add it directly:

```html
<table>
  <tr>
    <th>Product</th>
    <th>Price</th>
    <th>Quantity</th>
  </tr>
  <tr>
    <td>Widget</td>
    <td>$9.99</td>
    <td>42</td>
  </tr>
</table>
```

Every `<th>` contains descriptive text, giving screen reader users full context for each data cell.
