Lliçó 4 de 10 · 20 min de lectura · Revisada el 26/10/2026
Menús, desplegables i pestanyes (patrons APG)
Estàs llegint una lliçó oberta. Matricula't gratis per fer els tests i els exercicis, desar el teu progrés i aconseguir el certificat.
Els menús de navegació amb submenús, els botons que despleguen opcions i els conjunts de pestanyes són a gairebé totes les aplicacions i a gairebé tots els informes d'auditoria. La causa sol ser la mateixa: es construeixen pensant en el ratolí i després s'intenta «afegir-hi accessibilitat». L'ARIA Authoring Practices Guide (s'obre en una finestra nova) (APG) del W3C descriu per a cadascun d'aquests patrons els rols, els estats i, sobretot, el comportament del teclat que esperen les persones usuàries. Aquesta lliçó resumeix els tres patrons més necessaris i, igual d'important, quan no s'han de fer servir.
Navegació del lloc amb submenús: disclosure, no menu
El primer error és conceptual. La navegació principal d'un web (Inici, Tràmits, Transparència…) no és un «menú» en el sentit d'ARIA. Els rols menu, menubar i menuitem descriuen menús d'aplicació com els d'un editor de text: s'obren amb un botó, es recorren amb fletxes, es tanquen amb Escape i Tab surt del menú complet. Aplicar-los a una llista d'enllaços de navegació fa que els lectors de pantalla anunciïn «menú» i esperin fletxes que no funcionen.
L'APG recomana per a la navegació amb submenús el patró disclosure (mostrar/amagar): un <nav> amb una llista d'enllaços i, en els elements amb submenú, un botó amb aria-expanded que mostra la subllista.
Exemple
<nav aria-label="Principal">
<ul>
<li><a href="/">Inici</a></li>
<li>
<button type="button" aria-expanded="false" aria-controls="sub-tramites">
Tràmits
</button>
<ul id="sub-tramites" hidden>
<li><a href="/tramites/cita">Cita prèvia</a></li>
<li><a href="/tramites/padron">Padró</a></li>
</ul>
</li>
<li><a href="/transparencia" aria-current="page">Transparència</a></li>
</ul>
</nav>Teclat: Tab recorre enllaços i botons en ordre; Retorn o Espai sobre el botó obre el submenú i la pulsació següent de Tab hi entra; Escape el tanca i retorna el focus al botó. aria-current="page" marca la pàgina actual. Si l'element pare ha de ser alhora enllaç i desplegable, posa l'enllaç i, al costat, un botó petit amb nom («Mostra el submenú de Tràmits») que el desplegui.
Si el submenú s'obre també amb :hover per als usuaris de ratolí, assegura't que no es tanca en moure el punter cap a ell (deixa un petit retard) i que es pot tancar sense moure el ratolí (1.4.13); i a les pantalles tàctils, el primer toc ha d'obrir, no navegar.
Botó de menú (menu button)
El patró menu sí que és l'adequat per a un botó que desplega una llista curta d'accions: «Més opcions» en una fila (Edita, Duplica, Elimina), el menú del compte d'usuari, el selector d'ordenació. Estructura:
<button type="button" id="acciones-42" aria-haspopup="menu" aria-expanded="false"
aria-controls="menu-42">Accions per a l'expedient 42</button>
<ul id="menu-42" role="menu" aria-labelledby="acciones-42" hidden>
<li role="none"><button role="menuitem" tabindex="-1">Edita</button></li>
<li role="none"><button role="menuitem" tabindex="-1">Duplica</button></li>
<li role="none"><button role="menuitem" tabindex="-1">Elimina</button></li>
</ul>Teclat que cal implementar: Retorn, Espai o Fletxa avall obren el menú i enfoquen el primer element; Fletxa amunt/avall es mouen entre elements, normalment de manera circular (l'APG ho deixa com a opció); Inici/Fi van al primer i a l'últim; una lletra salta a l'element que comença per aquesta lletra; Retorn activa; Escape tanca i retorna el focus al botó; Tab tanca el menú i passa a l'element següent de la pàgina. Els li porten role="none" perquè dins d'un menu els fills han de ser menuitem. Per a accions que són navegació, fes servir <a role="menuitem"> amb href.
Pestanyes (tabs)
Un conjunt de pestanyes mostra un panell cada vegada. El patró de l'APG fa servir tres rols: tablist per al contenidor, tab per a cada pestanya i tabpanel per a cada panell, enllaçats amb aria-controls i aria-labelledby.
<div role="tablist" aria-label="Informació del tràmit">
<button role="tab" id="tab-req" aria-selected="true" aria-controls="panel-req">Requisits</button>
<button role="tab" id="tab-doc" aria-selected="false" aria-controls="panel-doc" tabindex="-1">Documentació</button>
<button role="tab" id="tab-pla" aria-selected="false" aria-controls="panel-pla" tabindex="-1">Terminis</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 clau és al teclat, que segueix el patró roving tabindex que vas veure a la lliçó «Ginys compostos»: només la pestanya activa té tabindex="0" i les altres -1, de manera que Tab entra a la llista de pestanyes una sola vegada i després passa al panell. Dins de la llista, Fletxa esquerra/dreta mouen el focus (i, en la variant d'activació automàtica, canvien de pestanya); Inici/Fi van a la primera i a l'última. En la variant d'activació manual, les fletxes només mouen el focus i cal prémer Retorn o Espai per activar; és preferible quan canviar de panell és costós (carrega dades). El panell porta tabindex="0" perquè s'hi pugui arribar encara que no contingui elements enfocables.
Error freqüent
<ul class="tabs">
<li class="active" onclick="mostrar(1)">Requisits</li>
<li onclick="mostrar(2)">Documentació</li>
</ul>Sense rols, sense focus, sense estat, sense teclat: el lector anuncia «llista de 2 elements» i res més. I la variant «amb ARIA però sense teclat», que posa role="tab" als li i deixa els onclick, és encara pitjor perquè promet pestanyes que no funcionen. Tampoc no facis servir aria-selected als panells ni aria-expanded a les pestanyes: els estats tenen amo.
Quan no s'han de fer servir pestanyes ni menús
Si el contingut ha de ser enllaçable (cada pestanya té la seva URL i es vol compartir), el més robust és que siguin enllaços de debò a pàgines o àncores diferents, estilitzats com a pestanyes, i no el patró tablist. Si les «pestanyes» són només dues o tres seccions de text, un simple contingut seqüencial amb encapçalaments o un acordió és més fàcil per a tothom, sobretot al mòbil. I si el «menú» té un sol element o són enllaços, és una llista d'enllaços.
Punt clau
Navegació del lloc: <nav> amb llistes, enllaços i botons disclosure (aria-expanded). Llista d'accions: patró menu button amb fletxes i Escape. Panells alternatius: tablist/tab/tabpanel amb roving tabindex. Cada patró té el seu teclat i no es barregen.
Com comprovar-ho
- Només amb teclat: obre i tanca cada desplegable, recorre les pestanyes amb fletxes, surt amb Tab i tanca amb Escape.
- Amb lector de pantalla: el rol anunciat ha de coincidir amb el patró («botó, contret», «pestanya, 1 de 3, seleccionada», «menú»).
- Comprova l'estat després de cada interacció a l'arbre d'accessibilitat:
aria-expanded,aria-selected. - Al mòbil, verifica que el primer toc sobre un desplegable amb hover l'obre en lloc de navegar.
Resum
- La navegació del lloc no és un
menuARIA: fes servirnav, llistes, enllaços i botons ambaria-expanded. - El patró menu button (
aria-haspopup,role="menu",menuitem) exigeix fletxes, Inici/Fi, lletres, Escape i retorn del focus. - Les pestanyes fan servir
tablist/tab/tabpanel,aria-selected,aria-controlsi roving tabindex amb fletxes. - Els estats tenen amo:
aria-expandedal botó,aria-selecteda la pestanya, mai als panells. - Si el contingut ha de tenir URL pròpia o és breu, els enllaços o els encapçalaments simples són millors que les pestanyes.
Portes 9 lliçons d'aquest curs: que no es perdin
Crea el teu compte gratis i desa el que ja has llegit i segueix el curs amb tot:
- El test d'aquesta lliçó (3 preguntes) per comprovar el que has après
- Exercicis pràctics amb pàgines reals
- El teu progrés desat: continues on ho vas deixar, des de qualsevol dispositiu
- Constància del curs amb les hores i certificat gratuït del nivell 3 quan aprovis l'examen
Entres amb el teu compte de Google o només amb el teu correu electrònic. No cal contrasenya.