Tablas accesibles en HTML: caption, th y scope | Nicolás Garzón
HTML
Copiar < th scope = " col" > Precio</ th>
< th scope = " row" > Teclado</ th>
< td> $320.000</ td> HTML
Copiar < table>
< caption> Comparación de planes mensuales</ caption>
...
</ table> La caption debe explicar qué representa la tabla, no repetir un heading cercano sin aportar nada.
Una descripción extensa puede colocarse antes o después y relacionarse cuando sea necesario.
HTML
Copiar < p id = " plan-description" >
Los precios no incluyen impuestos.
</ p>
< table aria-describedby = " plan-description" > ...</ table> No utilices aria-label para reemplazar una caption visible útil.
HTML
Copiar < table>
< caption> Precio por producto</ caption>
< thead>
< tr>
< th scope = " col" > Producto</ th>
< th scope = " col" > Precio</ th>
</ tr>
</ thead>
< tbody>
< tr>
< th scope = " row" > Teclado</ th>
< td> $320.000</ td>
</ tr>
</ tbody>
</ table> Cada dato tiene un heading de fila y uno de columna fáciles de identificar.
col: encabezado de columna.
row: encabezado de fila.
colgroup: grupo de columnas.
rowgroup: grupo de filas.
No añadas scope a td; pertenece a heading cells.
HTML
Copiar < table>
< caption> Ventas por trimestre</ caption>
< thead>
< tr>
< th rowspan = " 2" scope = " col" > Producto</ th>
< th colspan = " 2" scope = " colgroup" > Primer semestre</ th>
</ tr>
< tr>
< th scope = " col" > Q1</ th>
< th scope = " col" > Q2</ th>
</ tr>
</ thead>
...
</ table> colgroup comunica que el heading superior abarca varias columnas.
Comprueba el resultado con lectores de pantalla; combinaciones complejas no se anuncian igual en todas las herramientas.
HTML
Copiar < tbody>
< tr>
< th rowspan = " 2" scope = " rowgroup" > Bogotá</ th>
< th scope = " row" > Centro</ th>
< td> 120</ td>
</ tr>
< tr>
< th scope = " row" > Norte</ th>
< td> 90</ td>
</ tr>
</ tbody> Si la estructura requiere demasiada explicación, separar tablas puede ser mejor.
En relaciones complejas puedes asociar explícitamente:
HTML
Copiar < th id = " product" > Producto</ th>
< th id = " january" > Enero</ th>
< td headers = " product january" > 120</ td> Cada headers contiene IDs de headings aplicables.
Este método es verboso y fácil de desactualizar. Úsalo cuando scope no puede representar la relación de forma suficiente y prueba la implementación.
Textos demasiado largos dificultan lectura repetida. Mantén headings concisos y añade contexto fuera de la tabla.
El atributo histórico abbr sobre th tiene soporte irregular; no dependas de él como solución principal.
Una tabla debe seguir teniendo sentido cuando se recorre en orden DOM:
Texto
Copiar caption
encabezados
fila 1
fila 2No dependas únicamente de colores, posición o líneas visuales.
HTML
Copiar < td> < span aria-label = " Aumentó" > ↑ 12%</ span> </ td> Una flecha o color puede reforzar, pero no debe ser la única forma de comunicar tendencia.
HTML
Copiar < td>
< a href = " /products/p1" > Ver producto</ a>
< button type = " button" > Eliminar</ button>
</ td> Cada control necesita un nombre distinguible.
Peligroso cuando se repite:
HTML
Copiar < button aria-label = " Eliminar" > ...</ button> HTML
Copiar < button aria-label = " Eliminar Teclado mecánico" > ...</ button> O utiliza texto visible y contexto de fila comprobado.
HTML
Copiar < th scope = " col" aria-sort = " descending" >
< button type = " button" > Ventas</ button>
</ th> Solo una columna suele llevar aria-sort a la vez. Valores:
ascending.
descending.
none.
other.
El botón debe actualizar datos y estado.
Una tabla con links, checkboxes o buttons sigue pudiendo ser una table HTML.
role="grid" crea expectativas de navegación compleja con flechas y gestión de foco. No lo añadas para decir “tabla interactiva”.
Mantener estructura table.
Permitir scroll.
Ajustar densidad visual.
Dividir tablas demasiado amplias.
Ofrecer una representación alternativa cuando mejora comprensión.
No cambies a cards ocultando captions o relaciones importantes.
HTML
Copiar < p id = " inventory-note" >
Las cantidades corresponden al cierre del 23 de julio.
</ p>
< table aria-describedby = " inventory-note" >
< caption> Stock por sucursal</ caption>
< thead>
< tr>
< th scope = " col" > Producto</ th>
< th scope = " col" > Centro</ th>
< th scope = " col" > Norte</ th>
</ tr>
</ thead>
< tbody>
< tr>
< th scope = " row" > Café molido</ th>
< td> 14</ td>
< td> 8</ td>
</ tr>
</ tbody>
</ table>
Omitir contexto de la tabla.
Usar aria-label en vez de caption visible.
Colocar scope incorrecto.
Crear grupos con colspan sin colgroup.
Utilizar headers/ids y dejarlos desactualizados.
Comunicar estados solo con color.
Repetir buttons sin nombres distinguibles.
Añadir role grid sin implementar navegación.
Destruir la semántica para mobile.
Crear una tabla tan compleja que nadie pueda interpretarla.
Cada td debe poder relacionarse con headings.
Caption identifica la tabla.
Scope resuelve la mayoría de tablas simples.
Colgroup y rowgroup expresan agrupaciones.
Headers/ids sirven para casos complejos.
El orden DOM y el texto deben ser comprensibles sin estilo.
Controles repetidos necesitan nombres específicos.
Grid ARIA no es un upgrade automático de table.
Simplificar datos puede ser la mejor mejora accesible.
¿Cuándo deberías considerar headers e IDs en lugar de depender solo de scope?
Respuesta Cuando la tabla tiene varias capas o asociaciones que no pueden expresarse claramente mediante headings simples de fila, columna o grupos.
Formularios HTML inicia el bloque de captura, validación y envío de datos.