Skip to content

Table headers need text

  • Rule: axe/empty-table-header
  • Checks: Table header text should not be empty
  • WCAG: none; a best practice, not a WCAG success criterion
  • Fix class: content: A fix needs authored text.
  • People who use a screen reader or a braille display, most of them blind.

A header cell is empty, such as the corner cell above a column of row headers

Section titled “A header cell is empty, such as the corner cell above a column of row headers”

Make it a <td> if it heads nothing. If it does head a row or column, give it text that names what that row or column holds.

<table>
<caption>Opening hours</caption>
<tr>
<td></td>
<th scope="col">Opens</th>
<th scope="col">Closes</th>
</tr>
<tr>
<th scope="row">Monday</th>
<td>9:00</td>
<td>17:00</td>
</tr>
</table>

A header holds only an icon or a control, or a column of actions has no header text

Section titled “A header holds only an icon or a control, or a column of actions has no header text”

Put text inside the cell that names the column, hidden visually if the design calls for it. An icon needs a text alternative, such as an <img> with alt. Decide the words from what the column holds.

<table>
<caption>Team members</caption>
<tr>
<th scope="col">Name</th>
<th scope="col"><span class="visually-hidden">Actions</span></th>
</tr>
<tr>
<td>Ana Lima</td>
<td><button type="button">Remove</button></td>
</tr>
</table>

axe still reports it, because it looks for text inside the cell. Put the text in the cell, hidden visually if needed.

Whitespace is not text, so the header still names nothing and axe still reports it.

axe-core’s documentation of the rule

CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.