Programação Por Equipe DevBras

Capa do artigo: Paleta gerada por ferramenta: como testar o contraste antes de usar no site

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

  1. 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.
  2. Rode a matriz e marque os reprovados. Use o script acima ou o verificador das ferramentas de desenvolvedor do navegador.
  3. 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.
  4. 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.
  5. 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-2 não diz se pode receber texto; --link diz.

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á?

Avalie este artigo

Referência: http://colormind.io/