Lección 4 de 10 · 20 min de lectura · Revisado el 26/10/2026
Menús, desplegables y pestañas (patrones APG)
Menús de navegación con submenús, botones que despliegan opciones y conjuntos de pestañas están en casi todas las aplicaciones y en casi todos los informes de auditoría. La causa suele ser la misma: se construyen pensando en el ratón y después se intenta «añadir accesibilidad». La ARIA Authoring Practices Guide (se abre en una ventana nueva) (APG) del W3C describe para cada uno de estos patrones los roles, los estados y, sobre todo, el comportamiento del teclado que las personas usuarias esperan. Esta lección resume los tres patrones más necesarios y, igual de importante, cuándo no usarlos.
Navegación del sitio con submenús: disclosure, no menu
El primer error es conceptual. La navegación principal de una web (Inicio, Trámites, Transparencia…) no es un «menú» en el sentido de ARIA. Los roles menu, menubar y menuitem describen menús de aplicación como los de un editor de texto: se abren con un botón, se recorren con flechas, se cierran con Escape y Tab sale del menú completo. Aplicarlos a una lista de enlaces de navegación hace que los lectores de pantalla anuncien «menú» y esperen flechas que no funcionan.
La APG recomienda para la navegación con submenús el patrón disclosure (mostrar/ocultar): un <nav> con una lista de enlaces y, en los elementos con submenú, un botón con aria-expanded que muestra la sublista.
Ejemplo
<nav aria-label="Principal">
<ul>
<li><a href="/">Inicio</a></li>
<li>
<button type="button" aria-expanded="false" aria-controls="sub-tramites">
Trámites
</button>
<ul id="sub-tramites" hidden>
<li><a href="/tramites/cita">Cita previa</a></li>
<li><a href="/tramites/padron">Padrón</a></li>
</ul>
</li>
<li><a href="/transparencia" aria-current="page">Transparencia</a></li>
</ul>
</nav>Teclado: Tab recorre enlaces y botones en orden; Intro o Espacio sobre el botón abre el submenú y la siguiente pulsación de Tab entra en él; Escape lo cierra y devuelve el foco al botón. aria-current="page" marca la página actual. Si el elemento padre debe ser a la vez enlace y desplegable, pon el enlace y, a su lado, un botón pequeño con nombre («Mostrar submenú de Trámites») que despliegue.
Si el submenú se abre también con :hover para los usuarios de ratón, asegúrate de que no se cierra al mover el puntero hacia él (deja un pequeño retraso) y de que puede cerrarse sin mover el ratón (1.4.13); y en pantallas táctiles, el primer toque debe abrir, no navegar.
Botón de menú (menu button)
El patrón menu sí es el adecuado para un botón que despliega una lista corta de acciones: «Más opciones» en una fila (Editar, Duplicar, Eliminar), el menú de la cuenta de usuario, el selector de ordenación. Estructura:
<button type="button" id="acciones-42" aria-haspopup="menu" aria-expanded="false"
aria-controls="menu-42">Acciones para el expediente 42</button>
<ul id="menu-42" role="menu" aria-labelledby="acciones-42" hidden>
<li role="none"><button role="menuitem" tabindex="-1">Editar</button></li>
<li role="none"><button role="menuitem" tabindex="-1">Duplicar</button></li>
<li role="none"><button role="menuitem" tabindex="-1">Eliminar</button></li>
</ul>Teclado que hay que implementar: Intro, Espacio o Flecha abajo abren el menú y enfocan el primer elemento; Flecha arriba/abajo se mueven entre elementos, normalmente de forma circular (la APG lo deja como opción); Inicio/Fin van al primero y al último; una letra salta al elemento que empieza por ella; Intro activa; Escape cierra y devuelve el foco al botón; Tab cierra el menú y pasa al siguiente elemento de la página. Los li llevan role="none" porque dentro de un menu los hijos deben ser menuitem. Para acciones que son navegación, usa <a role="menuitem"> con href.
Pestañas (tabs)
Un conjunto de pestañas muestra un panel a la vez. El patrón de la APG usa tres roles: tablist para el contenedor, tab para cada pestaña y tabpanel para cada panel, enlazados con aria-controls y aria-labelledby.
<div role="tablist" aria-label="Información del trámite">
<button role="tab" id="tab-req" aria-selected="true" aria-controls="panel-req">Requisitos</button>
<button role="tab" id="tab-doc" aria-selected="false" aria-controls="panel-doc" tabindex="-1">Documentación</button>
<button role="tab" id="tab-pla" aria-selected="false" aria-controls="panel-pla" tabindex="-1">Plazos</button>
</div>
<div role="tabpanel" id="panel-req" aria-labelledby="tab-req" tabindex="0">…</div>
<div role="tabpanel" id="panel-doc" aria-labelledby="tab-doc" tabindex="0" hidden>…</div>
<div role="tabpanel" id="panel-pla" aria-labelledby="tab-pla" tabindex="0" hidden>…</div>La clave está en el teclado, que sigue el patrón roving tabindex que viste en la lección «Widgets compuestos»: solo la pestaña activa tiene tabindex="0" y las demás -1, de modo que Tab entra en la lista de pestañas una sola vez y luego pasa al panel. Dentro de la lista, Flecha izquierda/derecha mueven el foco (y, en la variante de activación automática, cambian de pestaña); Inicio/Fin van a la primera y la última. En la variante de activación manual, las flechas solo mueven el foco y hay que pulsar Intro o Espacio para activar; es preferible cuando cambiar de panel es costoso (carga datos). El panel lleva tabindex="0" para que sea alcanzable aunque no contenga elementos enfocables.
Error frecuente
<ul class="tabs">
<li class="active" onclick="mostrar(1)">Requisitos</li>
<li onclick="mostrar(2)">Documentación</li>
</ul>Sin roles, sin foco, sin estado, sin teclado: el lector anuncia «lista de 2 elementos» y nada más. Y la variante «con ARIA pero sin teclado», que pone role="tab" a los li y deja los onclick, es todavía peor porque promete pestañas que no funcionan. Tampoco uses aria-selected en los paneles ni aria-expanded en las pestañas: los estados tienen dueño.
Cuándo no usar pestañas ni menús
Si el contenido debe ser enlazable (cada pestaña tiene su URL y se quiere compartir), lo más robusto es que sean enlaces de verdad a páginas o anclas distintas, estilizados como pestañas, y no el patrón tablist. Si las «pestañas» son solo dos o tres secciones de texto, un simple contenido secuencial con encabezados o un acordeón es más fácil para todo el mundo, sobre todo en móvil. Y si el «menú» tiene un solo elemento o son enlaces, es una lista de enlaces.
Punto clave
Navegación del sitio: <nav> con listas, enlaces y botones disclosure (aria-expanded). Lista de acciones: patrón menu button con flechas y Escape. Paneles alternativos: tablist/tab/tabpanel con roving tabindex. Cada patrón tiene su teclado y no se mezclan.
Cómo comprobarlo
- Con solo teclado: abre y cierra cada desplegable, recorre las pestañas con flechas, sal con Tab y cierra con Escape.
- Con lector de pantalla: el rol anunciado debe coincidir con el patrón («botón, contraído», «pestaña, 1 de 3, seleccionada», «menú»).
- Comprueba el estado tras cada interacción en el árbol de accesibilidad:
aria-expanded,aria-selected. - En móvil, verifica que el primer toque sobre un desplegable con hover lo abre en lugar de navegar.
Resumen
- La navegación del sitio no es un
menuARIA: usanav, listas, enlaces y botones conaria-expanded. - El patrón menu button (
aria-haspopup,role="menu",menuitem) exige flechas, Inicio/Fin, letras, Escape y devolución del foco. - Las pestañas usan
tablist/tab/tabpanel,aria-selected,aria-controlsy roving tabindex con flechas. - Los estados tienen dueño:
aria-expandeden el botón,aria-selecteden la pestaña, nunca en los paneles. - Si el contenido debe tener URL propia o es breve, enlaces o encabezados simples son mejores que pestañas.
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.