Attributi ARIA e accessibilità: creare un menu a schede accessibile

by Anastasia P.
SHARE
Attributi ARIA e accessibilità: creare un menu a schede accessibile
© Guida-HTML5.it

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.

SHARE