HTML Guides for thead
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 cell inside a <thead> has a rowspan value larger than the number of rows left in that <thead>.
The <thead>, <tbody> and <tfoot> elements each form a row group, and a cell's rowspan can't reach from one row group into the next. When a header cell asks for more rows than its <thead> holds, the browser stops the cell at the last header row, as if the extra part of the rowspan weren't there.
This usually comes from a two-level header. The first row has a cell such as "Name" with rowspan="2" next to a grouped heading with colspan="2", and the row of sub-headings that belongs under the group ends up after </thead>, as the first row of the <tbody>. The "Name" cell can't extend into the <tbody>, so that row ends up one cell short, and the validator usually adds a second message about it: "A table row was 2 columns wide, which is less than the column count established by the first row (3)."
Put every header row inside the <thead> so the span has rows to cover. If the header really has a single row, remove the rowspan.
Invalid example
<table>
<thead>
<tr>
<th rowspan="2">Name</th>
<th colspan="2">Score</th>
</tr>
</thead>
<tbody>
<tr>
<th>Round 1</th>
<th>Round 2</th>
</tr>
<tr>
<td>Ana</td>
<td>8</td>
<td>9</td>
</tr>
</tbody>
</table>
Valid example
<table>
<thead>
<tr>
<th rowspan="2">Name</th>
<th colspan="2">Score</th>
</tr>
<tr>
<th>Round 1</th>
<th>Round 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ana</td>
<td>8</td>
<td>9</td>
</tr>
</tbody>
</table>
The <thead> element is exposed with the ARIA role rowgroup by default, so role="rowgroup" on it only states its built-in role a second time.
Every HTML element maps to a default ARIA role, and the table sections share one: <thead>, <tbody> and <tfoot> are all row groups. Browsers already tell screen readers that the rows inside a <thead> form a group at the top of the table. An explicit role="rowgroup" changes nothing users can notice, which is why the validator reports it as a warning rather than an error.
You'll often see this in tables generated by page builders, table plugins or content copied from online editors, which tend to stamp a role onto every table part. The rowgroup role exists for tables made from generic elements such as <div>, where the markup gives assistive technology no clue about how the rows are grouped. A real <thead> already carries that meaning, so delete the attribute. While you're there, check the rest of the table. In a native <table>, a role on <tr>, <th> or <td> is reported as an error, so remove those in the same pass.
Invalid example
<table>
<thead role="rowgroup">
<tr>
<th>Feature</th>
<th>Included</th>
</tr>
</thead>
<tbody>
<tr>
<td>Backups</td>
<td>Yes</td>
</tr>
<tr>
<td>Priority support</td>
<td>No</td>
</tr>
</tbody>
</table>
Valid example
<table>
<thead>
<tr>
<th>Feature</th>
<th>Included</th>
</tr>
</thead>
<tbody>
<tr>
<td>Backups</td>
<td>Yes</td>
</tr>
<tr>
<td>Priority support</td>
<td>No</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