HTML Guides for scope
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.
The scope attribute tells browsers and assistive technologies how a header cell relates to the data cells around it. Its valid values are col, row, colgroup, and rowgroup. In older versions of HTML, scope was permitted on <td> elements, but the current HTML Living Standard restricts it to <th> elements only. When the W3C validator encounters scope on a <td>, it flags it as obsolete.
This matters for several reasons. First, if a cell acts as a header for other cells, it should be marked up as a <th>, not a <td>. Using <td scope="row"> sends conflicting signals — the element says “I’m a data cell” while the attribute says “I’m a header for this row.” Second, screen readers rely on proper <th> elements with scope to announce table relationships. A <td> with scope may not be interpreted correctly, making the table harder to navigate for users of assistive technology. Third, using obsolete attributes means your markup doesn’t conform to current standards, which could lead to unpredictable behavior in future browsers.
The fix is straightforward: if a cell has a scope attribute, it’s acting as a header and should be a <th> element. Change the <td> to <th> and keep the scope attribute. If the cell is genuinely a data cell and not a header, remove the scope attribute entirely and leave it as a <td>.
Examples
Incorrect: scope on a <td> element
<table>
<tr>
<td scope="col">Name</td>
<td scope="col">Role</td>
</tr>
<tr>
<td scope="row">Alice</td>
<td>Engineer</td>
</tr>
</table>
This triggers the validation error because scope is used on <td> elements. The first row contains column headers and the first column contains row headers, yet they are all marked as data cells.
Correct: scope on <th> elements
<table>
<tr>
<th scope="col">Name</th>
<th scope="col">Role</th>
</tr>
<tr>
<th scope="row">Alice</th>
<td>Engineer</td>
</tr>
</table>
Now the header cells are correctly marked with <th>, and the scope attribute is valid on each one. Screen readers can properly associate “Alice” with “Engineer” and announce the column header “Role” when navigating to that cell.
A more complete table example
<table>
<thead>
<tr>
<th scope="col">Day</th>
<th scope="col">Morning</th>
<th scope="col">Afternoon</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Monday</th>
<td>Meeting</td>
<td>Code review</td>
</tr>
<tr>
<th scope="row">Tuesday</th>
<td>Workshop</td>
<td>Planning</td>
</tr>
</tbody>
</table>
Here, scope="col" on the column headers in <thead> tells assistive technology that “Day,” “Morning,” and “Afternoon” each apply to the cells below them. scope="row" on “Monday” and “Tuesday” indicates they apply to the cells in their respective rows. Every scope attribute sits on a <th>, so the markup is valid and accessible.
When to remove scope instead
If the cell truly contains data and isn’t a header, simply remove the scope attribute:
<!-- Before (invalid) -->
<td scope="row">Some data</td>
<!-- After (valid) -->
<td>Some data</td>
Only add scope when a cell genuinely serves as a header. If it does, make it a <th>. If it doesn’t, leave it as a plain <td> without scope.
Ready to validate your sites?
Start your free trial today.