Skip to main content
HTML Validation

The “scope” attribute on the “td” element is obsolete. Use the “scope” attribute on a “th” element instead.

About This HTML Issue

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.

Find issues like this automatically

Rocket Validator scans thousands of pages in seconds, detecting HTML issues across your entire site.

Help us improve our guides

Was this guide helpful?

Ready to validate your sites?
Start your free trial today.