Acerca de este problema HTML
La especificación ARIA define una jerarquía de propiedad estricta para los roles relacionados con tablas. Un columnheader representa una celda de encabezado para una columna, análogo a un elemento <th> en HTML nativo. Para que las tecnologías asistivas como lectores de pantalla anuncien correctamente los encabezados de columna y los asocien con las celdas de datos debajo de ellos, el columnheader debe existir dentro de un contexto de row. La estructura requerida es:
-
Un elemento con
role="table",role="grid", orole="treegrid"sirve como contenedor. -
Dentro de él, elementos con
role="rowgroup"(opcional) orole="row"organizan las filas. -
Cada elemento
role="row"contiene uno o más elementos conrole="columnheader",role="rowheader", orole="cell".
Cuando un elemento role="columnheader" se coloca directamente dentro de un contenedor role="table" o role="grid" — o cualquier otro elemento que no sea role="row" — el validador genera este error. Sin el envoltorio row, los lectores de pantalla no pueden navegar la estructura de la tabla correctamente. Los usuarios que dependen de tecnología asistiva pueden escuchar contenido inconexo o perderse los encabezados de columna completamente, haciendo que la tabla de datos sea inutilizable.
La mejor práctica, siempre que sea factible, es usar elementos HTML de tabla nativos (<table>, <thead>, <tr>, <th>, <td>). Estos llevan roles ARIA implícitos y establecen las relaciones de propiedad correctas automáticamente, eliminando toda esta categoría de errores. Solo usa roles ARIA de tabla cuando genuinamente no puedas usar marcado de tabla nativo — por ejemplo, al construir un widget de cuadrícula personalizado con elementos que no son tabla por razones de diseño.
Ejemplos
Incorrecto: columnheader no dentro de un row
En este ejemplo, los elementos columnheader son hijos directos del contenedor table, sin un envoltorio role="row":
<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>
Correcto: columnheader dentro de un row
Envolver los encabezados de columna en un elemento con role="row" soluciona el problema:
<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>
Correcto: usar rowgroup para estructura adicional
Opcionalmente puedes usar role="rowgroup" para separar encabezados de filas del cuerpo, similar a <thead> y <tbody>. Los elementos columnheader aún deben estar dentro de un 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>
Mejor práctica: usa elementos de tabla nativos
Las tablas HTML nativas tienen semántica incorporada que hace innecesarios los roles ARIA. Un <th> dentro de un <tr> ya se comporta como un columnheader dentro de un row:
<table>
<thead>
<tr>
<th>Name</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>Engineering</td>
</tr>
</tbody>
</table>
Este enfoque es más simple, más robusto entre navegadores y tecnologías asistivas, y evita el riesgo de mal uso de ARIA. Reserva los roles ARIA de tabla para situaciones donde el marcado de tabla nativo no es una opción.
Encuentra problemas como este automáticamente
Rocket Validator escanea miles de páginas en segundos, detectando problemas de HTML en todo tu sitio web.