ARIA (Accessible Rich Internet Applications) è un insieme di attributi che permette di descrivere alle tecnologie assistive ciò che l'HTML da solo non riesce a esprimere: che un elemento è una scheda di un pannello a schede, che un menu è aperto, che una parte della pagina si è aggiornata. È uno strumento potente, ma usato male fa più danni che benefici. La sua regola principale, infatti, è un invito a non usarlo.
La prima regola di ARIA
La specifica sull'uso di ARIA nell'HTML lo dice chiaramente: se puoi usare un elemento HTML nativo che ha già la semantica e il comportamento che ti servono, usa quello invece di aggiungere ARIA a un elemento generico.
<!-- Da evitare -->
<div role="button" tabindex="0" aria-pressed="false">Grassetto</div>
<!-- Meglio -->
<button type="button" aria-pressed="false">Grassetto</button>
Il motivo è che ARIA cambia solo ciò che viene annunciato, non il comportamento. Un div con role="button" viene annunciato come pulsante, ma non riceve il focus senza tabindex, non si attiva con Invio o Spazio senza codice aggiuntivo, non partecipa all'invio dei form. Stai promettendo all'utente un pulsante che non si comporta come tale.
Le tre famiglie di attributi
Ruoli
Descrivono che cos'è un elemento: role="dialog", role="tablist", role="tab", role="alert", role="switch". Molti ruoli corrispondono già a elementi HTML (button, navigation, main, list) e non vanno ripetuti: <nav role="navigation"> è ridondante.
Proprietà
Descrivono caratteristiche relativamente stabili: aria-label, aria-labelledby, aria-describedby, aria-controls, aria-required.
Stati
Descrivono condizioni che cambiano con l'interazione: aria-expanded, aria-pressed, aria-checked, aria-selected, aria-invalid, aria-disabled, aria-current. Vanno aggiornati via JavaScript ogni volta che lo stato reale cambia.
Dare un nome agli elementi
Ogni controllo interattivo ha bisogno di un nome accessibile: è ciò che il lettore di schermo pronuncia. Normalmente lo fornisce il testo visibile. Quando non c'è testo visibile, ARIA aiuta:
<button type="button" aria-label="Chiudi finestra">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
aria-labelassegna un nome tramite una stringa.aria-labelledbyusa il testo di un altro elemento, indicandone l'id; ha la precedenza suaria-label.aria-describedbyaggiunge una descrizione supplementare, letta dopo il nome.
Un consiglio pratico: se c'è un testo visibile, il nome accessibile dovrebbe contenerlo. Chi usa i comandi vocali dice "clicca Invia" leggendo ciò che vede; se il nome accessibile è diverso, il comando non funziona.
Un caso reale: il pulsante che apre un menu
Un menu a scomparsa è il classico punto in cui ARIA serve davvero, perché l'HTML non ha un modo nativo per dire "questo pulsante controlla quel pannello ed è aperto":
<button type="button" aria-expanded="false" aria-controls="menu-principale">
Menu
</button>
<ul id="menu-principale" hidden>
<li><a href="/servizi">Servizi</a></li>
<li><a href="/contatti">Contatti</a></li>
</ul>
const btn = document.querySelector('[aria-controls="menu-principale"]');
const menu = document.getElementById('menu-principale');
btn.addEventListener('click', () => {
const aperto = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!aperto));
menu.hidden = aperto;
});
Lo stato aria-expanded permette al lettore di schermo di annunciare "Menu, pulsante, compresso" ed "espanso". L'attributo hidden nasconde il pannello a tutti, non solo visivamente.
Per pannelli espandibili semplici, prima di scrivere codice, valuta details e summary: offrono lo stesso comportamento in modo nativo.
Regioni live: annunciare gli aggiornamenti
Quando una parte della pagina cambia senza che l'utente vi abbia spostato il focus (un messaggio "Salvato", il numero di risultati di una ricerca, un errore dopo l'invio), chi usa un lettore di schermo non se ne accorge. Le regioni live risolvono:
<div aria-live="polite" id="stato"></div>
document.getElementById('stato').textContent = 'Modifiche salvate';
aria-live="polite"annuncia il cambiamento quando l'utente ha finito ciò che sta ascoltando.aria-live="assertive"(orole="alert") interrompe subito: va riservato a messaggi urgenti.
La regione deve essere presente nella pagina prima che il contenuto cambi; inserire nel DOM un elemento già pieno spesso non produce alcun annuncio.
Nascondere con criterio
aria-hidden="true" rimuove un elemento dall'albero di accessibilità ma lo lascia visibile. È utile per icone decorative accanto a un testo. Non va mai messo su elementi che possono ricevere il focus, né su un contenitore che include controlli interattivi: l'utente da tastiera finirebbe su elementi "fantasma" che il lettore di schermo non annuncia.
Gli errori più comuni
- Aggiungere ruoli ridondanti agli elementi nativi.
- Usare
role="button"orole="link"sudivespaninvece di elementi veri. - Dimenticare di aggiornare gli stati: un
aria-expanded="false"fisso su un menu aperto è peggio di nessun attributo. - Usare
aria-labelsu elementi non interattivi comedivespangenerici, dove spesso viene ignorato. - Costruire widget complessi (schede, caselle combinate, griglie) senza seguire i modelli di interazione da tastiera documentati nella guida alle pratiche di authoring ARIA del W3C.
In sintesi
- La prima regola di ARIA è preferire sempre l'elemento HTML nativo.
- ARIA cambia ciò che viene annunciato, non il comportamento: tastiera e focus restano a tuo carico.
- Usa
aria-labelearia-labelledbyper dare un nome ai controlli senza testo visibile. - Aggiorna gli stati come
aria-expandeda ogni cambiamento reale. - Le regioni live annunciano gli aggiornamenti;
aria-hiddennon va mai su elementi focalizzabili.