Pruébalo en tu web
01
Elige HTML adecuado
Para una acción utiliza un botón. Para navegar, un enlace con destino.
02
Comprueba el estado
Abre y cierra un desplegable. El estado expandido debe coincidir con lo visible y actualizarse.
03
Prueba la interacción
Revisa teclado, foco y árbol de accesibilidad; contrasta con un lector de pantalla en el entorno de prueba.
CASO SIMULADO
Un botón que anuncia «contraído» mientras el contenido está abierto transmite un estado incorrecto. Cambiar solo el atributo tampoco abre el contenido.
Qué hacer con el resultado
Si HTML ya resuelve la necesidad, úsalo. Añade ARIA cuando falte información y comprueba que la implementación sostiene lo que comunica.
Para profundizar
Abrir la guía completa, el código y las fuentes
ARIA permite comunicar semántica que falta en una interfaz: qué es un elemento, cómo se llama, qué estado tiene y con qué contenido se relaciona. No crea automáticamente el comportamiento que anuncia. Un div con role="button" sigue necesitando foco, activación por teclado y lógica; un button ya aporta buena parte de esa base.
Para quién: desarrollo y revisión técnica. Antes de añadir atributos, identifica la tarea y el comportamiento esperado. Esta guía usa ejemplos didácticos independientes: al integrarlos en un producto, comprueba identificadores, estilos, compatibilidad y estados del recorrido completo.
Primera decisión: ¿HTML ya resuelve la necesidad?
| Necesidad | Punto de partida | Qué revisar después |
|---|---|---|
| Ir a otra página | <a href="…"> | Destino y nombre comprensibles. |
| Ejecutar una acción | <button type="button"> | Resultado y estados de la acción. |
| Elegir una opción sencilla | input o select con etiqueta | Instrucciones, grupos y errores. |
| Mostrar información ampliada | details y summary | Título del desplegable y utilidad de ocultar ese contenido. |
| Crear una interacción sin equivalente suficiente | HTML adecuado más ARIA y JavaScript | Patrón completo: teclado, foco, relaciones y estados. |
Evita reemplazar semántica válida por roles contradictorios o repetir todos los roles implícitos por costumbre. Un atributo ARIA solo debe aparecer si comunica información real, permitida para ese elemento y sincronizada con la interfaz. Un valor incorrecto puede empeorar lo que antes exponía correctamente el navegador.
Rol, nombre, descripción y estado
El rol identifica la clase de elemento. El nombre accesible permite distinguirlo: «Buscar guías». La descripción aporta ayuda adicional: «Busca por método, componente o problema». El estado cuenta lo que ocurre ahora, como expandido o pulsado. No incrustes «botón» en el nombre si el rol ya comunica que lo es.
Cómo elegir entre label, aria-label y aria-labelledby
- Empieza con texto visible: contenido del botón, texto del enlace o un
labelasociado al campo medianteforeid. - Usa
aria-labelledbypara tomar el nombre de otro texto existente. Referencia identificadores únicos y vigentes. Puede combinar varios, en el orden indicado. - Usa
aria-labelcuando el elemento admite nombre y no hay texto visible adecuado que referenciar, por ejemplo un control formado solo por un icono. Sigue siendo preferible ofrecer una etiqueta visible cuando ayude a reconocer la función. - Usa
aria-describedbypara instrucciones o mensajes complementarios; no sustituye el nombre. Evita asociar párrafos enteros si una ayuda breve basta.
<label for="buscar-guias">Buscar guías</label>
<p id="ayuda-busqueda">Escribe un método, componente o problema.</p>
<input id="buscar-guias" name="s" type="search"
aria-describedby="ayuda-busqueda">
Fragmento HTML implementable de un campo; para enviar una búsqueda debes integrarlo en un formulario con destino real. No añadas además un aria-label="Consulta": podrías sustituir el nombre visible «Buscar guías» por uno distinto. Para controles con etiqueta visible, WCAG 2.5.3 requiere que el nombre accesible incluya ese texto, lo que facilita activarlos mediante voz.
Estados y relaciones que conviene distinguir
| Atributo | Qué comunica | Error frecuente |
|---|---|---|
aria-expanded | Si el contenido controlado está expandido. | Cambiar el panel sin actualizar true/false. |
aria-controls | Qué elemento controla este botón mediante su id. | Pensar que crea por sí mismo la interacción o que siempre se anuncia igual. |
aria-current="page" | Cuál es la página actual dentro de un conjunto de enlaces. | Usarlo como sustituto de selected en unas pestañas. |
aria-pressed | Estado de un botón de alternancia. | Añadirlo a un botón de enviar que no conserva un estado. |
aria-selected | Selección en roles que la admiten, como tab. | Aplicarlo indiscriminadamente a enlaces o contenedores. |
aria-disabled | Que el control se expone como no disponible. | Suponer que bloquea clics, envío o teclado; esa lógica debe implementarse. |
Ejemplo completo: un desplegable con mejora progresiva
Este ejemplo mantiene la explicación visible si JavaScript no funciona. El botón solo aparece cuando puede abrir y cerrar el panel. Intro y Espacio funcionan por ser un botón nativo. No mueve el foco al abrir: la persona continúa desde el control.
<button type="button" id="ver-criterio" hidden
aria-controls="criterio" aria-expanded="true">
Criterio para considerar la tarea completada
</button>
<div id="criterio">
<p>La persona llega a la confirmación sin ayuda del moderador.</p>
</div>
<script>
const boton = document.getElementById("ver-criterio");
const panel = document.getElementById("criterio");
function mostrar(abierto) {
panel.hidden = !abierto;
boton.setAttribute("aria-expanded", String(abierto));
}
boton.addEventListener("click", () => {
mostrar(boton.getAttribute("aria-expanded") !== "true");
});
mostrar(false);
boton.hidden = false;
</script>
Para un simple bloque de información ampliable, details suele evitar este JavaScript. El ejemplo explica la sincronización necesaria cuando construyes el comportamiento tú mismo. Si el panel contiene controles y lo cierras desde otro lugar mientras el foco está dentro, devuélvelo antes a un destino lógico.
Mensajes de estado: informar sin secuestrar el foco
Una búsqueda que actualiza resultados necesita comunicar su resultado, pero no obligar a abandonar el campo. Una región role="status" suele ser adecuada para información no urgente; lleva implícita una prioridad cortés y lectura atómica. role="alert" está pensado para información importante y urgente. Usarlo para cada actualización genera interrupciones.
En una región live general, aria-live="polite" solicita anunciar cambios en un momento apropiado y assertive pide mayor prioridad. El anuncio concreto depende de la combinación de navegador y tecnología de apoyo. Crea la región antes de actualizar su texto y evita convertir toda la lista de resultados o toda la página en una región viva.
<button type="button" id="comprobar-demo">Comprobar selección de ejemplo</button>
<p id="estado-demo" role="status"></p>
<script>
let veces = 0;
document.getElementById("comprobar-demo").addEventListener("click", () => {
veces += 1;
document.getElementById("estado-demo").textContent =
`Comprobación ${veces}: hay tres guías en esta selección de ejemplo.`;
});
</script>
Demostración completa y local: el resultado es ficticio y no consulta una API. En una búsqueda real, escribe el recuento que devuelve la petición vigente y maneja también carga, cero resultados y error. No anuncies un éxito antes de que el servidor lo confirme. No añadas simultáneamente mecanismos redundantes de anuncio sin probar el resultado.
Oculto visualmente no significa fuera de la interacción
| Técnica | Presentación habitual | Accesibilidad e interacción |
|---|---|---|
hidden normal o display:none | No se muestra. | Normalmente se excluye del árbol de accesibilidad y no recibe foco; evita CSS que contradiga hidden. |
| Clase para ocultar solo visualmente | Queda fuera de la lectura visual ordinaria. | Puede seguir expuesto a tecnologías de apoyo; no debe dejar controles enfocables invisibles. |
aria-hidden="true" | No cambia el aspecto. | Excluye el elemento y descendientes del árbol de accesibilidad; no impide por sí solo foco ni clic. |
inert | No obliga a cambiar el aspecto. | Desactiva la interacción y excluye el subárbol de la navegación por foco y del árbol de accesibilidad. |
Usa aria-hidden con cuidado para información decorativa o redundante. No lo pongas en un control enfocable ni en un ancestro que contenga controles utilizables. Tampoco puedes recuperar un descendiente con aria-hidden="false" si un ancestro sigue oculto. role="presentation" elimina determinada semántica; no equivale a ocultar todo el contenido.
Foco y teclado forman parte del contrato
Antes de programar un widget, escribe qué hacen Tab, Mayús+Tab, Intro, Espacio, flechas y Escape cuando proceda. No todos los componentes usan todas las teclas. En un desplegable de navegación convencional, Tab recorre enlaces; en una lista de pestañas, las flechas cambian el foco entre pestañas y la selección sigue el modelo elegido.
No asignes role="menu" a la navegación del sitio solo porque se llama «menú». Ese rol implica un comportamiento de aplicación que suele ser innecesario para enlaces de páginas. Un nav, una lista y botones de expansión suelen expresar mejor la intención. No uses role="application" como arreglo general.
Cómo revisar tu implementación
- Inspecciona en el navegador el rol, nombre, descripción y estados calculados de cada control.
- Recorre el ejemplo solo con teclado, en ambos sentidos y con todos sus estados abiertos/cerrados.
- Confirma que cada referencia ARIA apunta a un id único y que lo anunciado coincide con lo visible.
- Prueba con JavaScript desactivado o fallido cuando hayas prometido mejora progresiva.
- Comprueba anuncios y navegación con las combinaciones reales de lector de pantalla que soporte tu producto. Documenta exactamente cuáles.
- Repite la revisión tras cargar datos, mostrar errores y cambiar el tamaño de ventana.
APG, la guía de prácticas de autoría de ARIA, ofrece patrones y ejemplos explicativos. Sus ejemplos no son una garantía de producción ni reemplazan las pruebas de integración. La pregunta de cierre es concreta: ¿puede la persona completar la acción y entender su resultado con la forma de interacción que utiliza?
