Cumprir o contraste de cor da WCAG sem adivinhar
Para texto corrido normal, o nível AA da WCAG exige um rácio de contraste de pelo menos 4,5:1 em relação ao fundo. O texto grande precisa de 3:1. Os componentes de interface e os gráficos com significado precisam de 3:1. O nível AAA sobe o texto para 7:1 e o texto grande para 4,5:1. Os rácios vão de 1:1 (cores idênticas) a 21:1 (preto puro sobre branco puro).
Isto não se avalia a olho. Dois pares que parecem igualmente legíveis no seu monitor podem estar em lados opostos do limiar, por isso o rácio tem de ser calculado.
Os limiares
| O que é | AA | AAA |
|---|---|---|
| Texto normal | 4,5:1 | 7:1 |
| Texto grande | 3:1 | 4,5:1 |
| Componentes de interface e objetos gráficos | 3:1 | sem exigência mais alta |
Vêm de três critérios de sucesso: 1.4.3 Contraste (Mínimo) em AA, 1.4.6 Contraste (Melhorado) em AAA e 1.4.11 Contraste Não Textual em AA, que abrange os limites visuais dos controlos e as partes dos gráficos de que se precisa para os compreender.
Texto grande significa pelo menos 18 pontos, ou 14 pontos se for a negrito, o que dá aproximadamente 24px em peso normal ou cerca de 18,5px a negrito. Não significa "um título": para efeitos das regras, um título de 20px é texto normal e precisa dos 4,5:1 completos.
Algumas coisas estão isentas: texto puramente decorativo, texto num controlo inativo, texto que faz parte de uma imagem com outro conteúdo visual significativo, e logótipos. O texto de marcador e o texto de ajuda não estão isentos.
O que o rácio mede realmente
A fórmula compara a luminância relativa das duas cores:
ratio = (L_lighter + 0.05) / (L_darker + 0.05)
A luminância relativa é um número de 0 (preto) a 1 (branco) que representa quanta luz uma cor emite. Calcula-se desfazendo a curva de gama sRGB em cada canal e ponderando-os depois:
L = 0.2126 R + 0.7152 G + 0.0722 B (on linearised channels)
O 0.05 somado aos dois lados modela um pouco de luz ambiente refletida no ecrã, e é por isso que o máximo é 21 e não infinito.
Daqui decorrem duas coisas. O rácio depende apenas da luminância, não do matiz, por isso duas cores podem ter matizes radicalmente diferentes e continuar quase invisíveis uma contra a outra. E é simétrico, pelo que é indiferente a qual das cores chama primeiro plano.
Luminância relativa não é brilho
Repare nos pesos. O verde contribui com cerca de 72% da luminância, o vermelho com cerca de 21% e o azul com uns escassos 7%. Isto é um modelo da visão humana, não da vivacidade aparente de uma cor, e produz resultados que parecem errados até nos habituarmos a eles.
- O amarelo puro
#FFFF00sobre branco dá cerca de 1,07:1, quase invisível, apesar de o amarelo parecer uma cor forte. - O mesmo amarelo sobre preto dá cerca de 19,6:1, quase tão alto como branco sobre preto.
- O azul puro
#0000FFsobre preto dá cerca de 2,4:1 e chumba em tudo, apesar de parecer forte e saturado.
É também por isso que a luminosidade do HSL é um mau substituto. Duas cores podem estar ambas nos 50% de luminosidade em HSL e ter luminâncias muito diferentes, porque o HSL trata o matiz e a luminosidade como independentes e a visão não o faz. Converta e depois meça, em vez de assumir que um passo de luminosidade é um passo de contraste.
Os erros que se repetem
Texto corrido cinzento sobre branco. É o erro maior. O cinzento mais claro que passa em AA sobre branco é cerca de #767676, que dá 4,54:1. Um passo mais claro, #777777, dá 4,48:1 e chumba. Cinzentos "discretos" populares como #999999 ficam perto de 2,9:1, menos de dois terços do exigido. Se quiser AAA sobre branco, precisa de qualquer coisa como #595959, que dá exatamente 7:1.
Texto de marcador e texto de ajuda. São compostos vários tons mais claros do que o texto corrido por serem secundários. Continuam a ser texto e continuam a precisar de 4,5:1.
Cores semitransparentes. Um rácio calculado a partir da cor declarada não quer dizer nada se o elemento estiver a 60% de opacidade. Determine a cor composta que de facto aparece no ecrã e meça essa.
Texto sobre imagens, gradientes e vídeo. O rácio muda de píxel para píxel e o píxel que conta é o pior deles. A solução habitual é uma camada semiopaca por trás do texto, em vez de esperar que a fotografia se mantenha escura.
Botões na cor da marca. Texto branco sobre uma cor de marca de tom médio cai muitas vezes entre 3:1 e 4,5:1, passando como objeto gráfico e chumbando como etiqueta. Escureça o botão, ou aceite a falha com conhecimento de causa.
Indicadores de foco e botões só com ícone. Ambos caem na regra dos 3:1 para conteúdo não textual. Um anel de foco num tom ligeiramente diferente da mesma cor é uma falha frequente, tal como um ícone cinzento que é o único conteúdo de um botão.
Modo escuro por inversão. Virar do avesso uma paleta clara não preserva os rácios, porque a luminância não é simétrica em torno do meio. Branco puro sobre preto puro dá o máximo de 21:1, mas muita gente acha o halo desconfortável, por isso um branco-sujo sobre um cinzento muito escuro, como #E6E6E6 sobre #121212 a cerca de 15:1, é um compromisso comum.
Medir a partir de uma captura de ecrã. Um conta-gotas na margem de texto suavizado recolhe uma mistura de texto e fundo, não a cor do texto. Recolha no meio de um traço grosso ou, melhor ainda, leia os valores diretamente do seu CSS.
Um método que dispensa adivinhação
Fixe primeiro o fundo, depois escureça ou clareie o primeiro plano até passar o limiar com alguma folga, e registe esse par. Construa a paleta a partir de pares aprovados e não de cores aprovadas: uma cor não é acessível por si só, apenas em combinação. Verifique também todos os estados, não apenas o de repouso, e os dois temas se distribuir um modo escuro.
Uma última nota. Está a ser desenvolvido um modelo percetual mais recente, o APCA, para uma versão futura da WCAG, e comporta-se de forma bastante diferente, sobretudo para texto claro sobre fundos escuros. Vale a pena conhecê-lo, mas os números a cumprir hoje são os rácios da WCAG 2 acima.