Un revisore ha trovato tre criteri WCAG sbagliati nel mio plugin di accessibilità. Sono andato a guardare, e ne ho trovati otto.

Sviluppo uno scanner di accessibilità per WordPress. La settimana scorsa il revisore di un marketplace me l'ha respinto, e fra le motivazioni c'era questa:

Le regole di conformità vanno corrette. «Lingua dei passaggi» valida soltanto gli attributi lang già presenti e non può rilevare passaggi in lingua straniera non marcati; la regola sul landmark principale è WCAG 1.3.6 di livello AAA ma viene presentata come un controllo AA; e gli id duplicati non dovrebbero essere presentati come una violazione WCAG 2.2 sotto il 4.1.1.

Tre rilievi. Tutti e tre corretti. Quello che segue è cosa è successo quando ho smesso di correggere i tre e ho controllato gli altri ventidue.

La regola che non esiste più

Comincio dal terzo, perché è il più facile da verificare e il più diffusamente sbagliato.

Il criterio di successo 4.1.1 Parsing è stato rimosso dalle WCAG 2.2. Non deprecato, non ammorbidito: rimosso. La Raccomandazione W3C lo elenca nella sezione sulla conformità come «Parsing (Obsolete and removed)». È uscito perché la cosa da cui proteggeva — le tecnologie assistive che si strozzavano su markup malformato — ha smesso di essere una modalità di guasto reale da quando browser e API di accessibilità convergono su come recuperare da HTML sbagliato.

Il mio scanner segnalava gli attributi id duplicati come violazione WCAG 4.1.1. Rispetto alle WCAG 2.2, quel criterio non c'è più per essere violato.

Gli id duplicati restano da correggere. Rompono l'associazione label for e i riferimenti aria-labelledby, quindi viene annunciato l'elemento sbagliato, o nessuno. Ma quello è un problema di 4.1.2 quando rompe davvero un nome accessibile, e rilevarlo è un controllo diverso dal contare gli id duplicati. Quello che avevo era un generico controllo sugli id duplicati che indossava il numero di un criterio ritirato.

Poi ho controllato il resto

Il revisore ne aveva trovati tre. Potevo correggerne tre. Invece ho preso tutte e 25 le regole e ho verificato ciascuna contro la Raccomandazione WCAG 2.2 e contro il modo in cui axe-core classifica la regola equivalente — perché axe-core è l'implementazione di riferimento su cui è costruita buona parte di questo settore, e fa una distinzione che io avevo perso.

Otto regole erano sbagliate.

Regola Dichiarava In realtà
Id duplicati 4.1.1 Rimosso dalle WCAG 2.2
Un solo landmark main 1.3.6 AAA 1.3.6 è Identify Purpose, un criterio scollegato
Lingua dei passaggi 3.1.2 AA 3.1.2 è livello A — e il controllo non faceva ciò che il titolo prometteva
Testo di link generico 2.4.4 Il 2.4.4 è soddisfatto dal contesto circostante
Livelli di intestazione saltati 1.3.1 Le WCAG non impongono livelli sequenziali
h1 mancante 1.3.1 Buona pratica
Intestazione vuota 1.3.1 Buona pratica
tabindex positivo 2.4.3 Buona pratica

Alcune meritano una frase.

Testo di link generico. Il 2.4.4 è Link Purpose (In Context). Nel contesto. Un link che dice «leggi di più» lo soddisfa se il paragrafo, la voce di elenco o la cella che lo circonda rendono chiara la destinazione — cosa che di solito accade. Il criterio che pretende che il testo del link regga da solo è il 2.4.9, ed è AAA. Quindi «leggi di più» ripetuto lungo una pagina è un problema reale di usabilità per chi naviga i link col tab, e non è una violazione di livello A.

Livelli di intestazione. Non esiste alcun criterio che imponga a un h2 di seguire un h1. Il 1.3.1 Info and Relationships richiede che la struttura trasmessa visivamente sia disponibile a livello programmatico — usare le intestazioni del tutto è il modo in cui lo si soddisfa. Saltare da h1 a h3 è disordinato e peggiora la navigazione con screen reader, ma non è ciò che dice il 1.3.1. axe-core classifica heading-order come buona pratica, e lo fa da anni.

Lingua dei passaggi. Questa era sbagliata due volte. Il 3.1.2 è livello A, non AA — avevo il livello errato. E il controllo si intitolava «I passaggi in lingua straniera devono dichiarare la propria lingua», che prometteva una cosa che nessun controllo automatico può fare: sapere che un passaggio è in un'altra lingua quando nulla lo segnala. Quello che il codice faceva davvero era validare gli attributi lang già presenti. È un controllo utile. Non è il controllo che il titolo pubblicizzava.

Quali sono allora i numeri veri

25 controlli. Diciotto corrispondono a un criterio di successo WCAG 2.2, su quattordici criteri distinti di livello A e AA: 1.1.1, 1.3.1, 1.3.5, 1.4.2, 1.4.3, 1.4.4, 2.4.1, 2.4.2, 2.4.4, 2.5.8, 3.1.1, 3.1.2, 3.3.2 e 4.1.2.

Sette sono buone pratiche. Da correggere, non violazioni di conformità.

Prima della verifica la scheda prodotto diceva «25 controlli automatici sui livelli A e AA delle WCAG 2.2» ed elencava il 4.1.1 fra i criteri coperti. Entrambe le affermazioni erano false, e la seconda era verificabilmente falsa da chiunque avesse letto la Raccomandazione 2.2.

Perché non è pedanteria

Ecco la parte che mi ha fatto smettere di trattarla come un problema di etichette.

Il plugin ha un generatore per l'informativa di accessibilità che l'European Accessibility Act richiede. Precompila la sezione delle «barriere note» dalla scansione più recente e — questo era l'argomento di vendita — associa a ciascuna barriera il criterio di successo WCAG.

Quindi otto regole che citavano criteri sbagliati, obsoleti o scollegati stavano scrivendo quei numeri dentro un documento che il proprietario del sito pubblica come dichiarazione legale sul proprio servizio.

Una dichiarazione di conformità non è un report. Un report che esagera ti rovina il pomeriggio. Una dichiarazione pubblicata che cita un criterio inesistente, su un documento che sei tenuto per legge a mantenere, è una categoria diversa di errore — ed è di quelle che scopre il tuo cliente, non tu.

È questo l'argomento a favore della distinzione, ed è l'unico che conta. Uno strumento che presenta ogni segnalazione come violazione WCAG gonfia due numeri: il proprio — 25 controlli WCAG si legge meglio di 18 — e il tuo. E il tuo numero gonfiato è quello che finisce in pubblico.

Cosa ho cambiato

Ogni regola ora dichiara cos'è:

{
    id: 'heading_order',
    wcag: '',
    level: '',
    standard: 'best-practice',
    ...
}

Il report stampa il criterio dove c'è, e Buona pratica dove non c'è, invece di un WCAG () vuoto.

Il generatore dell'informativa ora richiede due condizioni indipendenti prima di scrivere un criterio dentro un documento legale: la regola dev'essere marcata come mappata su WCAG, e il suo valore deve corrispondere a ^\d+\.\d+\.\d+$. Una sola delle due sarebbe bastata a impedire quello che è successo. Volevo quella che sopravvive a qualcuno che modifica l'altra.

E i testi di marketing ora dicono 18 e 7, nel readme, nella documentazione e nella scheda del marketplace. È stato il commit meno piacevole della settimana e quello che rifarei.

Se costruisci o compri uno di questi strumenti

Tre domande che vale la pena porsi, nessuna delle quali richiede di fidarsi di me.

Segnala ancora il 4.1.1? Trenta secondi per verificarlo, e ti dice quando l'insieme di regole è stato letto contro lo standard invece che copiato da un altro strumento.

Distingue i criteri di successo dalle buone pratiche? Se ogni segnalazione porta un numero di criterio, almeno alcuni di quei numeri sono decorazione. L'implementazione di riferimento su cui gira questo settore marca circa un quarto delle proprie regole come buona pratica. Uno strumento che non ne ha nessuna non è più severo: è meno attento.

Dove finisce il criterio dopo il report? Se confluisce in una dichiarazione, un badge, un PDF o qualunque cosa il cliente pubblichi, la sua accuratezza smette di essere una questione di qualità interna.

La parte che non è cambiata

I test automatici trovano circa un terzo delle barriere di accessibilità reali. Sistemare le etichette non sposta quel numero. Un report pulito è un buon segno, non una dichiarazione di conformità, e la verifica con tastiera e screen reader fatta da una persona resta l'unico modo per sapere.

Quello che la verifica ha cambiato è più ristretto e, credo, valeva la settimana: quando adesso il mio strumento dice WCAG, lo intende davvero.