A 04-01 vam fixar la regla d'or: fes servir l'element HTML natiu abans de recrear-lo. A 04-02 ja vam apuntar atributs com aria-describedby, aria-invalid i role="alert". Ara toca entendre què són aquests atributs: WAI-ARIA (Accessible Rich Internet Applications), un conjunt d'atributs que permeten descriure semàntica —rol, estat i propietats— en el marcatge quan l'HTML natiu no hi arriba. ARIA no canvia el que veu l'usuari ni afegeix comportament: només canvia el que exposa l'arbre d'accessibilitat que vam estudiar al Mòdul 2. És una capa d'informació, no d'interacció.

Precisament per això, aquesta lliçó tracta ARIA com a semàntica declarativa: com etiquetar rols i estats en l'HTML. Construir el widget interactiu —capturar teclat, moure el focus i actualitzar aquests estats amb JavaScript— és el Mòdul 5 (05-01). Aquí veuràs, per exemple, com donar a la Marta el name/role/value correcte del desplegable de filtres de Cursalia; fer que funcioni amb el teclat serà el mòdul següent.

Contingut

  1. Què és ARIA i què no és
  2. Les 5 regles de l'ARIA
  3. Rols: landmark, widget i document
  4. Estats i propietats
  5. El nom accessible: aria-label, aria-labelledby, aria-describedby
  6. aria-hidden i el perill d'amagar contingut focusable
  7. Introducció a les live regions
  8. Cas Cursalia: name/role/value del desplegable de filtres
  9. Taula d'atributs ARIA freqüents
  10. Errors comuns i consells
  11. Exercicis
  12. Conclusió

Què és ARIA i què no és

ARIA aporta tres tipus d'informació a l'arbre d'accessibilitat:

  • Rols (role="..."): què és un element (un botó, una pestanya, una alerta).
  • Propietats (aria-label, aria-describedby...): característiques relativament estables.
  • Estats (aria-expanded, aria-checked...): condicions que canvien amb la interacció.

El que ARIA NO fa, i convé gravar-s'ho:

  • No afegeix comportament. role="button" en un <div> no el fa clicable amb teclat ni focusable; només diu que és un botó. El comportament l'hi poses tu amb JS (Mòdul 5).
  • No canvia l'aparença. No aplica estils.
  • No millora res per si sol. Un ARIA mal posat és pitjor que no posar-ne cap, perquè menteix al lector de pantalla.

"Cap ARIA és millor que un mal ARIA." Un aria-* incorrecte crea una experiència trencada i difícil de diagnosticar.

Les 5 regles de l'ARIA

L'especificació resumeix el bon ús de l'ARIA en cinc regles. Són la brúixola d'aquesta lliçó.

  1. Si pots fer servir un element HTML natiu amb la semàntica i el comportament que necessites, fes-lo servir, i no facis servir ARIA. Un <button> abans que un <div role="button">. És la regla d'or de 04-01.
  2. No canviïs la semàntica nativa llevat que sigui imprescindible. No facis <h2 role="button">; embolcalla el text en un <button> dins de l'<h2>. Canviar el rol natiu esborra l'original (el "heading" desapareix).
  3. Tot widget ARIA interactiu ha de ser operable amb el teclat. Si crees role="button", ha de rebre focus i activar-se amb Enter/Espai. (La implementació és del Mòdul 5, però la regla neix aquí.)
  4. No posis role="presentation" ni aria-hidden="true" en elements focusables. Amagaries de l'arbre alguna cosa que el teclat sí que pot assolir: l'usuari enfoca "el no-res".
  5. Tot element interactiu ha de tenir un nom accessible. Un botó sense nom ("botó, sense etiqueta") és inservible per a la Marta.
graph TD
    A["Necessito semàntica extra?"] --> B{"Existeix un element<br/>HTML natiu?"}
    B -->|"Sí"| C["Fes-lo servir. NO facis servir ARIA<br/>(Regla 1)"]
    B -->|"No"| D["Fes servir ARIA:<br/>rol + estats + nom"]
    D --> E["Fes-lo operable per teclat<br/>(Regla 3 → Mòdul 5)"]

Rols: landmark, widget i document

Els rols ARIA s'agrupen en famílies. Tres importen aquí:

  • Rols de landmark: regions de navegació. Ja els obtens gratis amb HTML5 (<nav>=navigation, <main>=main...). Només faries servir l'ARIA equivalent (role="navigation") si no pots emprar l'element natiu.
  • Rols de widget: components interactius (button, checkbox, tab, menuitem, dialog, slider...). Descriuen controls rics. Construir-ne el comportament és el Mòdul 5; aquí només declarem el rol.
  • Rols de document/estructura: organitzen contingut (list, listitem, heading, article, alert, status...).
Tipus d'element HTML5 Rol implícit (no cal declarar-lo)
<nav> navigation
<main> main
<button> button
<ul> / <li> list / listitem
<h1><h6> heading (amb el seu nivell)

La lliçó: si l'element natiu ja té el rol, no el repeteixis amb role. <nav role="navigation"> és redundant.

Estats i propietats

Quan l'HTML natiu no pot expressar un estat (perquè el control no existeix de forma nativa), ARIA el comunica. Els més freqüents en marcatge declaratiu:

Atribut Què comunica Valors típics
aria-expanded Si un desplegable/acordió està obert true / false
aria-checked Estat d'un checkbox/radio personalitzat true / false / mixed
aria-selected Element seleccionat (pestanyes, llistes) true / false
aria-disabled Deshabilitat (però encara perceptible pel lector) true / false
aria-current L'element actual dins d'un conjunt page, step, true...
aria-pressed Estat d'un botó d'alternança true / false

Un exemple declaratiu: a la barra de navegació de Cursalia, marcar la pàgina actual amb aria-current="page" perquè la Marta sàpiga on és.

<!-- CORRECTE: aria-current assenyala la pàgina activa -->
<nav aria-label="Principal">
  <ul>
    <li><a href="/cursos" aria-current="page">Catàleg</a></li>
    <li><a href="/preus">Preus</a></li>
  </ul>
</nav>

Advertència clau: aquests estats són declaracions. aria-expanded="false" al botó de filtres només diu "tancat"; actualitzar-lo a true en obrir és feina de JavaScript (Mòdul 5). Un estat que mai no s'actualitza torna a mentir al lector.

El nom accessible: aria-label, aria-labelledby, aria-describedby

El nom accessible és el text que el lector anuncia per identificar un element (el name de name/role/value). El navegador el calcula seguint una prioritat. Simplificada, de major a menor:

  1. aria-labelledby (apunta als id d'altres elements que serveixen de nom). Guanya sobre tota la resta.
  2. aria-label (text literal en el mateix atribut).
  3. El contingut/label natiu (el text del <button>, el <label for> associat).
  4. Altres mecanismes (title...), com a últim recurs.
  • aria-label: dona un nom quan no hi ha text visible. Ideal per al botó de tancar (una "X") o el cercador amb només una lupa.
<!-- CORRECTE: sense text visible, aria-label dona el nom -->
<button type="button" aria-label="Tanca el panell de filtres">✕</button>
  • aria-labelledby: construeix el nom a partir de text ja visible a la pàgina (evita duplicar i traduir dues vegades). Pot referenciar diversos id, que es concatenen.
<!-- CORRECTE: el nom de la regió es pren del seu encapçalament visible -->
<section aria-labelledby="tit-filtres">
  <h2 id="tit-filtres">Filtra cursos</h2>
  ...
</section>
  • aria-describedby: no aporta el nom sinó una descripció addicional (es llegeix després). És el que fem servir a 04-02 per a ajudes i errors de formulari.

Diferència essencial: labelledby = com es diu; describedby = informació extra. I aria-labelledby sobreescriu el text intern, així que fes-lo servir amb cura.

aria-hidden i el perill d'amagar contingut focusable

aria-hidden="true" elimina un element (i els seus descendents) de l'arbre d'accessibilitat, sense amagar-lo visualment. Serveix per amagar del lector coses purament decoratives o duplicades: una icona al costat d'un text que ja ho diu.

<!-- CORRECTE: la icona és decorativa; el text ja comunica l'acció -->
<button type="button">
  <span aria-hidden="true">🔍</span> Cerca
</button>

L'error greu (Regla 4) és posar aria-hidden="true" en alguna cosa focusable o que contingui elements focusables (un enllaç, un botó, un input). El resultat: la Lucía tabula fins a un control que el lector considera inexistent. Enfoca "el no-res".

<!-- INCORRECTE: s'amaga del lector un enllaç que segueix sent focusable -->
<a href="/ajuda" aria-hidden="true">Ajuda</a>

Si de debò vols retirar alguna cosa del tot (visual i de l'arbre), fes servir hidden, display:none o treu-la del DOM; aria-hidden només afecta l'arbre, no el focus.

Introducció a les live regions

Una live region és una zona el contingut de la qual, quan canvia dinàmicament, el lector anuncia sol, sense que l'usuari hagi d'anar-hi fins allà. És la base del criteri 4.1.3 (Missatges d'estat) que vam veure com a requisit al Mòdul 3. A nivell de marcatge hi ha tres peces:

  • aria-live="polite": anuncia quan el lector acabi el que estava dient. Per a missatges no urgents: "Filtre aplicat: 12 cursos".
  • aria-live="assertive": interromp i anuncia de seguida. Per al que és urgent: un error d'enviament.
  • Rols drecera: role="status" equival a polite; role="alert" equival a assertive.
<!-- CORRECTE: regió educada per confirmar resultats del filtre -->
<div role="status" aria-live="polite" id="resultat-filtre"></div>
// El lector anunciarà aquest text tan aviat com el teu JS l'escrigui:
document.getElementById('resultat-filtre').textContent =
  'Filtre aplicat: 12 cursos trobats.';

Punt important: el contenidor ha d'existir buit en el DOM des del principi; el lector vigila els canvis dins d'ell. Ens quedem en el marcatge. El detall del comportament dinàmic, quan fer servir polite vs assertive en formularis validats en viu i el testing corresponen als Mòduls 5, 6 i 7.

Cas Cursalia: name/role/value del desplegable de filtres

Cursalia té un botó "Filtres" que mostra o amaga un panell. Suposem —seguint la regla 1— que no podem fer servir <details>/<summary> natius i cal construir-lo. La nostra tasca en aquest mòdul és només el marcatge semàntic correcte perquè la Marta percebi què és i en quin estat està:

<!-- Semàntica declarativa: rol (button, natiu), nom (text) i estat (aria-expanded) -->
<button type="button"
        aria-expanded="false"
        aria-controls="panell-filtres">
  Filtres
</button>

<div id="panell-filtres" hidden>
  <h2>Filtra cursos</h2>
  <!-- checkboxes de categoria, nivell, idioma... -->
</div>

Analitzem què rep la Marta:

  • Role: button (natiu, no cal ARIA per al rol).
  • Name: "Filtres" (text del botó).
  • Value/State: aria-expanded="false" → el lector anuncia "contret".
  • aria-controls="panell-filtres": declara quin element governa el botó.

Amb això, la Marta sent: "Filtres, botó contret". Sap què és, com es diu i el seu estat.

El que falta i NO és d'aquest mòdul: que en prémer es mostri el panell, que aria-expanded passi a true, que es retiri el hidden, i que el teclat (Enter/Espai/Escape) funcioni. Tot aquest comportament és 05-01/05-02. Aquí hem deixat la bastida semàntica perfecta perquè aquest JavaScript només hagi d'actualitzar l'estat.

Taula d'atributs ARIA freqüents

Atribut Categoria Per a què serveix
role Declara què és l'element (si no hi ha natiu)
aria-label Propietat Nom per text literal (sense text visible)
aria-labelledby Propietat Nom pres d'altres elements visibles
aria-describedby Propietat Descripció addicional (ajudes, errors)
aria-expanded Estat Obert/tancat de desplegables i acordions
aria-controls Propietat Quin element controla aquest control
aria-current Estat Element actual (pàgina, pas)
aria-selected Estat Element seleccionat (pestanyes, opcions)
aria-checked Estat Marcat de checkbox/radio personalitzat
aria-disabled Estat Deshabilitat però perceptible
aria-hidden Propietat Amaga de l'arbre (mai en focusables!)
aria-live Propietat Regió que s'anuncia en canviar

Errors Comuns i Consells

  • Reinventar amb ARIA el que ja existeix. <div role="button"> en lloc de <button>. Regla 1 sempre primer.
  • Sobreescriure la semàntica nativa. <a role="button"> o <h2 role="tab"> esborren el rol original. Gairebé mai és el que vols.
  • Estats que no s'actualitzen. Un aria-expanded="false" que mai no canvia menteix. Si poses l'estat, alguna cosa (JS, Mòdul 5) l'ha de mantenir al dia.
  • aria-hidden sobre elements focusables. Crea "trampes" on el focus existeix però el lector no veu res.
  • Elements interactius sense nom. Un botó-icona sense aria-label és "botó sense etiqueta". Regla 5.
  • Confondre labelledby amb describedby. El primer és el nom; el segon, informació extra que es llegeix després.
  • Live region creada en el moment del canvi. Ha d'existir buida des de l'inici perquè el lector la vigili.
  • Consell: valida el teu ARIA amb la vista d'arbre d'accessibilitat del navegador (DevTools) i amb un lector real. Si dubtes si afegir un aria-*, probablement la resposta sigui fer servir HTML natiu.

Exercicis

Exercici 1: aplicar la regla 1

Un company ha maquetat el botó de reproduir de la vídeo-lliçó així. Reescriu-lo complint les regles de l'ARIA.

<div role="button" tabindex="0" onclick="play()">▶</div>

Exercici 2: donar nom accessible

El botó de tancar el panell de filtres només mostra una icona "✕" i no té text. Dona-li un nom accessible sense afegir text visible, i explica quin mecanisme fas servir.

Exercici 3: name/role/value d'un acordió de FAQ

Cursalia té una pregunta freqüent plegable. Escriu només el marcatge semàntic (rol, nom i estat) de l'encapçalament que obre/tanca la resposta, indicant quina part queda per al Mòdul 5.

Solucions

Solució 1. La regla 1 diu: fes servir l'element natiu. Un <button> dona rol, focus i teclat gratis, sense tabindex ni gestors manuals de teclat; la icona es marca decorativa i s'afegeix un nom:

<button type="button" aria-label="Reprodueix la lliçó" onclick="play()">
  <span aria-hidden="true">▶</span>
</button>

Així complim les regles 1 (natiu), 3 (operable amb teclat, gratis) i 5 (té nom).

Solució 2. Com que no hi ha text visible, es fa servir aria-label (propietat que dona un nom per text literal). La icona s'amaga del lector perquè és decorativa:

<button type="button" aria-label="Tanca el panell de filtres">
  <span aria-hidden="true">✕</span>
</button>

Si el nom ja estigués escrit visiblement en un altre element, l'opció preferent seria aria-labelledby apuntant-hi.

Solució 3. El patró accessible embolcalla el text de la pregunta en un <button> dins de l'encapçalament (no es canvia el rol de l'<h3>; regla 2):

<h3>
  <button type="button" aria-expanded="false" aria-controls="resp-1">
    Puc pausar la meva subscripció?
  </button>
</h3>
<div id="resp-1" hidden>
  <p>Sí, des del teu panell d'alumne pots pausar-la en qualsevol moment.</p>
</div>
  • Role: button (natiu) dins d'un heading que es conserva.
  • Name: el text de la pregunta.
  • State: aria-expanded="false".

El que queda per a 05-01: mostrar/amagar #resp-1, alternar aria-expanded i gestionar el teclat. Aquí només deixem la semàntica a punt.

Conclusió

ARIA és la manera d'afegir semàntica —rol, estat i nom— en el marcatge quan l'HTML natiu no basta, però la seva primera regla és no fer-lo servir si hi ha una alternativa nativa, i un ARIA mal posat és pitjor que cap. Has vist les cinc regles, els tipus de rol, els estats declaratius com aria-expanded, com es calcula el nom accessible (amb la prioritat labelledby > label > text natiu), el perill d'aria-hidden sobre focusables i una introducció a les live regions. I has deixat el desplegable de filtres de Cursalia amb un name/role/value impecable, a punt perquè el Mòdul 5 li doni comportament.

Ens queda una peça del principi "Perceptible" que encara no hem implementat i que és pur CSS: que el contingut es vegi i es llegeixi bé. A 04-04 · Contrast de Color i Redimensionament de Text tancarem el mòdul amb ratios de contrast, no dependre del color, unitats relatives, reflow i focus visible, aplicats a la paleta de marca de Cursalia i a la Sofía amb el seu zoom al 300%.

© Copyright 2026. Tots els drets reservats