Texto cinza claro que ninguém lê: como medir e corrigir o contraste
Calcule o contraste do seu cinza contra o fundo real, entenda os mínimos da WCAG para texto normal e grande e escolha tons que passam sem perder o visual.
O redesign do blog ficou "leve": texto em #999 sobre branco, metadados em #bbb. No monitor calibrado da agência, ficou bonito. No celular, ao meio-dia, com o brilho no automático, a pessoa precisa inclinar a tela para ler a frase. A resposta curta: texto normal precisa de contraste de pelo menos 4,5:1 com o fundo; texto grande, de 3:1, segundo o critério 1.4.3 da WCAG. O #999 sobre branco dá 2,85:1 e reprova. O cinza mais claro que passa em texto normal sobre branco puro é perto de #767676, e um tom como #666 dá folga. Meça contra o fundo real da página, não contra o branco do protótipo.
Cinza não é proibido. Ele só precisa ser escuro o bastante para o que está escrito com ele.
Por que o cinza que parecia ótimo some
O contraste que você enxerga depende da tela, da luz ambiente e dos seus olhos. Monitor de escritório, com brilho alto e luz controlada, é o melhor caso. Celular ao sol, notebook com brilho reduzido para economizar bateria, tela velha com o painel desgastado e pessoas com visão reduzida são o caso comum. A diferença de luminância que sobra nessas condições é bem menor que a do protótipo.
A WCAG resolve isso com uma conta objetiva. Ela calcula a luminância relativa das duas cores, de 0 (preto) a 1 (branco), e faz a razão (L1 + 0,05) / (L2 + 0,05), com a cor mais clara em cima. Preto sobre branco dá 21:1, o máximo. A mesma cor sobre ela mesma dá 1:1. Os mínimos do nível AA, que é o nível mais usado como referência, são:
- 4,5:1 para texto normal.
- 3:1 para texto grande, definido como pelo menos 18 pontos, ou 14 pontos em negrito. Em CSS, isso dá cerca de 24 pixels, ou cerca de 18,66 pixels em negrito.
O nível AAA, no critério 1.4.6, pede 7:1 e 4,5:1. Texto de elemento desabilitado, texto puramente decorativo e logotipos ficam fora da exigência.
O cálculo no exemplo do blog
A luminância de um cinza vem do valor de cada canal convertido para uma escala linear. Para o #999, cada canal vale 153 de 255, ou 0,6, que na escala linear vira cerca de 0,318. O branco vale 1. A conta fica:
(1 + 0,05) / (0,318 + 0,05) = 1,05 / 0,368 = 2,85
Reprovado para qualquer tamanho de texto, inclusive o grande. Fazendo a mesma conta com outros tons sobre branco puro:
#999999 2,85:1 reprova
#777777 4,48:1 reprova por pouco
#767676 4,54:1 passa em texto normal
#666666 5,74:1 passa com folga
#595959 7,00:1 chega perto do nível AAA
Agora o detalhe que pegou o blog. O fundo da área de leitura não era branco: era #f5f5f5, para ficar "mais suave". Sobre esse fundo, o mesmo #767676 cai para cerca de 4,17:1 e reprova. O protótipo mostrava os textos sobre branco, e foi lá que alguém conferiu.
A correção ficou assim: texto do artigo em #333, metadados em #5c5c5c, fundo mantido em #f5f5f5. Os dois passam com folga, e o visual continua sem o preto puro que o designer queria evitar.
Transparência também é cor
Texto em rgba(0, 0, 0, 0.4) parece mais "elegante" no código, mas sobre branco ele resulta exatamente em #999: 2,85:1. E sobre um fundo colorido ou uma imagem, o resultado muda a cada ponto. Para medir, calcule a cor que aparece de fato na tela, não a declarada no CSS.
Como auditar e corrigir o contraste
- Liste os pares de cor do texto: corpo, metadados, links, legendas, placeholder, botões, rodapé, mensagens de erro.
- Para cada par, identifique o fundo real em que ele aparece, inclusive cartões, faixas e imagens.
- Meça a razão com o seletor de cor das ferramentas de desenvolvedor do Chrome ou do Edge, que mostra o contraste e indica se passa em AA e AAA, ou com o verificador de contraste da WebAIM.
- Rode o Lighthouse ou o axe na página para pegar os casos que você esqueceu.
- Escureça o texto, não o fundo, quando o fundo faz parte da identidade visual.
- Troque os valores nas variáveis do tema, para a correção valer no site inteiro.
- Teste num celular real, com brilho baixo e, se possível, ao ar livre.
As variáveis do tema corrigidas
:root {
--fundo-leitura: #f5f5f5;
--texto: #333333;
--texto-secundario: #5c5c5c;
--borda-campo: #8a8a8a;
}
body {
background: var(--fundo-leitura);
color: var(--texto);
}
.meta {
color: var(--texto-secundario);
}
A borda dos campos de formulário também entra na conta, por outro critério: a WCAG 2.1 incluiu o 1.4.11, que pede 3:1 para componentes de interface e elementos gráficos necessários para entender o conteúdo. Uma borda de campo quase invisível faz a pessoa não saber onde clicar.
O que o contraste não resolve sozinho
Placeholder costuma vir em cinza claro por padrão, e muita gente o usa como rótulo. Quando a pessoa começa a digitar, ele some, e a informação vai junto. Use um rótulo visível e deixe o placeholder só como exemplo de formato. Mensagens de erro não podem depender só da cor vermelha: acrescente texto ou ícone com significado. E respeite o modo de cores forçadas do sistema, como o alto contraste do Windows: a media query forced-colors: active permite ajustar o que quebrar nele.
Se as cores vieram de um gerador de paletas, o post paleta gerada por ferramenta mostra como conferir antes de aplicar, e cor no CSS: o contraste é o que se lê trata das notações de cor e da escolha de pares.
Erros comuns
- Medir contra o fundo do protótipo. O fundo real, levemente cinza ou colorido, derruba a razão e o texto reprova em produção.
- Usar opacidade para clarear o texto. A cor final depende do fundo e costuma ficar mais clara do que parece no código.
- Tratar metadado como texto grande. Data e autor em 13 pixels são texto normal e precisam de 4,5:1.
- Placeholder como único rótulo. Ele já vem fraco e ainda some quando a pessoa digita.
- Erro indicado só pela cor. Quem não distingue vermelho de verde, ou lê ao sol, não percebe que o campo falhou.
- Corrigir cor por cor no CSS. Sem variáveis de tema, o cinza antigo volta no próximo componente novo.
Checklist
- Todo texto normal tem pelo menos 4,5:1 contra o fundo onde aparece.
- Texto grande tem pelo menos 3:1, e o tamanho está de fato acima do limite.
- Bordas de campo e ícones necessários têm pelo menos 3:1.
- As cores de texto vêm de variáveis do tema, não de valores soltos.
- Nenhum texto com informação usa opacidade sobre fundo variável.
- Formulários têm rótulo visível além do placeholder.
- A página passa no Lighthouse ou no axe sem alertas de contraste.
- Alguém leu a página num celular com brilho baixo.
Pegue o tom de cinza do texto secundário do seu site e o fundo exato onde ele aparece, coloque os dois no medidor de contraste: o número passa de 4,5, ou o seu cinza está contando com uma tela boa?
Referência: https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html