Introduzione
Quando si parla di accessibilità in HTML, spesso si pensa subito ai moduli o ai contenuti dinamici. Un caso molto pratico, però, è quello delle schede o tab: interfacce usate ovunque, dai pannelli di impostazioni ai dashboard, fino alle pagine prodotto con sezioni separate.
Un menu a schede ben fatto deve funzionare non solo con il mouse, ma anche con la tastiera e con le tecnologie assistive, come gli screen reader. Qui entrano in gioco gli attributi ARIA, che permettono di descrivere il ruolo e lo stato degli elementi quando l’HTML semantico da solo non basta.
In questo tutorial vedremo come costruire un tabbed interface accessibile, usando ruoli ARIA, relazioni tra elementi e gestione dello stato attivo. L’obiettivo non è “aggiungere ARIA a caso”, ma usarla in modo mirato per comunicare chiaramente la struttura dell’interfaccia.
Codice completo
<div class="tabs">
<div role="tablist" aria-label="Sezioni del prodotto">
<button
role="tab"
id="tab-descrizione"
aria-selected="true"
aria-controls="panel-descrizione"
tabindex="0">
Descrizione
</button>
<button
role="tab"
id="tab-specifiche"
aria-selected="false"
aria-controls="panel-specifiche"
tabindex="-1">
Specifiche
</button>
<button
role="tab"
id="tab-recensioni"
aria-selected="false"
aria-controls="panel-recensioni"
tabindex="-1">
Recensioni
</button>
</div>
<section
role="tabpanel"
id="panel-descrizione"
aria-labelledby="tab-descrizione">
<h3>Descrizione</h3>
<p>
Questo prodotto è progettato per offrire prestazioni elevate e un’interfaccia semplice da usare.
</p>
</section>
<section
role="tabpanel"
id="panel-specifiche"
aria-labelledby="tab-specifiche"
hidden>
<h3>Specifiche tecniche</h3>
<ul>
<li>Processore: octa-core</li>
<li>Memoria: 16 GB</li>
<li>Archiviazione: 512 GB</li>
</ul>
</section>
<section
role="tabpanel"
id="panel-recensioni"
aria-labelledby="tab-recensioni"
hidden>
<h3>Recensioni utenti</h3>
<p>
Gli utenti apprezzano la velocità del dispositivo e la qualità del display.
</p>
</section>
</div>
<script>
const tabs = document.querySelectorAll(´[role="tab"]´);
const panels = document.querySelectorAll(´[role="tabpanel"]´);
function activateTab(tab) {
tabs.forEach((t) => {
const selected = t === tab;
t.setAttribute(´aria-selected´, selected);
t.tabIndex = selected ? 0 : -1;
});
panels.forEach((panel) => {
panel.hidden = panel.id !== tab.getAttribute(´aria-controls´);
});
tab.focus();
}
tabs.forEach((tab) => {
tab.addEventListener(´click´, () => activateTab(tab));
tab.addEventListener(´keydown´, (event) => {
const index = Array.from(tabs).indexOf(tab);
let nextIndex = null;
if (event.key === ´ArrowRight´) {
nextIndex = (index + 1) % tabs.length;
} else if (event.key === ´ArrowLeft´) {
nextIndex = (index - 1 + tabs.length) % tabs.length;
} else if (event.key === ´Home´) {
nextIndex = 0;
} else if (event.key === ´End´) {
nextIndex = tabs.length - 1;
}
if (nextIndex !== null) {
event.preventDefault();
activateTab(tabs[nextIndex]);
}
});
});
</script> Spiegazione
Questo esempio implementa un pattern molto comune: un insieme di schede che controllano contenuti diversi. La parte importante non è solo l’aspetto visivo, ma il modo in cui l’interfaccia viene annunciata e navigata.
1. Il contenitore delle schede
Il blocco con role="tablist" indica agli assistive technology che gli elementi interni fanno parte di un gruppo di schede. L’attributo aria-label fornisce un nome descrittivo al gruppo, utile quando ci sono più tablist nella stessa pagina.
2. Ogni scheda è un tab
Ogni pulsante ha role="tab", così viene interpretato come una scheda e non come un semplice bottone generico. Usiamo un <button> perché è già accessibile di default: supporta tastiera, focus e comportamento semantico corretto. ARIA qui serve a specificare il ruolo nel pattern, non a sostituire HTML valido.
Gli attributi più importanti sono:
- aria-selected: indica quale scheda è attiva.
- aria-controls: collega la scheda al pannello che controlla.
- tabindex: gestisce l’ordine di focus tra le schede.
3. I pannelli di contenuto
Ogni sezione contenuta nel tab ha role="tabpanel". Questo dice allo screen reader che si tratta del contenuto associato alla scheda selezionata. L’attributo aria-labelledby collega il pannello alla sua scheda, così il nome del pannello può essere annunciato correttamente.
Per i pannelli non attivi usiamo hidden, che li rimuove sia dalla visualizzazione sia dall’albero di accessibilità. È una scelta molto pulita per evitare che contenuti nascosti vengano letti o raggiunti accidentalmente.
4. Gestione della tastiera
Un’interfaccia a schede accessibile deve supportare la navigazione con le frecce. Nel codice, ArrowRight e ArrowLeft spostano la selezione tra le schede, mentre Home e End portano rispettivamente alla prima e all’ultima scheda.
Questo comportamento è importante perché riduce il numero di tasti necessari per esplorare l’interfaccia e rispetta le convenzioni attese dagli utenti di tecnologie assistive.
5. Stato attivo e focus
Quando una scheda viene attivata, il codice aggiorna aria-selected e imposta tabIndex in modo coerente. Solo la scheda attiva rimane nel flusso del tab key con tabindex="0", mentre le altre diventano raggiungibili solo tramite frecce.
Infine, la chiamata a focus() mantiene il focus sulla scheda selezionata, così l’utente capisce subito dove si trova.
Best practice
- Preferisci HTML semantico quando possibile. ARIA non deve sostituire elementi nativi già accessibili.
- Usa <button> per le schede. Evita <div> cliccabili: richiederebbero più lavoro per essere accessibili.
- Collega sempre tab e pannello. Usa aria-controls e aria-labelledby per creare relazioni chiare.
- Aggiorna lo stato in modo coerente. Se una scheda è selezionata, anche il suo pannello deve essere visibile e gli altri nascosti.
- Testa con tastiera e screen reader. L’accessibilità reale si verifica con l’uso, non solo leggendo il codice.
- Non lasciare contenuti nascosti nel flusso di lettura. Usa hidden o tecniche equivalenti, non solo CSS visivo.
- Evita ARIA superflua. Attributi errati o inutili possono peggiorare l’esperienza invece di migliorarla.
Riepilogo
Le schede sono un ottimo esempio di come HTML e ARIA possano lavorare insieme. L’HTML fornisce elementi interattivi solidi, mentre ARIA aggiunge le informazioni necessarie per descrivere struttura, relazioni e stato dell’interfaccia.
In questo tutorial abbiamo visto come:
- definire una tablist accessibile;
- marcare correttamente le tab e i tabpanel;
- gestire aria-selected, aria-controls e aria-labelledby;
- supportare la navigazione da tastiera con frecce, Home ed End;
- nascondere i pannelli inattivi in modo corretto.
Se il tuo progetto usa interfacce a schede, applicare questo pattern ti aiuta a costruire componenti più robusti, inclusivi e professionali.
Approfondisci con risorse ufficiali
- WAI-ARIA Authoring Practices: guida ufficiale ai pattern accessibili per componenti complessi.
- MDN Web Docs - ARIA: documentazione pratica sugli attributi e sui ruoli ARIA.
- MDN Web Docs - Keyboard accessible: risorse utili per progettare interazioni da tastiera corrette.
- WCAG 2.2: standard di riferimento per i requisiti di accessibilità web.
