Skip to main content
Accessibility AccessLint 0.16

Table header cells should have visible text.

About this AccessLint rule

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)

<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

<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>
.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>:

<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:

<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.

Detect accessibility issues automatically

Rocket Validator scans your site with complementary accessibility engines, helping teams find issues across every page.

Help us improve our guides

Was this guide helpful?
๐ŸŒ Trusted by teams worldwide

Validate at scale.
Ship accessible websites, faster.

Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.

Scheduled Reports
API Access
Open Source Standards
$7 / 7 days

Pro Trial

Full Pro access. Cancel anytime.

Start Pro Trial โ†’

Join teams across 40+ countries