Accessibilità
Contrasto: strumenti e soglie
Il contrasto si misura in rapporto tra luminanze: 4.5:1 per il testo normale e 3:1 per quello grande. Il criterio WCAG 1.4.3, livello AA, fissa le soglie.
In sintesi
- Il criterio 1.4.3 Contrast (Minimum), livello AA, chiede 4.5:1 per il testo normale e 3:1 per quello grande.
- Il testo grande è almeno 24 px, oppure 18.66 px in grassetto; sotto quella misura serve 4.5:1.
- Il rapporto si calcola sulla luminanza relativa sRGB: gradienti e trasparenze vanno misurati sul colore effettivo.
Il contrasto è il rapporto tra la luminanza del testo e quella dello sfondo: il criterio 1.4.3 Contrast (Minimum), livello AA, chiede 4.5:1 per il testo normale e 3:1 per il testo grande. Uno strumento calcola il numero; la soglia dipende dal ruolo dell'elemento.
Le soglie
- Testo normale: 4.5:1 con 1.4.3, livello AA; 7:1 con 1.4.6 Contrast (Enhanced), livello AAA.
- Testo grande: 3:1, dove grande significa almeno 24 px oppure 18.66 px in grassetto.
- Componenti e icone: 3:1 con 1.4.11 Non-text Contrast, livello AA, per bordi, stati e simboli che portano informazione.
- Indicatore di focus: 3:1 tra stato con focus e senza, con un'area minima, per 2.4.13 Focus Appearance, livello AAA.
Il calcolo in una riga
node -e "const L=h=>{const c=[1,3,5].map(i=>parseInt(h.slice(i,i+2),16)/255).map(v=>v{const[x,y]=[L(a),L(b)].sort((m,n)=>n-m);return((x+0.05)/(y+0.05)).toFixed(2)};console.log('#767676 on white',R('#767676','#ffffff'));console.log('#777777 on white',R('#777777','#ffffff'))"
Il primo grigio passa con 4.54:1, il secondo si ferma a 4.48:1 e non basta per il testo normale. La differenza tra i due colori è un valore esadecimale, non una percezione: per questo il controllo va fatto sul valore calcolato, non a occhio. La formula usa la luminanza relativa sRGB definita in WCAG 2.2, W3C Recommendation del 5 ottobre 2023, pubblicata con W3C Document License.
Dove il numero inganna
Tre casi falsano la misura. Un testo sopra un'immagine o un gradiente non ha un solo sfondo: va misurato nel punto peggiore. L'opacità e color-mix() cambiano il colore effettivo, quindi il valore nel CSS non è quello renderizzato. Un font sottile a 16 px resta testo normale anche se il design lo tratta come grande.
Gli strumenti
Lighthouse, licenza Apache-2.0, segnala gli elementi sotto soglia nella pagina renderizzata e usa axe-core, licenza MPL-2.0, per l'audit di accessibilità. Il Contrast Checker di WebAIM e il Colour Contrast Analyser di TPGi calcolano il rapporto di una coppia di colori. Nessuno dei tre conosce il ruolo del testo: la scelta tra 4.5:1 e 3:1 resta a chi scrive il markup.
npx --yes lighthouse https://example.com --only-categories=accessibility --chrome-flags="--headless" --output=json --output-path=./a11y.json
Il report elenca i nodi con contrasto insufficiente e il rapporto calcolato. Un elemento non elencato non è automaticamente conforme: testo su canvas, shadow DOM chiuso e stati hover non vengono analizzati.
Il catalogo EneUI
EneUI indicizza le palette, la categoria accessibilità e il set icons di shadcn, licenza MIT del progetto shadcn/ui: un'icona che porta informazione ricade in 1.4.11 e va verificata a 3:1. Controllo del 1 novembre 2026 sul catalogo da 111.985 componenti.
La limitazione
Il rapporto di contrasto non misura la leggibilità. Non tiene conto della dimensione effettiva dopo lo zoom, della spaziatura, del rendering dei font né della perdita di contrasto percepita da chi ha una cataratta. Rispettare 4.5:1 è il minimo verificabile, non la prova che il testo si legga.
Dove continuare
Il metodo per misurare una palette è in contrasto misurato in una palette UI; l'indicatore di focus è in il focus ring invisibile. Il confronto tra primitive headless è in Radix, Base UI e React Aria.
Domande frequenti
4.5:1 basta sempre?
No. Vale per il testo normale; il testo grande si ferma a 3:1 e i componenti non testuali a 3:1. Per il livello AAA la soglia del testo normale sale a 7:1.
Che cos'è il testo grande?
Almeno 24 px, oppure 18.66 px in grassetto. Sotto queste misure il criterio 1.4.3 chiede 4.5:1.
Il contrasto si misura sul CSS o sullo schermo?
Sul colore effettivamente renderizzato. Opacità, gradienti e immagini di sfondo cambiano il risultato, quindi il valore scritto nel CSS non basta.
Lighthouse trova tutti gli errori di contrasto?
No. Analizza il DOM renderizzato e salta canvas, shadow DOM chiuso e stati dinamici come hover e focus. Un report pulito non chiude il controllo.
Il nero su bianco è sempre la scelta migliore?
Ha il rapporto più alto, 21:1, ma non è sempre la scelta giusta per chi ha dislessia o fotofobia. Il criterio chiede una soglia minima, non un colore specifico.