HTML Guides for header cell
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
A <th> gets its ARIA role from the table it belongs to, so it can't have a role attribute when that <table> has no role, or has role="table", role="grid" or role="treegrid".
In those tables the browser works out what each header cell is. A <th> inside <thead>, or in a row of headers above the data, is exposed as columnheader. A <th> at the start of a data row is exposed as rowheader. When the layout is ambiguous, scope="col" or scope="row" states the direction. Screen readers use these roles to announce the right header as the user moves from one data cell to the next.
The ARIA in HTML specification allows no role value at all on a <th> in these tables, so the validator reports this error whatever the value is. With role="columnheader" or role="rowheader" you also get a warning that the role is unnecessary for the element, and deleting the attribute clears both messages. Any other value replaces the header role. A sortable header marked role="button", or a header marked role="presentation", stops being a header, and screen readers no longer read its text before the cells it labels.
To make a column sortable, keep the <th> as it is, put a <button> inside it, and set aria-sort on the <th> for the column that is currently sorted. If the table exists only for layout, put role="presentation" on the <table> instead. A table with a role other than table, grid or treegrid loses its table semantics, and this rule no longer applies to its cells.
Table plugins and page builders often add a role to every part of a table: role="table" on the <table>, role="rowgroup" on <thead> and <tbody>, role="row" on each <tr> and role="cell" on each <td>. The validator reports all of them, with errors for the roles on <tr> and <td> and warnings for the rest, so remove them in the same change.
Invalid example
<table>
<thead>
<tr>
<th scope="col" role="columnheader">Product</th>
<th scope="col" role="button" tabindex="0">Price</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" role="rowheader">Desk lamp</th>
<td>$45</td>
</tr>
<tr>
<th scope="row" role="rowheader">Notebook</th>
<td>$8</td>
</tr>
</tbody>
</table>
Valid example
<table>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col" aria-sort="descending">
<button type="button">Price</button>
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Desk lamp</th>
<td>$45</td>
</tr>
<tr>
<th scope="row">Notebook</th>
<td>$8</td>
</tr>
</tbody>
</table>
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.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries