ARIA (Accessible Rich Internet Applications) offre agli sviluppatori un potente toolkit per rendere accessibili agli utenti di screen reader interfacce web dinamiche e complesse, ma il suo uso improprio è dilagante e costoso. Questa guida analizza in dettaglio ogni principale categoria di ruoli ARIA, spiega le regole d’oro per l’uso di ARIA e ti mostra esempi di codice concreti così che tu possa applicarla correttamente.
Ecco un dato che fa riflettere: secondo l’analisi di WebAIM sulle prime un milione di homepage, le pagine che includono attributi ARIA presentano in media un numero significativamente maggiore di errori di accessibilità rilevabili rispetto alle pagine senza ARIA. Questo non è un argomento contro l’uso di ARIA — è un argomento a favore del suo uso corretto. ARIA è uno degli strumenti più potenti nel toolkit dell’accessibilità moderna, ma è anche uno dei più fraintesi. Se lo usi bene, apri concretamente il tuo sito a milioni di persone con disabilità. Se lo usi male, peggiori attivamente la loro esperienza.
Che cos’è ARIA e perché esiste?
ARIA sta per Accessible Rich Internet Applications. È un insieme di attributi HTML, definiti dalla Web Accessibility Initiative del W3C, che permette agli sviluppatori di comunicare informazioni semantiche alle tecnologie assistive come screen reader, display braille e software di controllo vocale. Quando un browser renderizza una pagina, costruisce due strutture parallele: il DOM (ciò che vedi) e l’Accessibility Tree (ciò che leggono le tecnologie assistive). Gli attributi ARIA ti permettono di modificare quell’Accessibility Tree per descrivere accuratamente cosa sono i componenti personalizzati e come si comportano.
La necessità di ARIA è nata da un problema reale. L’HTML è stato progettato per i documenti, non per le applicazioni. Quando il web si è evoluto in una piattaforma per esperienze ricche e interattive — interfacce a schede, dialog modali, drag-and-drop, feed di dati in tempo reale — gli elementi HTML nativi non riuscivano a comunicare a uno screen reader cosa fossero quei componenti o come funzionassero. ARIA colma questa lacuna. Come dice MDN, ARIA "integra l’HTML in modo che le interazioni e i widget comunemente usati nelle applicazioni possano essere trasmessi alle tecnologie assistive quando altrimenti non esiste un meccanismo."
ARIA non cambia la presentazione visiva. Non aggiunge comportamento. Non fornisce automaticamente il supporto da tastiera. Modifica esclusivamente ciò che l’Accessibility Tree espone alla tecnologia assistiva. Questa è una distinzione importante — ed è alla base di molti degli errori che gli sviluppatori commettono quando ricorrono ad ARIA come scorciatoia.
La specifica è mantenuta dal W3C come WAI-ARIA, attualmente alla versione 1.2, con la 1.3 in fase di sviluppo attivo. Fornisce un’ontologia di ruoli, stati e proprietà che insieme descrivono elementi di interfaccia utente accessibili per l’intera gamma dei pattern web moderni.
I tre pilastri: ruoli, stati e proprietà
Prima di scrivere una sola riga di ARIA, devi capire i tre blocchi costitutivi distinti che la specifica fornisce. Non sono intercambiabili, e confonderli è una delle fonti di errore più comuni.
I ruoli definiscono cosa è un elemento. Un ruolo risponde alla domanda: che tipo di cosa sto guardando? Esempi includono button, dialog, navigation, tablist e progressbar. Applichi un ruolo con l’attributo role: <div role='button'>. Il ruolo comunica lo scopo dell’elemento alla tecnologia assistiva, così l’utente sa come interagirci.
Gli stati descrivono la condizione dinamica di un elemento — qualcosa che cambia man mano che l’utente interagisce con la pagina. L’attributo aria-expanded dice a uno screen reader se una sezione comprimibile è aperta o chiusa. aria-checked riflette se una checkbox personalizzata è selezionata. Gli stati devono essere mantenuti sincronizzati con JavaScript; un aria-expanded='false' statico che non cambia mai non è solo inutile, ma attivamente fuorviante.
Le proprietà forniscono informazioni descrittive, di solito più stabili, su un elemento. aria-label assegna a un elemento un nome accessibile che sovrascrive il suo testo visibile. aria-labelledby punta a un altro elemento il cui testo funge da etichetta. aria-describedby collega a un testo descrittivo supplementare. aria-required segnala che un campo di un form deve essere compilato. Mentre ci si aspetta che gli stati cambino frequentemente, le proprietà tendono a essere impostate una volta e lasciate così — anche se ci sono eccezioni.
I ruoli definiscono cosa è un elemento. Gli stati definiscono come si sta comportando in questo momento. Le proprietà forniscono ulteriore contesto descrittivo. Hai bisogno che tutti e tre lavorino insieme per produrre un componente personalizzato pienamente accessibile.
La regola d’oro — e perché conta più di quanto pensi
La prima regola del W3C sull’uso di ARIA è inequivocabile: se puoi usare un elemento o un attributo HTML nativo con le semantiche e il comportamento di cui hai bisogno già integrati, usalo. Non ricorrere ad ARIA per prima cosa. Questo è talvolta chiamato il principio "nessuna ARIA è meglio di una ARIA sbagliata" — un’espressione che riflette il pericolo molto reale di un uso ben intenzionato ma scorretto di ARIA.
Gli elementi HTML nativi portano con sé gratuitamente semantiche ARIA implicite. Un elemento <button> è già esposto all’Accessibility Tree come un pulsante. È già focalizzabile da tastiera. Già reagisce sia al tasto Invio che alla barra spaziatrice. Già annuncia la sua etichetta. Nel momento in cui scrivi <div role='button'>, ti assumi la responsabilità di ricreare manualmente tutto quel comportamento — la gestione della tastiera, la gestione del focus, l’aggiornamento degli stati — in JavaScript. Non è una preoccupazione teorica. Dimenticare il supporto da tastiera su un pulsante personalizzato è uno degli errori ARIA più comuni e più dannosi in produzione.
I casi in cui ARIA è davvero necessaria tendono a concentrarsi attorno a pochi scenari: quando stai costruendo un widget complesso che non ha un equivalente HTML (un carosello, una combobox con autocompletamento, una tree view); quando stai correggendo markup legacy in cui ristrutturare il DOM è troppo costoso; quando stai costruendo un web component che deve esporre semantiche personalizzate; o quando il supporto da parte dei browser e delle tecnologie assistive per un elemento nativo è così incoerente che l’equivalente ARIA funziona in pratica in modo più affidabile.
Al di fuori di questi scenari, il tuo primo istinto dovrebbe essere sempre l’HTML semantico. Usa <nav> invece di <div role='navigation'>. Usa <main> invece di <div role='main'>. Usa <button> invece di <div role='button'>. Gli elementi nativi sono più robusti, meglio supportati e richiedono molta meno manutenzione.
Un tour delle principali categorie di ruoli ARIA
La specifica WAI-ARIA organizza i ruoli in diverse categorie. Capire queste categorie ti aiuta a sapere a quale ruolo ricorrere e quando.
Ruoli di tipo landmark
I ruoli landmark contrassegnano le principali regioni di una pagina, permettendo agli utenti di screen reader di saltare direttamente alle sezioni chiave usando scorciatoie da tastiera. I ruoli landmark più usati sono banner, navigation, main, complementary, contentinfo, search e form. Ognuno di questi ha un equivalente HTML nativo diretto: <header>, <nav>, <main>, <aside>, <footer> e così via. In pratica, questo significa che i ruoli landmark sono quasi sempre ridondanti se stai usando HTML semantico moderno. Aggiungili solo quando sei bloccato con markup non semantico per ragioni strutturali.
<!-- Preferisci questo -->
<header>
<nav>...</nav>
</header>
<main>...</main>
<footer>...</footer>
<!-- Usa ARIA solo quando devi usare i div -->
<div role='banner'>
<div role='navigation'>...</div>
</div>
<div role='main'>...</div>
<div role='contentinfo'>...</div>
Ruoli di tipo widget
I ruoli di tipo widget descrivono componenti interattivi che l’utente manipola direttamente. È qui che ARIA svolge il suo lavoro più importante, perché molti pattern di widget non hanno un equivalente HTML nativo. I ruoli di widget più comuni includono button, checkbox, dialog, menu, menuitem, slider, tablist, tab, tabpanel, tooltip, tree e combobox.
Quando usi un ruolo di widget, ti assumi la piena responsabilità per l’interazione da tastiera. La WAI-ARIA Authoring Practices Guide (APG) definisce i pattern di tastiera attesi per ogni tipo di widget — per esempio, i tasti freccia per spostarsi tra le schede, Esc per chiudere un dialog, Home e End per saltare al primo e all’ultimo elemento in una listbox. Non implementare questi pattern significa che il tuo componente è tecnicamente etichettato ma funzionalmente inutilizzabile per gli utenti che usano solo la tastiera.
<!-- Un’interfaccia a schede personalizzata -->
<div role='tablist' aria-label='Account settings'>
<button role='tab' aria-selected='true' aria-controls='panel-profile' id='tab-profile'>
Profile
</button>
<button role='tab' aria-selected='false' aria-controls='panel-security' id='tab-security' tabindex='-1'>
Security
</button>
</div>
<div role='tabpanel' id='panel-profile' aria-labelledby='tab-profile'>
<p>Profile settings content</p>
</div>
<div role='tabpanel' id='panel-security' aria-labelledby='tab-security' hidden>
<p>Security settings content</p>
</div>
Ruoli per le live region
Le live region sono una delle funzionalità più utili in assoluto di ARIA. Permettono alle tecnologie assistive di annunciare aggiornamenti dinamici dei contenuti — cose come messaggi di stato, notifiche di errore, messaggi di chat e indicatori di caricamento — agli utenti che non possono vedere il cambiamento sullo schermo. Senza live region, un utente di screen reader che invia un form potrebbe non sapere mai se l’operazione è riuscita o fallita, a meno che il focus non venga spostato esplicitamente sul risultato.
I ruoli principali delle live region sono alert, status, log, marquee e timer. Il ruolo alert porta con sé un’aria-live='assertive' implicita, il che significa che interrompe immediatamente l’utente — appropriato per errori o avvisi urgenti. Il ruolo status usa aria-live='polite', aspettando che l’utente finisca il compito corrente prima di annunciare — ideale per messaggi di successo e indicatori di avanzamento.
<!-- Messaggio di stato "polite" per feedback non urgenti -->
<div role='status' aria-live='polite' aria-atomic='true'>
<!-- Inserisci dinamicamente il testo qui con JavaScript -->
</div>
<!-- Alert "assertive" per errori che richiedono attenzione immediata -->
<div role='alert'>
Please correct the errors below before submitting.
</div>
La chiave delle live region è che il contenitore deve essere presente nel DOM prima che il contenuto dinamico venga inserito. Una live region che viene creata e popolata simultaneamente viene spesso ignorata dagli screen reader. Crea il contenitore al caricamento della pagina e popolalo con JavaScript man mano che si verificano gli eventi.
Ruoli per la struttura del documento
I ruoli per la struttura del documento — come article, list, listitem, table, row, cell, figure e heading — descrivono l’organizzazione strutturale dei contenuti. La maggior parte di questi è ora superata dagli elementi HTML nativi, e MDN osserva che la maggior parte dei ruoli per la struttura del documento "non dovrebbe più essere usata poiché i browser ora supportano elementi HTML semantici con lo stesso significato." La principale eccezione è quando lavori con ambienti di rendering personalizzati, web component o contenuti basati su SVG in cui gli elementi HTML nativi non sono disponibili.
Proprietà ARIA essenziali che ogni sviluppatore dovrebbe conoscere
Oltre ai ruoli, diverse proprietà ARIA compaiono costantemente nel lavoro di accessibilità reale. Sono quelle a cui ricorrerai più spesso:
- aria-label: Fornisce un nome accessibile per un elemento quando non è disponibile alcuna etichetta testuale visibile o quando il testo visibile è insufficiente. Casi d’uso comuni: pulsanti composti solo da icone, campi di ricerca senza etichetta visibile e pulsanti di chiusura nei modali. Nota che
aria-labelsovrascrive qualsiasi testo visibile o etichetta nativa, quindi usala con cautela sugli elementi che hanno già testo visibile. - aria-labelledby: Punta a uno o più elementi il cui contenuto testuale funge da nome accessibile. È più robusta di
aria-labelper i casi complessi perché il testo dell’etichetta rimane sincronizzato con il contenuto visibile. Accetta un elenco di ID di elementi separati da spazi, e le tecnologie assistive concatenano i testi di riferimento nell’ordine. - aria-describedby: Collega a un testo descrittivo supplementare — non un nome, ma un contesto aggiuntivo. Usala per collegare i campi di un form ai loro messaggi di errore, o per associare un tooltip all’elemento che descrive. Gli screen reader di solito annunciano questo dopo il nome e il ruolo dell’elemento.
- aria-hidden: Rimuove completamente un elemento dall’Accessibility Tree. Inestimabile per icone decorative, contenuti duplicati ed elementi solo visivi che creerebbero rumore per gli utenti di screen reader. Non applicare mai
aria-hidden='true'a un elemento focalizzabile — un utente potrebbe comunque raggiungerlo con il tab, ma non riceverebbe alcuna informazione al riguardo. - aria-expanded: Comunica se un elemento comprimibile — un menu a discesa, un accordion o un widget di tipo disclosure — è attualmente aperto o chiuso. Deve essere attivato e disattivato dinamicamente con JavaScript; un valore statico è peggiore che omettere del tutto l’attributo.
- aria-current: Indica l’elemento corrente all’interno di un insieme, usato più comunemente per la navigazione per contrassegnare il link alla pagina attiva (
aria-current='page') o la fase corrente in un processo a più step.
Errori ARIA comuni che in realtà danneggiano l’accessibilità
Dato che le pagine con ARIA tendono a mostrare più errori di accessibilità rispetto a quelle senza, vale la pena essere espliciti su ciò che va storto più spesso. Questi non sono casi limite — sono pattern che compaiono nel codice di produzione ogni giorno.
Usare ruoli ARIA su elementi con semantica nativa forte. Alcuni elementi HTML hanno quella che la specifica chiama "semantica nativa forte" — significati profondamente integrati nel browser e che non possono essere sovrascritti in modo sicuro. Applicare un ruolo inappropriato a un <button> o a un <input> può portare il browser a ignorare del tutto il ruolo ARIA, o a produrre un comportamento contraddittorio che confonde le tecnologie assistive. Il ruolo che dichiari deve essere appropriato per l’elemento su cui lo applichi.
Dimenticare il supporto da tastiera con i ruoli di widget. Il role='button' di ARIA dice a uno screen reader che l’elemento è un pulsante. Non rende l’elemento utilizzabile da tastiera. Se usi un <div> con role='button', devi aggiungere tabindex='0' per renderlo focalizzabile, e devi aggiungere listener di eventi sia per il tasto Invio che per la barra spaziatrice. Se manca una qualsiasi di queste parti, l’esperienza si rompe per gli utenti che usano solo la tastiera.
<!-- Incompleto e inaccessibile -->
<div role='button' onclick='doSomething()'>Submit</div>
<!-- Implementazione corretta di un pulsante personalizzato -->
<div
role='button'
tabindex='0'
onclick='doSomething()'
onkeydown='if(event.key==="Enter"||event.key===" ")doSomething()'
>Submit</div>
<!-- Oppure, la risposta giusta: usa semplicemente un button -->
<button onclick='doSomething()'>Submit</button>
Usare aria-hidden su elementi focalizzabili. Applicare aria-hidden='true' a un elemento focalizzabile lo nasconde dall’Accessibility Tree ma non dalla navigazione da tastiera. Un utente che usa la tastiera può comunque raggiungerlo con il tab, non ricevere alcuna informazione al riguardo e non avere modo di sapere cosa faccia. Questo è un fallimento WCAG 2.1 rispetto al criterio di successo 4.1.2 (Name, Role, Value).
Stati ARIA obsoleti. Non aggiornare aria-expanded, aria-checked, aria-selected e stati simili quando l’interfaccia cambia lascia gli utenti di screen reader con un quadro fondamentalmente errato dell’interfaccia. Un menu che si apre visivamente ma il cui trigger continua a riportare aria-expanded='false' è attivamente ingannevole.
Ruoli ridondanti. Aggiungere role='navigation' a un elemento <nav>, o role='button' a un <button>, non fa nulla di utile. Appesantisce il codice e può occasionalmente confondere alcune combinazioni di tecnologie assistive. Fidati della semantica nativa.
ARIA e WCAG: capire la connessione
ARIA non è WCAG. Sono specifiche separate che lavorano insieme. WCAG (Web Content Accessibility Guidelines) definisce il cosa — i risultati richiesti per contenuti accessibili. ARIA fa parte del come — un meccanismo tecnico per raggiungere alcuni di questi risultati. Il criterio di successo WCAG più rilevante per ARIA è 4.1.2: Name, Role, Value, che richiede che tutti i componenti dell’interfaccia utente abbiano un nome, espongano il loro ruolo alle tecnologie assistive e comunichino il loro stato e le loro proprietà in modo programmabile. ARIA è uno degli strumenti principali per soddisfare questo criterio per i componenti personalizzati.
ARIA supporta anche diversi altri criteri di successo. I ruoli landmark contribuiscono al 2.4.1 (Bypass Blocks) abilitando la navigazione di salto. Le live region sono spesso lo strumento giusto per soddisfare il 4.1.3 (Status Messages) in WCAG 2.1, che richiede che i messaggi di stato siano determinabili in modo programmabile senza ricevere il focus. L’uso corretto di aria-label e aria-labelledby aiuta a soddisfare il 2.4.6 (Headings and Labels) e l’1.3.1 (Info and Relationships).
Vale la pena notare che la conformità a WCAG è sempre più un requisito legale. L’European Accessibility Act è entrato pienamente in vigore a giugno 2025, estendendo i requisiti di accessibilità obbligatori a un’ampia gamma di servizi digitali del settore privato negli Stati membri dell’UE. Negli Stati Uniti, l’ADA continua a essere interpretato come comprensivo dell’accessibilità web, e i requisiti federali ai sensi della Section 508 si applicano alle organizzazioni governative e finanziate a livello federale. Capire ARIA correttamente non è solo una best practice — è sempre più parte dei tuoi obblighi di conformità.
Testare la tua implementazione ARIA
L’unico modo per sapere se la tua implementazione ARIA funziona davvero è testarla con tecnologie assistive reali. Strumenti automatici come axe, WAVE e Lighthouse possono rilevare violazioni strutturali — una proprietà obbligatoria mancante, un ruolo non valido, un aria-hidden applicato a un elemento focalizzabile — ma non possono dirti se uno screen reader annuncia il tuo modal in modo sensato, o se la navigazione da tastiera attraverso il tuo widget tree personalizzato segue i pattern attesi.
Per i test manuali, gli screen reader principali da coprire sono JAWS e NVDA su Windows (insieme rappresentano la grande maggioranza dell’uso di screen reader desktop) e VoiceOver su macOS e iOS. TalkBack copre Android. Ogni combinazione di screen reader e browser può comportarsi in modo diverso, quindi è fortemente consigliato testare almeno due combinazioni. Testa ogni stato interattivo: apri il dialog, espandi l’accordion, seleziona l’opzione, attiva l’alert. Conferma che l’annuncio corrisponda a ciò che un utente vedente capirebbe guardando la stessa interfaccia.
Quando testi widget personalizzati, segui il modello di interazione da tastiera definito nella WAI-ARIA Authoring Practices Guide per quel tipo di widget. Se il tuo tablist non risponde ai tasti freccia, o il tuo dialog non intrappola il focus, questi sono fallimenti indipendentemente da quanto il markup ARIA sembri corretto in un audit automatico.
Punti chiave
- Preferisci sempre l’HTML semantico ad ARIA. Gli elementi nativi come
<button>,<nav>,<main>e<dialog>portano semantiche di accessibilità integrate che sono più robuste e richiedono molto meno codice rispetto agli equivalenti ARIA-su-div. Ricorri ad ARIA solo quando l’HTML nativo è davvero insufficiente. - I ruoli ARIA sono una promessa, non una scorciatoia. Applicare
role='button'orole='dialog'a un elemento personalizzato ti impegna a implementare il modello completo di interazione da tastiera per quel tipo di widget. Ruoli senza comportamento corrispondente creano confusione e fallimenti WCAG. - Tieni gli stati ARIA sincronizzati con la tua UI. Attributi dinamici come
aria-expanded,aria-checked,aria-selectede i contenutiaria-livedevono essere aggiornati in JavaScript man mano che l’interfaccia cambia. Uno stato obsoleto è attivamente dannoso — comunica informazioni sbagliate all’utente. - Usa le live region per gli aggiornamenti dinamici dei contenuti. Qualsiasi contenuto che si aggiorna senza un ricaricamento della pagina — notifiche, messaggi di errore, stati di caricamento, feed di chat — ha bisogno di una regione
aria-liveo di un ruolo appropriato comealertostatusaffinché gli utenti di screen reader ricevano automaticamente le stesse informazioni che vedono gli utenti vedenti. - Testa con tecnologie assistive reali, non solo con strumenti automatici. Gli scanner automatici rilevano errori strutturali ARIA ma non possono verificare se la tua implementazione produce un’esperienza coerente e utilizzabile. I test manuali con JAWS, NVDA e VoiceOver sono l’unico modo per colmare questo divario.
