Skip to main content
Validación HTML

An element with “role=columnheader” must be contained in, or owned by, an element with “role=row”.

Acerca de este problema HTML

The ARIA specification defines a strict ownership hierarchy for table-related roles. A columnheader represents a header cell for a column, analogous to a <th> element in native HTML. For assistive technologies like screen readers to correctly announce column headers and associate them with the data cells beneath them, the columnheader must exist within a row context. The required structure is:

  • An element with role="table", role="grid", or role="treegrid" serves as the container.
  • Inside it, elements with role="rowgroup" (optional) or role="row" organize the rows.
  • Each role="row" element contains one or more elements with role="columnheader", role="rowheader", or role="cell".

When a role="columnheader" element is placed directly inside a role="table" or role="grid" container — or any other element that is not role="row" — the validator raises this error. Without the row wrapper, screen readers cannot navigate the table structure properly. Users who rely on assistive technology may hear disjointed content or miss the column headers entirely, making the data table unusable.

The best practice, whenever feasible, is to use native HTML table elements (<table>, <thead>, <tr>, <th>, <td>). These carry implicit ARIA roles and establish the correct ownership relationships automatically, eliminating this entire category of errors. Only use ARIA table roles when you genuinely cannot use native table markup — for example, when building a custom grid widget with non-table elements for layout reasons.

Examples

Incorrect: columnheader not inside a row

In this example, the columnheader elements are direct children of the table container, with no role="row" wrapper:

<div role="table" aria-label="Employees">
  <div role="columnheader">Name</div>
  <div role="columnheader">Department</div>
  <div role="row">
    <div role="cell">Alice</div>
    <div role="cell">Engineering</div>
  </div>
</div>

Correct: columnheader inside a row

Wrapping the column headers in an element with role="row" fixes the issue:

<div role="table" aria-label="Employees">
  <div role="row">
    <div role="columnheader">Name</div>
    <div role="columnheader">Department</div>
  </div>
  <div role="row">
    <div role="cell">Alice</div>
    <div role="cell">Engineering</div>
  </div>
</div>

Correct: Using rowgroup for additional structure

You can optionally use role="rowgroup" to separate headers from body rows, similar to <thead> and <tbody>. The columnheader elements must still be inside a row:

<div role="table" aria-label="Employees">
  <div role="rowgroup">
    <div role="row">
      <div role="columnheader">Name</div>
      <div role="columnheader">Department</div>
    </div>
  </div>
  <div role="rowgroup">
    <div role="row">
      <div role="cell">Alice</div>
      <div role="cell">Engineering</div>
    </div>
  </div>
</div>

Best practice: Use native table elements

Native HTML tables have built-in semantics that make ARIA roles unnecessary. A <th> inside a <tr> already behaves as a columnheader inside a row:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Department</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>Engineering</td>
    </tr>
  </tbody>
</table>

This approach is simpler, more robust across browsers and assistive technologies, and avoids the risk of ARIA misuse. Reserve ARIA table roles for situations where native table markup is not an option.

Encuentra problemas como este automáticamente

Rocket Validator escanea miles de páginas en segundos, detectando problemas de HTML en todo tu sitio web.

Ayúdanos a mejorar nuestras guías

¿Te ha sido útil esta guía?

¿Listo para validar tus sitios?
Inicia tu prueba gratuita hoy.