Paleta gerada por ferramenta: como testar o contraste antes de usar no site
Pegue as cinco cores do gerador, monte a matriz de contraste com um script de 20 linhas e transforme a paleta em tokens de texto, fundo e botão que passam na WCAG.
A paleta saiu do gerador em dez segundos: um azul-petróleo, um azul médio, um azul-claro, um cinza-gelo e um laranja. A tela ficou coesa e o cliente aprovou. Na primeira semana, alguém reclamou que não conseguia ler o preço no botão laranja com o celular no sol. Gerador de paleta otimiza harmonia entre cores, não legibilidade de texto em cima delas. Bonita e legível são testes diferentes.
A resposta curta: antes de usar a paleta, calcule a razão de contraste de cada par texto e fundo que vai aparecer na tela. Texto comum precisa de pelo menos 4,5:1, texto grande e bordas de componentes precisam de 3:1. As cores que não passam viram decoração, e você cria variações mais escuras ou mais claras para o texto. Abaixo, o processo completo com a paleta da loja do exemplo.
Por que cinco cores que combinam podem falhar na leitura
Ferramentas como o Colormind geram cores que funcionam juntas visualmente: matizes próximos, saturação equilibrada, um contraste de destaque. Mas a leitura depende de outra coisa, a diferença de luminância entre o texto e o fundo. Duas cores podem ter matizes bem diferentes, como um laranja e um azul médio, e quase a mesma luminância. Para o olho, elas "brigam", e o texto some.
A WCAG define a razão de contraste a partir da luminância relativa de cada cor. Cada canal RGB é convertido para uma escala linear, os três são somados com pesos (o verde pesa muito mais que o azul, porque o olho é mais sensível a ele) e a razão é (L1 + 0,05) / (L2 + 0,05), com L1 a cor mais clara. O resultado vai de 1:1, cor sobre ela mesma, a 21:1, preto sobre branco.
Os limites que importam no dia a dia, no nível AA: 4,5:1 para texto comum, 3:1 para texto grande (cerca de 24px, ou cerca de 18,7px em negrito) e 3:1 para elementos de interface que a pessoa precisa perceber, como a borda de um campo ou o ícone de um botão. O nível AAA pede 7:1 para texto comum.
Como montar a matriz de contraste da paleta
A paleta da loja é esta: #1B3A4B (azul-petróleo), #3E7CB1 (azul médio), #81A4CD (azul-claro), #DBE4EE (cinza-gelo) e #F17300 (laranja). Mais o branco #FFFFFF, que quase sempre entra como fundo. O script abaixo calcula todos os pares e marca o que passa:
// contraste.js - rode com: node contraste.js
const paleta = {
petroleo: '#1B3A4B',
azul: '#3E7CB1',
claro: '#81A4CD',
gelo: '#DBE4EE',
laranja: '#F17300',
branco: '#FFFFFF'
};
function luminancia(hex) {
const canais = [1, 3, 5].map(function (i) {
const v = parseInt(hex.slice(i, i + 2), 16) / 255;
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
});
return 0.2126 * canais[0] + 0.7152 * canais[1] + 0.0722 * canais[2];
}
function razao(a, b) {
const la = luminancia(a);
const lb = luminancia(b);
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
}
const nomes = Object.keys(paleta);
for (let i = 0; i < nomes.length; i++) {
for (let j = i + 1; j < nomes.length; j++) {
const r = razao(paleta[nomes[i]], paleta[nomes[j]]);
const nivel = r >= 7 ? 'AAA' : r >= 4.5 ? 'AA' : r >= 3 ? 'só grande/UI' : 'reprova';
console.log(nomes[i] + ' x ' + nomes[j] + ': ' + r.toFixed(2) + ' ' + nivel);
}
}
Parte da saída, com os pares que interessam:
petroleo x gelo: 9.32 AAA
petroleo x branco: 11.98 AAA
petroleo x laranja: 4.10 só grande/UI
azul x gelo: 3.46 só grande/UI
azul x branco: 4.45 só grande/UI
claro x branco: 2.58 reprova
laranja x branco: 2.92 reprova
Lendo o resultado: o que cada cor pode fazer
O azul-petróleo é o herói: passa com folga em branco e no cinza-gelo, então ele vira a cor do texto principal. O azul médio é o caso traiçoeiro. Ele parece escuro o suficiente para links, mas dá 4,45 sobre branco. A WCAG não arredonda: 4,45 não é 4,5. Um link nessa cor reprova em texto comum.
O laranja com texto branco, exatamente o botão de "Comprar", dá 2,92. Reprova até para texto grande. Com texto petróleo em cima, chega a 4,10, que só serve para texto grande. O azul-claro não passa com nada claro e funciona só como decoração: fundo de faixa sem texto, ilustração, detalhe.
A saída da tabela não é jogar a paleta fora. É separar cor de marca de cor de texto: a marca continua com o laranja do gerador nas áreas grandes e decorativas, e o texto ganha versões ajustadas da mesma família.
Como transformar a paleta em tokens que passam
- Liste os pares reais da interface. Texto do corpo sobre fundo, texto secundário sobre fundo, link sobre fundo, texto do botão sobre o botão, borda do campo sobre o fundo. Cada par é uma linha de teste.
- Rode a matriz e marque os reprovados. Use o script acima ou o verificador das ferramentas de desenvolvedor do navegador.
- Escureça ou clareie sem mudar o matiz. No exemplo, o azul do link foi para
#3871A3(5,17 sobre branco) e o laranja do botão foi para#B35600(4,94 com texto branco). Visualmente continuam sendo "o azul" e "o laranja" da marca. - Cheque de novo em todos os fundos. O azul ajustado dá 5,17 no branco, mas 4,03 no cinza-gelo. Se o link aparece numa faixa cinza-gelo, ele precisa de outra variação ou de sublinhado e peso maior.
- Registre em variáveis com nome de função. O CSS usa o papel da cor, não o tom.
:root {
--marca-laranja: #F17300; /* decoração, áreas grandes, sem texto */
--marca-claro: #81A4CD; /* ilustração e faixas sem texto */
--fundo: #FFFFFF;
--fundo-suave: #DBE4EE;
--texto: #1B3A4B; /* 11,98 no fundo, 9,32 no suave */
--texto-secundario: #4A5866;/* 7,29 no fundo, 5,68 no suave */
--link: #3871A3; /* 5,17 no fundo; não usar no suave */
--botao-fundo: #B35600;
--botao-texto: #FFFFFF; /* 4,94 */
}
.botao-comprar {
background: var(--botao-fundo);
color: var(--botao-texto);
}
Os comentários com o número viram documentação. Quem mexer no token daqui a seis meses sabe qual teste tem de repetir. Para o caso específico do texto secundário cinza, que é onde a maioria dos sites reprova, o guia sobre cinza claro no texto aprofunda.
Alto contraste e modo escuro
Quem liga o modo de cores forçadas do sistema operacional recebe as cores do sistema, e você pode ajustar detalhes com @media (forced-colors: active). Quem pede mais contraste sem forçar cores pode ser atendido com @media (prefers-contrast: more). Nos dois casos, a paleta do gerador não é obrigatória: o que importa é a leitura. Se houver modo escuro, ele é uma segunda matriz inteira, não a primeira com as cores invertidas.
Erros comuns
- Aprovar a paleta olhando o mostruário. Cinco quadrados lado a lado não mostram como fica um parágrafo de 14px sobre um deles.
- Arredondar a razão para cima. 4,45 parece 4,5 na planilha e reprova na auditoria e na vida real.
- Usar a cor de marca como cor de texto por padrão. A cor que chama atenção numa faixa larga raramente tem luminância para texto miúdo.
- Testar só sobre branco. O mesmo link passa no fundo branco e reprova no cinza-gelo da faixa de destaque.
- Esquecer borda e ícone. Campo de formulário com borda azul-clara sobre branco (2,58) some, e a pessoa não acha onde digitar.
- Nomear variável pelo tom.
--azul-2não diz se pode receber texto;--linkdiz.
Checklist
- Cada par texto e fundo usado na interface tem a razão calculada e anotada.
- Texto comum passa de 4,5:1 sem arredondamento.
- Texto grande, bordas de campo e ícones de ação passam de 3:1.
- Cores que reprovam estão marcadas como decorativas no CSS.
- Variações ajustadas mantêm o matiz da marca.
- Variáveis CSS têm nome de função (texto, link, botão), não de tom.
- Modo escuro, se existir, tem a própria matriz de contraste.
Se você quer o passo anterior, de como escolher a cor de texto a partir do zero, veja cor no CSS com contraste; para organizar tudo em tema, tabela de cor não fecha tema. Agora abra o seu site e procure o menor texto da página, em geral o rodapé ou a legenda do preço: qual é a razão de contraste dele com o fundo onde está?
Referência: http://colormind.io/