Lección 9 de 10 · 11 min de lectura · Revisado el 26/10/2026
Errores habituales con ARIA y cómo detectarlos
Después de auditar cientos de sitios se repiten siempre los mismos errores de ARIA. No son errores exóticos: son copias de fragmentos de código sin entender la promesa que hacen, atributos puestos «por si acaso» y estados que se olvidan de actualizar. Esta lección los cataloga, explica qué oye la persona usuaria en cada caso y da el método para detectarlos antes de que lleguen a producción.
Los errores más repetidos
1. Roles redundantes o inválidos
<button role="button">…</button>
<nav role="navigation">…</nav>
<div role="boton">…</div>
<span role="text">…</span>Los dos primeros son inofensivos pero inútiles. El tercero usa un rol que no existe (los roles están en inglés y se escriben exactamente como en la especificación), así que el navegador lo ignora. El cuarto usa un rol que no forma parte de ARIA 1.2 y cuyo soporte es inconsistente. Un rol inválido equivale a no tener rol.
2. Rol sin comportamiento
<div role="button" onclick="abrir()">Abrir panel</div>El lector anuncia «Abrir panel, botón». La persona intenta pulsar Intro: no pasa nada, porque el elemento ni siquiera recibe el foco. Es la violación simultánea de las reglas 1 y 3. La corrección es usar <button>; si no es posible, añadir tabindex="0" y el manejador de Intro y Espacio.
3. Estados que no se actualizan
aria-expanded="false" escrito en el HTML y nunca cambiado por JavaScript; aria-selected="true" en todas las pestañas a la vez; aria-checked que no sigue al clic. La persona oye «contraído» con el panel abierto. Haz que el CSS dependa del atributo para que estos olvidos se vean también en pantalla.
4. aria-hidden en elementos enfocables o en contenido visible importante
<a href="/perfil" aria-hidden="true">Mi perfil</a>
<main aria-hidden="true">…</main> <!-- olvidado tras cerrar un modal -->El primer caso produce una parada de Tab muda. El segundo es un clásico de los modales hechos a mano: al abrir se oculta el resto de la página y al cerrar nadie lo restaura, con lo que el contenido principal desaparece para siempre del lector. Usa inert o <dialog> y comprueba el estado tras cerrar.
5. Referencias rotas
aria-labelledby="titulo" cuando no existe ningún elemento con id="titulo", o aria-controls apuntando a un id que cambió en una refactorización. El nombre queda vacío o se pierde la relación, y nadie se da cuenta porque visualmente no cambia nada. En aplicaciones con componentes reutilizables, además, los id fijos se duplican cuando el componente aparece dos veces en la página; genera identificadores únicos por instancia.
6. Atributos en elementos que no los admiten
<div aria-label="Noticias">…</div>
<p aria-expanded="true">…</p>
<input type="text" aria-checked="true">ARIA define qué estados y propiedades admite cada rol. aria-expanded no tiene sentido en un párrafo, aria-checked no se aplica a un cuadro de texto y aria-label en un div sin rol se ignora. Las herramientas automáticas detectan la mayoría de estas combinaciones.
7. aria-label que contradice el texto visible
<button aria-label="Submit form">Enviar solicitud</button>El control por voz no reconoce «pulsar Enviar solicitud» porque el nombre es otro; incumple 2.5.3 Etiqueta en el nombre. También es frecuente el aria-label que se quedó en inglés tras traducir la interfaz, o que describe el icono («lupa») en lugar de la acción («Buscar»).
8. Regiones activas que no anuncian o anuncian de más
Creadas con el mensaje dentro, en contenedores enormes, todas assertive, o aria-live en el contenedor de una lista que se repinta entera con cada tecla. Lo vimos en la lección anterior; y la ocultación mal hecha (aria-hidden olvidado, role="presentation" en tablas de datos) en la lección sobre ocultar, anular y desactivar.
9. Jerarquías rotas
role="tab" sin tablist, role="option" sueltos sin listbox, role="menuitem" en enlaces de navegación normales (que además cambia el teclado esperado: en un menu se navega con flechas, no con Tab). Usa role="menu" solo para menús de aplicación tipo escritorio, nunca para la navegación del sitio.
10. ARIA para «ayudar» al lector a leer texto
aria-label en párrafos para «leerlos mejor», role="text", aria-live en el contenido estático, tabindex="0" en encabezados. El lector ya lee todo el contenido; estos añadidos solo generan ruido y paradas sin función.
Punto clave
Casi todos los errores de ARIA se reducen a tres: prometer un comportamiento que no existe, olvidar actualizar un estado, o poner un atributo donde no tiene efecto. Antes de escribir un aria-, pregunta: ¿qué rol tiene este elemento, admite este atributo y quién lo va a actualizar?
Cómo detectarlos
Herramientas automáticas
axe DevTools, WAVE, Lighthouse y el inspector de accesibilidad de Firefox detectan de forma fiable: roles inválidos, atributos no permitidos para el rol, valores incorrectos (aria-expanded="yes"), referencias a id inexistentes, id duplicados, roles sin la jerarquía padre requerida, elementos enfocables con aria-hidden, controles sin nombre y roles redundantes. Es la parte «sintáctica» del problema, y conviene ejecutarla en cada compilación (lo veremos en el curso de frameworks).
Lo que solo detecta una persona
Ninguna herramienta sabe si aria-expanded se actualiza al pulsar, si el nombre «Buscar» describe realmente lo que hace el botón, si el role="alert" era necesario o si el menu debería ser una nav. Para eso:
- Inspecciona el árbol de accesibilidad en las herramientas del navegador mientras interactúas: el rol, el nombre y los estados deben cambiar como esperas.
- Escucha con un lector de pantalla (NVDA con Chrome o Firefox es la combinación más representativa en España) cada componente en reposo, al enfocarlo, al activarlo y al cerrarlo. Lo que oigas debe coincidir con lo que ves.
- Prueba con control por voz (Acceso por voz de Windows 11 o Control por voz de macOS) diciendo «pulsar» seguido del texto visible de los controles: detecta los
aria-labeldesincronizados. - Revisa el código en busca de
aria-y, para cada aparición, localiza la línea de JavaScript que lo actualiza. Si no la hay y es un estado, es un error.
Ejemplo
Antes de la revisión:
<div class="acordeon">
<div class="cabecera" role="button" aria-expanded="false" onclick="toggle(1)">Plazos</div>
<div id="panel1" aria-hidden="true">…</div>
</div>Hallazgos: rol sin foco ni teclado (regla 3), aria-expanded que toggle() no actualiza, panel oculto con aria-hidden pero visible en pantalla si falla el CSS, sin aria-controls. Después:
<div class="acordeon">
<h3><button type="button" aria-expanded="false" aria-controls="panel1">Plazos</button></h3>
<div id="panel1" hidden>…</div>
</div>boton.addEventListener('click', () => {
const abierto = boton.getAttribute('aria-expanded') === 'true';
boton.setAttribute('aria-expanded', String(!abierto));
panel.hidden = abierto;
});Error frecuente
Dar por bueno un componente porque axe no muestra errores. axe no puede saber que el estado no se actualiza ni que el nombre es engañoso. «Cero errores en axe» es una condición necesaria, nunca suficiente.
Resumen
- Los errores típicos: roles redundantes o inválidos, rol sin comportamiento, estados no actualizados,
aria-hiddenen enfocables o en contenido visible, referencias rotas, atributos no admitidos,aria-labelcontradictorio, regiones activas mal creadas, jerarquías rotas y ARIA sobre texto estático. - Las herramientas automáticas detectan la sintaxis: roles, atributos y referencias inválidos.
- Solo una persona detecta la semántica: estados que no cambian, nombres engañosos, roles inadecuados.
- Método: inspeccionar el árbol mientras interactúas, escuchar con lector de pantalla, probar con control por voz y buscar quién actualiza cada
aria-.
Comprueba lo que has aprendido
Esta lección se puede leer libremente. Para hacer su test de 3 preguntas, los ejercicios prácticos con páginas reales, guardar tu avance y optar al examen y al certificado, matricúlate en el curso.