Conlaccento

Un blog sul web making di Antonio Trifirò

Il contrasto che i tool di accessibilità non vedono: ho fatto un'estensione Chrome per gli stati


Ti è mai capitato di passare tutti i controlli automatici di accessibilità — usando axe, Lighthouse, WAVE — vederli verdi dappertutto, e poi accorgerti guardando la pagina che l’anello di focus è quasi invisibile, o che il testo di un link sbiadisce quando ci passi sopra col mouse?

Non è colpa tua e non è colpa degli strumenti: è che stanno guardando la pagina nel momento sbagliato. E siccome quella cosa mi dava fastidio da un po’, mi sono deciso e ho scritto un’estensione Chrome per stanarla: si chiama A11y State Contrast Checker.

Il punto cieco: gli strumenti guardano solo lo stato “a riposo”

axe-core, Lighthouse e WAVE ispezionano il DOM così com’è nel momento della scansione: lo stato a riposo della pagina. È lì che misurano il contrasto del testo, dei bordi, delle icone.

Il problema è che i problemi di contrasto possono non esistere, a riposo, compaiono solo quando un elemento è nello stato :hover, :focus o :focus-visible:

  • un colore di testo che all’hover diventa un grigio troppo tenue;
  • un anello di focus dal contrasto insufficiente contro lo sfondo (l’ho raccontato per esteso in Basta :focus { outline: none; });
  • un’icona che si “lava via” quando ci passi sopra.

Nessuno di questi stati è attivo mentre lo strumento automatico fa la sua scansione. Risultato: quelle violazioni passano inosservate e finiscono in produzione col bollino verde. È un cugino stretto del problema di cui parlavo a proposito di APCA e del contrasto calcolato male: il contrasto è misurabile, ma solo se lo misuri nel contesto giusto.

Perché è nata l’estensione

Certo, puoi controllare gli stati a mano. Apri i DevTools, forzi :hover su un elemento dal pannello degli stili, leggi il colore del testo, leggi quello dello sfondo, li dai in pasto a un calcolatore di contrasto, segni se passa. Poi ripeti per :focus. Poi passi all’elemento dopo. E a quello dopo ancora.

È un lavoro tedioso, lento e facilissimo da dimenticare — proprio perché è tedioso. Ed è esattamente da questa frustrazione che è nata l’idea: se un controllo è così noioso da fare a mano, prima o poi smetti di farlo. Meglio darlo in pasto a una macchina.

Cosa fa A11y State Contrast Checker

L’estensione fa una cosa sola: controlla il contrasto WCAG negli stati interattivi. La apri su una pagina qualsiasi, premi Run audit, e ti dice dove hover e focus rompono il contrasto.

Sotto il cofano non reimplementa la cascata CSS (una via lastricata di errori): forza i veri pseudo-stati usando il motore di rendering di Chrome e poi legge gli stili calcolati. Così specificità, !important, custom properties e media query sono risolte correttamente dal browser stesso — non da una mia approssimazione.

I controlli sono tre:

  • 1.4.3 Contrasto minimo — testo contro sfondo su :hover e :focus (4.5:1, oppure 3:1 per il testo grande).
  • 1.4.11 Contrasto non testuale — l’anello di focus contro ciò che lo circonda (3:1).
  • 1.4.11 sulle icone — per i controlli con la sola icona, il fill/stroke dell’SVG contro lo sfondo (3:1).

Il senso è tutto in quel micro-esempio che passa a riposo e fallisce nello stato:

.link {
  color: #1f7a5c; /* a riposo: ottimo contrasto sul bianco, passa */
}

.link:hover {
  color: #9cc7b8; /* all'hover: sbiadisce, ~1.8:1, fallisce — ma nessuno strumento se ne accorge */
}

Il calzolaio con le scarpe bucate

Sai qual è stata la prima pagina su cui ho lanciato l’estensione? Questo blog.

E ovviamente — legge di Murphy dell’accessibilità — ho trovato dei fail proprio sugli stati interattivi. Il classico calzolaio con le scarpe bucate: scrivo di contrasto e di focus, e intanto il mio stesso sito aveva degli stati che non passavano. Umiliante e istruttivo insieme.

La buona notizia è che li ho sistemati: i fix al design sono online da oggi, insieme a questo post. Se lo strumento è servito prima di tutto a me, forse serve anche a te.

Come si usa, in 3 passi

  1. Apri la pagina che vuoi controllare.
  2. Clicca l’icona dell’estensione e premi Run audit.
  3. Leggi la tabella dei risultati: elemento, stato, controllo WCAG, rapporto misurato, rapporto richiesto, esito. Clicca una riga per far scorrere la pagina fino all’elemento e evidenziarlo, così lo verifichi con l’occhio.

Un dettaglio che spiazza al primo utilizzo: mentre l’audit gira, Chrome mostra la sua barra gialla ”… ha avviato il debug di questo browser”. È previsto e non è un problema: è proprio così che l’estensione forza gli stati interattivi. Sparisce appena la scansione finisce.

Sii consapevole dei limiti

Non è una bacchetta magica, e preferisco dirtelo io. Al momento:

  • risolve lo sfondo guardando solo i livelli background-color (non immagini né gradienti);
  • non analizza gli iframe cross-origin;
  • sulle pagine molto grandi si ferma a un tetto di elementi e restituisce risultati parziali.

Resta uno strumento che copre un buco preciso, non un sostituto di axe o Lighthouse: quelli continuano a fare il loro lavoro sullo stato a riposo, questo aggiunge il pezzo mancante sugli stati.

Da non dimenticare, sugli stati interattivi

  • Il colore del testo cambia su :hover? Se sì, hai verificato che il nuovo colore passi 4.5:1?
  • L’anello di focus (outline, o il box-shadow usato come anello) ha almeno 3:1 contro lo sfondo su cui compare?
  • I controlli con la sola icona mantengono 3:1 anche all’hover, quando spesso il colore cambia?
  • Stai controllando :focus e :focus-visible, che possono avere stili diversi?
  • Hai fatto questo controllo su sfondi diversi (header scuro, card chiara, footer…), non solo sul bianco della pagina?

Provala (e dimmi com’è andata)

A11y State Contrast Checker è open source (licenza MIT), non raccoglie alcun dato e non fa nessuna richiesta di rete: gira tutto in locale. La trovi su GitHub, con le istruzioni di installazione nel README:

👉 github.com/conlaccento/a11y-state-contrast-checker

In questo momento è anche in fase di verifica sul Chrome Web Store: appena approvata potrai installarla con un clic, senza passare dal caricamento manuale. Nel frattempo la strada da GitHub funziona benissimo.

Se la provi e trovi fail interessanti sui tuoi progetti — o se becchi un bug nell’estensione — scrivimi: il feedback è la cosa che la fa migliorare più in fretta.

Peace&Love! ✌️