Diego Betto
Foto di Compagnons su Unsplash

Diego Betto · 13 ottobre 2026 · 14 min di lettura

European Accessibility Act e WCAG 2.2: guida pratica alla conformità per sviluppatori

Cosa chiede l'European Accessibility Act, chi è obbligato, sanzioni in Italia e come rendere un sito conforme a WCAG 2.2: tastiera, contrasti, screen reader, ARIA e test.

Condividi:XLinkedInFacebookWhatsApp

Dal 28 giugno 2025 l’accessibilità digitale non è più una buona pratica da inserire “se avanza tempo” per buona parte dei servizi online venduti ai consumatori nell’Unione Europea: è un requisito di legge, con un’autorità che vigila e sanzioni previste. È l’effetto dell’European Accessibility Act (EAA, Direttiva UE 2019/882), recepito in Italia con il D.Lgs. 82/2022.

Un anno e qualche mese dopo, molti siti sono ancora fermi a “abbiamo installato un widget”. Questo articolo serve a due cose: capire in modo concreto cosa chiede la norma e a chi, e poi — la parte più lunga — tradurla in codice: HTML, CSS, ARIA e test automatici.

ℹ️ Nota

Non sono un avvocato e questo non è un parere legale. Per capire se la tua azienda rientra nell’ambito di applicazione, e con quali eccezioni, la fonte sono il testo del decreto e le Linee guida AgID. Qui mi concentro sulla parte tecnica.

Chi deve adeguarsi

L’EAA copre un elenco preciso di prodotti e servizi forniti ai consumatori. Per chi sviluppa web, la parte che conta sono i servizi:

Servizio Esempi di cosa deve essere accessibile
Commercio elettronico Qualsiasi e-commerce B2C: catalogo, carrello, checkout, account
Servizi bancari ai consumatori Home banking, app, apertura conto, pagamenti
Trasporto passeggeri (aereo, treno, bus, nave) Siti e app di prenotazione, biglietti elettronici, info in tempo reale
Comunicazioni elettroniche Area clienti di operatori telefonici e internet
Accesso a servizi media audiovisivi Piattaforme di streaming, guide ai programmi
E-book Store, reader, software dedicato

Tre cose da sapere subito:

  • B2B escluso. La norma riguarda i servizi ai consumatori. Un gestionale venduto solo ad aziende non rientra (ma se ha un’area usata dai clienti finali, ragionaci).
  • Microimprese esentate, ma solo per i servizi. Meno di 10 dipendenti e fatturato o bilancio annuo non superiore a 2 milioni di euro: niente obbligo per i servizi. Per i prodotti l’esenzione non vale.
  • Periodo transitorio. I contratti di servizio stipulati prima del 28 giugno 2025 possono proseguire fino alla scadenza, per un massimo di cinque anni; i prodotti già usati per erogare il servizio prima di quella data possono restare in uso fino al 28 giugno 2030. Non è un rinvio generale: un sito o un’app rilasciati o rifatti dopo giugno 2025 devono essere conformi.

Esiste anche l’eccezione dell’onere sproporzionato: se adeguarsi costa troppo rispetto alle dimensioni e alle risorse dell’azienda, la si può invocare, ma va documentata con una valutazione scritta, conservata e rinnovata periodicamente. Non è un “no” gratuito.

Chi controlla e cosa si rischia in Italia

Per i servizi l’autorità di vigilanza è l’AgID, che a marzo 2026 ha pubblicato le Linee guida operative e gestisce le segnalazioni degli utenti. Per i prodotti la competenza è del Ministero delle Imprese e del Made in Italy.

Le sanzioni previste dal D.Lgs. 82/2022:

Violazione Sanzione amministrativa
Mancato rispetto dei requisiti di accessibilità da 5.000 a 40.000 €
Inottemperanza alle misure correttive dell’autorità da 2.500 a 30.000 €
Mancata collaborazione con l’autorità da 2.500 a 30.000 €

Per i grandi soggetti privati che già ricadevano nella Legge Stanca (fatturato medio oltre 500 milioni di euro) si aggiunge un regime con sanzioni fino al 5% del fatturato. E poi c’è il rischio meno visibile ma più concreto: le segnalazioni di utenti e associazioni, e i clienti che semplicemente non riescono a comprare.

Quale standard tecnico: WCAG 2.1 o 2.2?

L’EAA non cita le WCAG direttamente. Il riferimento tecnico è la norma armonizzata europea EN 301 549, e qui il calendario è in movimento:

  • EN 301 549 v3.2.1 (la versione armonizzata oggi in vigore) rimanda alle WCAG 2.1 livello AA.
  • EN 301 549 v4.1.1, pubblicata dall’ETSI il 2 settembre 2026, si allinea alle WCAG 2.2. Diventa la versione di riferimento quando viene citata nella Gazzetta Ufficiale dell’UE, attesa entro fine anno.

Il consiglio pratico è semplice: punta già a WCAG 2.2 AA. È un sovrainsieme della 2.1 (tranne un criterio rimosso), quindi chi è conforme alla 2.2 è conforme anche alla 2.1, e non dovrai rifare il lavoro tra qualche mese.

Le novità della 2.2, a livello A e AA:

Criterio Livello In pratica
2.4.11 Focus Not Obscured (Minimum) AA L’elemento con il focus non deve finire sotto un header sticky o un banner cookie
2.5.7 Dragging Movements AA Ogni drag & drop deve avere un’alternativa con click singoli
2.5.8 Target Size (Minimum) AA Target cliccabili di almeno 24×24 px (o abbastanza distanziati)
3.2.6 Consistent Help A Contatti e aiuto sempre nello stesso posto tra le pagine
3.3.7 Redundant Entry A Non chiedere due volte gli stessi dati nello stesso processo
3.3.8 Accessible Authentication (Minimum) AA Login senza test cognitivi: incolla e password manager consentiti

Il criterio 4.1.1 Parsing è stato rimosso: i browser moderni gestiscono l’HTML malformato in modo coerente, quindi non era più significativo.

Dalla norma al codice

Le WCAG sono organizzate in quattro principi (percepibile, utilizzabile, comprensibile, robusto), ma nel lavoro quotidiano conviene ragionare per aree. Queste sono quelle dove trovo più problemi negli audit.

1. HTML semantico prima di tutto

La maggior parte dell’accessibilità si ottiene usando l’elemento giusto. Un <button> è focusabile, si attiva con Invio e Spazio, ha ruolo “button” e un nome accessibile. Un <div onclick> non ha nulla di tutto questo, e ricostruirlo a mano richiede role, tabindex, gestione di keydown e stati ARIA.

<header>
  <a class="skip-link" href="#main">Vai al contenuto</a>
  <nav aria-label="Principale">...</nav>
</header>

<main id="main">
  <h1>Scarpe da trail</h1>
  <section aria-labelledby="filtri-title">
    <h2 id="filtri-title">Filtri</h2>
    ...
  </section>
</main>

<footer>...</footer>

Landmark (header, nav, main, footer), un solo h1, una gerarchia di titoli senza salti e un skip link come primo elemento focusabile: chi usa uno screen reader o la tastiera salta direttamente al contenuto invece di attraversare ogni volta il menu.

2. ARIA: meno è meglio

La prima regola di ARIA è non usare ARIA se esiste un elemento nativo che fa la stessa cosa. ARIA cambia solo ciò che viene annunciato, non il comportamento: un role="button" su un div non lo rende cliccabile da tastiera.

Dove serve davvero:

  • aria-label / aria-labelledby per dare un nome a controlli senza testo visibile (un bottone con solo un’icona).
  • aria-expanded e aria-controls su menu e accordion.
  • aria-current="page" sulla voce di navigazione attiva.
  • aria-live per annunciare cambi di stato che non spostano il focus.
  • aria-hidden="true" sulle icone decorative.
<button type="button" aria-expanded="false" aria-controls="menu-mobile">
  <svg aria-hidden="true">...</svg>
  <span class="visually-hidden">Menu</span>
</button>

3. Navigazione da tastiera e focus

Prova il tuo sito senza mouse: Tab, Shift+Tab, Invio, Spazio, Esc, frecce. Se c’è un punto in cui non sai dove sei o non puoi andare avanti, è un problema di livello A.

/* Mai rimuovere l'outline senza sostituirlo */
:focus-visible {
  outline: 3px solid var(--focus-color);
  outline-offset: 2px;
}

/* 2.4.11: il focus non deve finire sotto l'header sticky */
html {
  scroll-padding-top: 5rem; /* altezza dell'header + margine */
}

Per le modali, usa l’elemento <dialog> con showModal(): gestisce in modo nativo il focus trap, la chiusura con Esc e rende inerte il resto della pagina. Era il componente più difficile da fare bene a mano, oggi è gratis.

function ConfirmDialog({ open, onClose, onConfirm }: { open: boolean; onClose: () => void; onConfirm: () => void }) {
  const ref = useRef<HTMLDialogElement>(null);

  useEffect(() => {
    if (open) ref.current?.showModal();
    else ref.current?.close();
  }, [open]);

  return (
    <dialog ref={ref} onClose={onClose} aria-labelledby="dialog-title">
      <h2 id="dialog-title">Eliminare l'indirizzo?</h2>
      <button onClick={onClose}>Annulla</button>
      <button onClick={onConfirm}>Elimina</button>
    </dialog>
  );
}

Evita tabindex positivi (tabindex="3"): stravolgono l’ordine naturale. Usa solo 0 (rendi focusabile) e -1 (focusabile da script, non con Tab).

4. Contrasti e colore

I rapporti minimi per il livello AA:

  • 4.5:1 per il testo normale.
  • 3:1 per il testo grande (dal 24 px, o 18,66 px in grassetto).
  • 3:1 per componenti dell’interfaccia e grafica significativa: bordi dei campi, icone, indicatore di focus.

Il colore non deve mai essere l’unico modo per trasmettere un’informazione: un campo con errore ha bordo rosso e un messaggio testuale; un grafico ha legenda o pattern, non solo tinte diverse.

Il modo più efficace per non sbagliare è risolvere il problema una volta sola, nei design token: se la palette del design system è verificata, ogni componente nasce conforme. Ricordati del tema scuro: è un secondo set di coppie da controllare.

5. Form ed errori

I form sono dove gli utenti con disabilità abbandonano di più, e dove si concentra il valore economico (checkout, registrazione, login).

<label for="email">Email</label>
<input
  id="email"
  name="email"
  type="email"
  autocomplete="email"
  required
  aria-invalid="true"
  aria-describedby="email-error"
/>
<p id="email-error" class="field-error">Inserisci un indirizzo email valido, es. [email protected]</p>
  • Ogni campo ha una <label> visibile e collegata.
  • autocomplete corretto: aiuta tutti, ed è richiesto dal criterio 1.3.5.
  • L’errore è testuale, specifico (dice come correggere) e collegato con aria-describedby.
  • Al submit fallito sposta il focus sul primo campo in errore, o su un riepilogo degli errori.
  • 3.3.7: se l’indirizzo di spedizione è già stato inserito, proponi “uguale alla fatturazione” invece di farlo riscrivere.

6. Login accessibile (3.3.8)

Il criterio 3.3.8 vieta di basare l’autenticazione su test cognitivi senza alternative. In concreto:

  • Non bloccare l’incolla nei campi password (onpaste={e => e.preventDefault()} è una violazione).
  • Usa autocomplete="current-password" / "new-password" così i password manager funzionano.
  • I CAPTCHA “trova i semafori” o i puzzle richiedono un’alternativa.
  • Le passkey sono la soluzione ideale: nessuna password da ricordare né da digitare.

7. Target, movimento e responsive

  • 2.5.8: bottoni e link di almeno 24×24 px. Le icone da 16 px nelle tabelle e i link “×” per chiudere sono i casi classici.
  • 2.5.7: riordini con drag & drop? Aggiungi bottoni “sposta su/giù”.
  • 1.4.10 Reflow: a 320 px di larghezza (lo zoom al 400% di un desktop) niente scroll orizzontale.
  • Animazioni: rispetta prefers-reduced-motion per animazioni e parallax.
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

8. Contenuti dinamici e SPA

In una single page app il cambio di route non viene annunciato agli screen reader come un caricamento di pagina. Dopo la navigazione aggiorna il document.title e sposta il focus sull’h1 della nuova pagina (con tabindex="-1"). Per notifiche, aggiunte al carrello e risultati di ricerca usa una regione live:

<div aria-live="polite" class="visually-hidden" id="status"></div>
document.getElementById("status")!.textContent = "Prodotto aggiunto al carrello (3 articoli)";

La regione deve esistere nel DOM prima che cambi il testo, altrimenti molti screen reader non lo annunciano.

Testare: automatico, manuale, reale

I test automatici trovano solo una parte dei problemi, tipicamente stimata tra un terzo e la metà. Vedono un contrasto insufficiente o un’immagine senza alt; non possono sapere se l’alt descrive davvero l’immagine o se l’ordine del focus ha senso. Ma sono economici e impediscono le regressioni, quindi vanno in CI.

Con Playwright e axe-core:

// tests/a11y.spec.ts
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

const pages = ["/", "/prodotti", "/carrello", "/checkout"];

for (const path of pages) {
  test(`${path} non ha violazioni WCAG A/AA`, async ({ page }) => {
    await page.goto(path);
    const results = await new AxeBuilder({ page })
      .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
      .analyze();

    expect(results.violations).toEqual([]);
  });
}

test("lo skip link è il primo elemento focusabile", async ({ page }) => {
  await page.goto("/");
  await page.keyboard.press("Tab");
  await expect(page.getByRole("link", { name: "Vai al contenuto" })).toBeFocused();
});

Nota l’uso di getByRole: se i tuoi test trovano gli elementi per ruolo e nome accessibile, ogni test end-to-end diventa anche un piccolo test di accessibilità. Lo stesso vale per i test di componente con Testing Library, che ho usato nella guida a Vitest.

Poi i controlli manuali, da fare almeno sui flussi critici:

  1. Solo tastiera su tutto il percorso di acquisto o registrazione.
  2. Screen reader: NVDA su Windows, VoiceOver su macOS e iOS, TalkBack su Android.
  3. Zoom al 200% e larghezza di 320 px.
  4. Contrasto su stati hover, focus, disabled e tema scuro.

E infine, quando possibile, test con utenti reali con disabilità: trovano in mezz’ora cose che nessuna checklist prevede.

⛔ Pericolo

I widget di “accessibilità automatica” (overlay) non rendono un sito conforme. Non correggono il codice sottostante, spesso interferiscono con gli screen reader che gli utenti già usano, e nel 2025 la FTC statunitense ha sanzionato uno dei fornitori più noti con 1 milione di dollari per aver promesso una conformità WCAG che il prodotto non garantiva.

Un processo, non un progetto

L’errore più comune è trattare l’accessibilità come un audit da fare una volta. Il sito cambia ogni settimana, e ogni rilascio può introdurre nuove barriere. Quello che funziona:

  1. Audit iniziale sui template e sui flussi principali (home, listing, scheda prodotto, checkout, login, area utente), non su ogni pagina.
  2. Backlog di remediation ordinato per impatto: prima ciò che blocca un compito (non riesco a pagare), poi ciò che lo rende difficile.
  3. Design system conforme: correggi il componente una volta, si sistemano cento pagine.
  4. Test axe in CI che fanno fallire la build sulle regressioni.
  5. Informazioni sull’accessibilità pubblicate: l’EAA chiede di spiegare come il servizio soddisfa i requisiti, e di offrire un canale per segnalare problemi.
  6. Formazione: designer, sviluppatori e redattori. Un alt vuoto su un’immagine informativa è un problema di contenuto, non di codice.

Checklist rapida

  • Landmark, un solo h1, gerarchia dei titoli corretta
  • Skip link come primo elemento focusabile
  • Tutto raggiungibile e utilizzabile da tastiera, nessuna trappola
  • Focus visibile (:focus-visible) e mai nascosto da header sticky
  • Contrasti 4.5:1 per il testo, 3:1 per componenti e testo grande, anche in dark mode
  • Immagini informative con alt significativo, decorative con alt=""
  • Ogni campo con <label>, autocomplete ed errori collegati
  • Login: incolla consentito, password manager funzionanti, alternative ai CAPTCHA
  • Target di almeno 24×24 px, alternative al drag
  • Nessuno scroll orizzontale a 320 px, rispetto di prefers-reduced-motion
  • Modali con <dialog>, cambi di route e notifiche annunciati
  • Video con sottotitoli, audio con trascrizione
  • Test axe in CI e verifica manuale con screen reader dei flussi critici
  • Informazioni sull’accessibilità e canale di segnalazione pubblicati

Domande frequenti

❓ Il mio sito vetrina deve essere conforme all'EAA?

Un sito puramente informativo, che non vende né eroga uno dei servizi elencati dalla direttiva, di norma non rientra nell’EAA. Se però ha un e-commerce o permette di prenotare e pagare un servizio coperto, sì. In ogni caso un sito accessibile raggiunge più persone, funziona meglio con gli agenti AI e si posiziona meglio: non serve un obbligo per farlo.

❓ Devo seguire le WCAG 2.1 o le 2.2?

Oggi la norma armonizzata in vigore rimanda alle 2.1 AA, ma la nuova EN 301 549 v4.1.1 si allinea alle 2.2. Puntare subito alle 2.2 AA costa poco di più ed evita di rifare il lavoro quando la nuova versione verrà citata in Gazzetta Ufficiale.

❓ Lighthouse a 100 nella sezione Accessibility significa conformità?

No. Lighthouse usa un sottoinsieme delle regole di axe-core e verifica solo ciò che è controllabile automaticamente. Un 100 vuol dire “nessun errore rilevabile da una macchina”, non “utilizzabile da una persona con screen reader”.

❓ L'accessibilità peggiora le prestazioni o la SEO?

Il contrario. HTML semantico, testo invece di immagini, struttura dei titoli chiara e immagini con alt sono le stesse cose che aiutano motori di ricerca e agenti AI. E un sito leggero, che rispetta i Core Web Vitals, è più usabile anche per chi naviga con tecnologie assistive su dispositivi poco potenti.

Conclusione

L’European Accessibility Act ha trasformato l’accessibilità da “nice to have” a requisito, ma la parte tecnica non è cambiata: è ancora HTML semantico, tastiera, contrasti, form fatti bene e test. Se parti dai componenti condivisi, metti axe in CI e verifichi a mano i flussi che portano soldi, sei già nella parte giusta della curva — e il sito diventa migliore per tutti, non solo per chi usa tecnologie assistive.

Riferimenti:

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Scritto da

Diego Betto

Co-Fondatore & CTO presso PAPION. Senior full-stack engineer specializzato in React, TypeScript, Node.js e sicurezza applicativa.