Um revisor encontrou três critérios WCAG errados no meu plugin de acessibilidade. Fui verificar e encontrei oito.

Eu desenvolvo um scanner de acessibilidade para WordPress. Na semana passada o revisor de um marketplace rejeitou-o, e entre os motivos estava este:

As regras de conformidade precisam de correção. «Idioma dos trechos» apenas valida os atributos lang já presentes e não consegue detetar trechos em língua estrangeira não marcados; a regra do landmark principal é WCAG 1.3.6 de nível AAA mas é apresentada como uma verificação AA; e os id duplicados não deveriam ser apresentados como uma violação das WCAG 2.2 ao abrigo do 4.1.1.

Três constatações. As três corretas. O que se segue é o que aconteceu quando deixei de corrigir as três e verifiquei as outras vinte e duas.

A regra que já não existe

Começo pela terceira, porque é a mais fácil de verificar e a mais amplamente errada.

O critério de sucesso 4.1.1 Parsing foi removido das WCAG 2.2. Não foi depreciado nem suavizado: removido. A Recomendação do W3C lista-o na secção de conformidade como «Parsing (Obsolete and removed)». Saiu porque aquilo de que protegia — tecnologias de apoio que engasgavam com marcação malformada — deixou de ser um modo de falha real desde que navegadores e APIs de acessibilidade convergiram sobre como recuperar de HTML defeituoso.

O meu scanner reportava atributos id duplicados como violação das WCAG 4.1.1. Perante as WCAG 2.2, esse critério já não está lá para ser violado.

Os id duplicados continuam a merecer correção. Quebram as associações label for e as referências aria-labelledby, de modo que é anunciado o elemento errado, ou nenhum. Mas isso é um problema do 4.1.2 quando quebra efetivamente um nome acessível, e detetá-lo é uma verificação diferente de contar id duplicados. O que eu tinha era uma verificação genérica de id duplicados a usar o número de um critério retirado.

Depois verifiquei o resto

O revisor tinha encontrado três. Eu podia ter corrigido três. Em vez disso peguei nas 25 regras e verifiquei cada uma contra a Recomendação WCAG 2.2 e contra o modo como o axe-core classifica a regra equivalente — porque o axe-core é a implementação de referência sobre a qual boa parte deste setor está construída, e faz uma distinção que eu tinha perdido.

Oito regras estavam erradas.

Regra Declarava Na realidade
Id duplicados 4.1.1 Removido das WCAG 2.2
Um único landmark main 1.3.6 AAA 1.3.6 é Identify Purpose, um critério sem relação
Idioma dos trechos 3.1.2 AA 3.1.2 é nível A — e a verificação não fazia o que o título prometia
Texto de link vago 2.4.4 O 2.4.4 é satisfeito pelo contexto
Níveis de título saltados 1.3.1 As WCAG não exigem níveis sequenciais
h1 em falta 1.3.1 Boa prática
Título vazio 1.3.1 Boa prática
tabindex positivo 2.4.3 Boa prática

Algumas merecem uma frase.

Texto de link vago. O 2.4.4 chama-se Link Purpose (In Context). Em contexto. Um link que diz «leia mais» satisfá-lo se o parágrafo, o item de lista ou a célula que o rodeia tornarem claro o destino — o que costuma acontecer. O critério que exige que o texto do link se sustente sozinho é o 2.4.9, e é AAA. Portanto «leia mais» repetido ao longo de uma página é um problema real de usabilidade para quem percorre os links com o tabulador, e não é uma violação de nível A.

Níveis de título. Não existe critério de sucesso que exija que um h2 siga um h1. O 1.3.1 Info and Relationships exige que a estrutura transmitida visualmente esteja disponível programaticamente — usar títulos de todo é a forma de o satisfazer. Saltar de h1 para h3 é desarrumado e piora a navegação com leitor de ecrã, mas não é o que o 1.3.1 diz. O axe-core classifica heading-order como boa prática, e há anos.

Idioma dos trechos. Esta estava errada duas vezes. O 3.1.2 é nível A, não AA — eu tinha o nível errado. E a verificação intitulava-se «Trechos em língua estrangeira devem declarar o seu idioma», o que prometia algo que nenhuma verificação automática consegue fazer: saber que um trecho está noutra língua quando nada o assinala. O que o código fazia na realidade era validar os atributos lang já presentes. É uma verificação útil. Não é a verificação que o título anunciava.

Então quais são os números verdadeiros

25 verificações. Dezoito correspondem a um critério de sucesso das WCAG 2.2, distribuídas por catorze critérios distintos de nível 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.

Sete são boas práticas. Merecem correção, não são violações de conformidade.

Antes da auditoria, a página do produto dizia «25 verificações automatizadas nos níveis A e AA das WCAG 2.2» e listava o 4.1.1 entre os critérios cobertos. Ambas as afirmações eram falsas, e a segunda era verificavelmente falsa por quem tivesse lido a Recomendação 2.2.

Porque isto não é preciosismo

Aqui está a parte que me fez deixar de tratar isto como um problema de rótulos.

O plugin tem um gerador para as informações de acessibilidade que o European Accessibility Act exige. Preenche a secção de «barreiras conhecidas» a partir da análise mais recente e — este era o argumento de venda — associa a cada barreira o seu critério de sucesso WCAG.

Ou seja, oito regras que citavam critérios errados, obsoletos ou sem relação estavam a escrever esses números dentro de um documento que o dono do site publica como declaração legal sobre o seu próprio serviço.

Uma declaração de conformidade não é um relatório. Um relatório que exagera estraga-lhe a tarde. Uma declaração publicada que cita um critério inexistente, num documento que é legalmente obrigado a manter, é outra categoria de erro — e das que o seu cliente descobre, não você.

É este o argumento a favor da distinção, e é o único que importa. Uma ferramenta que apresenta cada achado como violação das WCAG infla dois números: o seu — 25 verificações WCAG lê-se melhor do que 18 — e o seu. E o seu número inflado é o que acaba em público.

O que mudei

Cada regra declara agora o que é:

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

O relatório imprime o critério onde existe, e Boa prática onde não existe, em vez de um WCAG () vazio.

O gerador exige agora duas condições independentes antes de escrever um critério dentro de um documento legal: a regra tem de estar marcada como mapeada nas WCAG, e o seu valor tem de corresponder a ^\d+\.\d+\.\d+$. Qualquer uma das duas sozinha teria bastado para impedir o que aconteceu. Eu queria aquela que sobrevive a alguém editar a outra.

E os textos comerciais dizem agora 18 e 7, no readme, na documentação e na ficha do marketplace. Foi o commit menos agradável da semana e aquele que repetiria.

Se constrói ou compra uma destas ferramentas

Três perguntas que vale a pena fazer, e nenhuma exige que confie em mim.

Ainda reporta o 4.1.1? Trinta segundos para verificar, e diz-lhe quando o conjunto de regras foi lido contra a norma em vez de copiado de outra ferramenta.

Distingue critérios de sucesso de boas práticas? Se cada achado traz um número de critério, pelo menos alguns desses números são decoração. A implementação de referência sobre a qual este setor funciona marca cerca de um quarto das suas regras como boa prática. Uma ferramenta que não tem nenhuma não é mais rigorosa: é menos cuidadosa.

Para onde vai o critério depois do relatório? Se alimenta uma declaração, um selo, um PDF ou qualquer coisa que um cliente publique, a exatidão deixa de ser uma questão de qualidade interna.

A parte que não mudou

Os testes automatizados encontram cerca de um terço das barreiras reais de acessibilidade. Corrigir os rótulos não mexe nesse número. Um relatório limpo é um bom sinal, não uma declaração de conformidade, e os testes com teclado e leitor de ecrã feitos por uma pessoa continuam a ser a única forma de saber.

O que a auditoria mudou é mais estreito e, creio, valeu a semana: quando a minha ferramenta agora diz WCAG, é a sério.