Lliçó 2 de 10 · 17 min de lectura · Revisada el 26/10/2026
Botons, enllaços i controls personalitzats
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 botons i els enllaços són els dos controls més abundants de qualsevol web i, paradoxalment, els que més es confonen entre si. Per a una persona que veu la pantalla la diferència és cosmètica; per a qui fa servir lector de pantalla o control per veu la diferència és funcional: un enllaç «va a un lloc», un botó «fa alguna cosa», i cadascun s'opera amb tecles diferents i apareix en llistes diferents. Aquesta lliçó fixa quan cal fer servir cadascun, com anomenar-los i què cal reconstruir quan, excepcionalment, es construeix un control des de zero.
Botó o enllaç: la decisió
| Si l'acció… | Fes servir | Teclat natiu |
|---|---|---|
| Navega a una altra URL o a una altra part de la pàgina | <a href="…"> | Retorn |
| Executa alguna cosa a la pàgina (obrir, enviar, esborrar, alternar) | <button type="button"> | Retorn i Espai |
| Envia un formulari | <button type="submit"> | Retorn i Espai |
| Baixa un fitxer | <a href="…" download> | Retorn |
Les conseqüències d'equivocar-se són concretes. Un enllaç amb href="#" que obre un modal apareix a la llista d'enllaços del lector de pantalla, ofereix «obre en una pestanya nova» al menú contextual (cosa que obre una pàgina buida), no respon a Espai i, si JavaScript falla, fa saltar la pàgina amunt. Un <button> que navega amb onclick="location.href=…" no es pot obrir en una altra pestanya, no mostra la URL de destinació i no apareix a la llista d'enllaços.
Error freqüent
<a href="#" onclick="abrirCarrito()">Mostra el carretó</a>
<a href="javascript:void(0)" class="btn">Accepta</a>
<a class="btn">Continua</a>Els dos primers són botons disfressats d'enllaç. El tercer és pitjor: un <a> sense href no és enfocable ni té rol d'enllaç, així que ni tan sols s'hi pot arribar amb teclat. Tots haurien de ser <button type="button">.
Si per restriccions del framework o del CMS no pots canviar l'element, aplica el pedaç complet: role="button", gestor d'Espai (perquè un enllaç no en té) i href vàlid. Però és un pedaç, no la solució.
Noms que funcionen fora de context
Les persones usuàries de lectors de pantalla naveguen sovint amb la llista d'enllaços o de botons, on cada nom apareix sense el text que l'envolta. El criteri 2.4.4 Propòsit dels enllaços (en context) (A) permet que el propòsit es dedueixi del context programàtic (la frase o l'element de llista que el conté), però la pràctica recomanable, i el que exigeix el criteri AAA 2.4.9, és que el text n'hi hagi prou per si sol.
- «Llegeix-ne més» repetit deu vegades no serveix. Millor: «Llegeix-ne més sobre les taxes 2025», encara que part del text quedi visualment amagat amb una classe d'ocultació accessible, o un
aria-labelledbyque combini el text de l'enllaç amb el títol de la targeta. - «Feu clic aquí» o «aquest enllaç» no descriuen res.
- En els enllaços que s'obren en una pestanya nova o que baixen un fitxer, indica'n el comportament i el format: «Memòria 2024 (PDF, 2 MB)», «Portal de transparència (s'obre en una finestra nova)».
- En els botons amb icona, el nom descriu l'acció («Tanca», «Cerca», «Elimina la fila d'Ana García»), no el dibuix («Creu», «Lupa», «Paperera»).
Exemple
Targeta de notícia amb un enllaç genèric que es torna específic sense canviar el disseny:
<article>
<h3 id="n-42">Noves ajudes al lloguer per a menors de 35 anys</h3>
<p>…</p>
<a href="/noticias/42" aria-labelledby="leer-42 n-42" id="leer-42">Llegeix-ne més</a>
</article>El nom accessible passa a ser «Llegeix-ne més Noves ajudes al lloguer per a menors de 35 anys». Compleix 2.5.3 perquè el text visible («Llegeix-ne més») és contingut al nom.
Estats dels botons
- Inhabilitat:
disabledtreu el botó de l'ordre de tabulació i l'anuncia com a «no disponible». És correcte per a accions impossibles en aquell moment, però té un cost: la persona no hi pot arribar per saber que existeix ni per què està inhabilitat. Per al botó d'enviament d'un formulari, moltes guies recomanen deixar-lo habilitat i mostrar els errors en prémer-lo, en lloc d'inhabilitar-lo fins que tot sigui vàlid. Si necessites que continuï sent enfocable, fes serviraria-disabled="true"i ignora el clic a JavaScript. - Commutador (activat/desactivat):
aria-pressed="true|false". El nom no canvia («Negreta»); canvia l'estat. No posis «Activa la negreta» i «Desactiva la negreta» alternant el text i a mésaria-pressed: el lector dirà «Desactiva la negreta, premut», que és confús. - Desplega alguna cosa:
aria-expandedi, si el que desplega no és l'element següent,aria-controls. - Obre un menú o diàleg:
aria-haspopup="menu",aria-haspopup="dialog"(opcional, però informatiu). - En curs: mentre es processa, canvia el text a «S'està enviant…» i fes servir
aria-disabledo una regió d'estat; evita l'indicador giratori mut.
Controls personalitzats: la llista de reconstrucció
Quan un control no té equivalent natiu (un botó de «m'agrada» amb una animació complexa, un selector de color, una fitxa que es pot arrossegar), cal reconstruir manualment el que donaria el navegador. La llista de comprovació mínima:
- Element contenidor amb el
rolecorrecte de l'APG (s'obre en una finestra nova). tabindex="0"(o roving tabindex si forma part d'un grup).- Nom accessible visible o amb
aria-labelledby/aria-label. - Estats ARIA actualitzats per JavaScript a cada canvi.
- Gestor
keydownper a Retorn i Espai (i fletxes si el patró les fa servir), ambpreventDefault()a Espai. - Indicador de focus visible amb
:focus-visible. - Mida mínima de l'objectiu de 24 per 24 píxels CSS (2.5.8 Mida de l'objectiu (mínim)), llevat que hi hagi prou separació, un equivalent més gran, o que sigui dins d'una línia de text.
- Esdeveniments de punter que no depenguin de gestos complexos (2.5.1) i que es puguin cancel·lar deixant anar fora (2.5.2 Cancel·lació del punter): activa a
clickopointerup, no apointerdown.
Punt clau
Abans de construir un control personalitzat, intenta estilitzar el natiu. Un <button> admet qualsevol CSS, incloses icones, animacions i formes, i t'estalvia els vuit punts de la llista. El control personalitzat és l'excepció justificada, no el punt de partida.
Com comprovar-ho
- Obre la llista d'enllaços i la de botons amb el lector de pantalla: cada nom s'ha d'entendre tot sol i ha de ser a la llista correcta.
- Prem Espai sobre cada cosa que sembla un botó: si navega o no fa res, no és un botó.
- Fes clic amb el botó dret sobre el que sembla un enllaç: ha d'oferir «obre en una pestanya nova».
- Mesura els objectius petits amb les eines del navegador: 24 per 24 píxels CSS com a mínim.
Resum
- Enllaç per navegar, botó per actuar; mai
href="#"amb unonclickni<a>sensehref. - Els noms han de funcionar fora de context i incloure el text visible; en icones, descriuen l'acció.
- Els estats s'expressen amb
disabled/aria-disabled,aria-pressed,aria-expandediaria-haspopup, actualitzats per JavaScript. - Un control personalitzat exigeix rol,
tabindex, nom, estats, teclat, focus visible, mida mínima i cancel·lació del punter. - Estilitzar l'element natiu gairebé sempre és més barat i més robust que reconstruir-lo.
Portes 4 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.