Ciência das cores para designers de interfaces: o que aprendi construindo um site sem cores
As Diretrizes de Acessibilidade para Conteúdo Web (WCAG) do W3C exigem uma taxa de contraste mínima de 4,5:1 para texto normal, mas uma pesquisa da WebAIM de 2023 descobriu que 83,6% dos um milhão de sites mais acessados apresentavam falhas detectáveis de contraste WCAG 2 em suas páginas iniciais. Eu construí o blakecrosley.com com o problema oposto: contraste máximo (21:1) em todo lugar, depois reduzindo-o seletivamente.1
Resumo
Projetei meu site pessoal com zero cores de marca. Toda a hierarquia visual opera através de texto branco sobre preto absoluto (#000000) em quatro níveis de opacidade: 100%, 65%, 40% e 10%. Essa decisão me forçou a aprender ciência perceptual das cores — por que o sRGB mente sobre espaçamento uniforme, como o OKLCH resolve isso e por que o modo escuro exige relações de contraste diferentes do modo claro. As ferramentas interativas abaixo permitem que você explore taxas de contraste e diferenças entre espaços de cores. A conclusão: entender a ciência por trás da percepção de cores produz decisões de design melhores do que a intuição estética.
Minha não-paleta de cores
A maioria dos design systems começa com uma paleta de cores. O meu começa com a ausência de uma:
:root {
--color-bg-dark: #000000;
--color-bg-elevated: #111111;
--color-bg-surface: #1a1a1a;
--color-text-primary: #ffffff;
--color-text-secondary: rgba(255,255,255,0.65);
--color-text-tertiary: rgba(255,255,255,0.4);
--color-border: rgba(255,255,255,0.1);
--color-border-subtle: rgba(255,255,255,0.05);
--color-accent: #ffffff;
}
Dez tokens. Nenhuma cor de marca. Nenhuma paleta semântica de erro/sucesso/aviso. Toda a hierarquia visual opera através de quatro níveis de transparência.2 O racional dessa abordagem baseada em restrições se conecta à minha reflexão mais ampla sobre a beleza do brutalismo — reduzir força a coerência.
Por que quatro níveis funcionam
Cada nível serve a uma função de comunicação específica:
| Nível | Opacidade | Token CSS | Função | Taxa WCAG (sobre #000) |
|---|---|---|---|---|
| Primário | 100% | --color-text-primary |
Títulos, texto do corpo, conteúdo crítico | 21:1 (AAA) |
| Secundário | 65% | --color-text-secondary |
Subtítulos, navegação, metadados | 13,7:1 (AAA) |
| Terciário | 40% | --color-text-tertiary |
Timestamps, texto auxiliar, estados desabilitados | 8,4:1 (AAA) |
| Estrutural | 10% | --color-border |
Bordas, divisores, separação de fundo | N/A (não-texto) |
Todos os níveis passam no WCAG AAA (7:1) para contraste de texto. O nível terciário a 40% de opacidade produz uma taxa de 8,4:1 — quase o dobro do mínimo AA de 4,5:1. A escolha brutalista do preto absoluto (#000000 em vez de #0a0a0a ou #1a1a1a) dá a cada nível de texto a margem máxima de contraste.3
O incidente do --spacing-2xs
Descobri o valor de design tokens rigorosos quando usei --spacing-2xs para a margem de um subtítulo. O token não existia na minha definição :root. O CSS falhou silenciosamente, o layout quebrou e eu passei 20 minutos depurando um problema de espaçamento que deveria ter sido um erro em tempo de compilação. A correção: mudar para --spacing-xs (o menor token que eu defini). A lição: se um valor não existe no sistema, o design está errado, não o sistema.4
Por que o sRGB mente para designers
O problema da não-uniformidade perceptual
O sRGB (o espaço de cores padrão para a web) mapeia cores em um cubo onde cada eixo (vermelho, verde, azul) varia de 0 a 255. Mover 50 unidades no canal vermelho não produz a mesma mudança percebida que mover 50 unidades no canal verde. Os olhos humanos contêm mais cones sensíveis ao verde do que ao vermelho ou azul, tornando variações de verde mais perceptíveis do que variações equivalentes de vermelho.5
A consequência prática: um designer que cria uma paleta de cores espaçando valores hexadecimais uniformemente produz cores que parecem espaçadas de forma desigual. O cinza “médio” entre #000000 e #FFFFFF não é #808080 (ponto médio matemático), mas aproximadamente #777777 (ponto médio perceptual), porque a percepção humana de brilho segue uma lei de potência em vez de uma função linear.6
Meu site contorna o problema inteiramente. Ao usar apenas branco em opacidades variáveis, evito a armadilha de uniformidade do sRGB. Escalas de opacidade se comportam linearmente com a transparência percebida contra um fundo preto — uma propriedade que a mistura de cores sRGB não compartilha.
A solução OKLCH
OKLCH (Oklab Lightness, Chroma, Hue) é um espaço de cores perceptualmente uniforme onde distâncias matemáticas iguais correspondem a diferenças percebidas iguais. Um passo de 10 unidades de luminosidade sempre parece a mesma quantidade de mudança, independentemente da cor inicial.7
/* sRGB: mathematically even, perceptually uneven */
--gray-100: #f5f5f5;
--gray-200: #e5e5e5;
--gray-300: #d4d4d4;
/* OKLCH: perceptually even steps */
--gray-100: oklch(96% 0 0);
--gray-200: oklch(88% 0 0);
--gray-300: oklch(80% 0 0);
O CSS moderno suporta oklch() nativamente. Para meu próximo projeto que precise de uma paleta de cores, definirei a paleta em OKLCH. Para meu site atual, o sistema baseado em opacidade alcança a mesma uniformidade perceptual por meios diferentes.
Minha decisão sobre modo escuro: sem modo claro
Meu site não tem media query prefers-color-scheme. Ele opera exclusivamente em modo escuro. A decisão foi deliberada.8
O argumento a favor de dois modos: os usuários esperam uma opção de modo claro. A integração com preferências do sistema respeita a escolha do usuário.
O argumento contra (que eu escolhi): manter dois sistemas visuais inevitavelmente compromete ambos. Um design que funciona a 65% de opacidade sobre preto requer uma opacidade diferente sobre branco (mais perto de 45%) para alcançar o mesmo peso perceptual. Cada estado de interação, cada borda, cada sombra precisa de recalibração. Eu escolhi projetar um sistema bem feito em vez de dois sistemas medianos.
O fundo preto absoluto (#000000) maximiza o contraste disponível para cada nível de texto:
/* My actual typography contrast hierarchy */
.hero__title { color: var(--color-text-primary); } /* 21:1 */
.hero__subtitle { color: var(--color-text-secondary); } /* 13.7:1 */
.nav a { color: var(--color-text-secondary); } /* 13.7:1 */
.nav a:hover { color: var(--color-text-primary); } /* 21:1 */
A transição do estado hover (secundário → primário) fornece feedback funcional inteiramente através da mudança de contraste — nenhuma mudança de cor necessária.
Contraste e legibilidade
Requisitos de contraste WCAG
| Nível | Texto normal (< 18pt) | Texto grande (≥ 18pt ou 14pt negrito) |
|---|---|---|
| AA | 4,5:1 | 3:1 |
| AAA | 7:1 | 4,5:1 |
A taxa de contraste mede a diferença de luminância relativa entre as cores do primeiro plano e do fundo. Uma taxa de 1:1 significa nenhum contraste (cores idênticas). Uma taxa de 21:1 significa contraste máximo (preto sobre branco ou branco sobre preto).9
Além do WCAG: APCA
O algoritmo de contraste do WCAG 2 tem limitações conhecidas. O algoritmo trata todas as cores igualmente, independentemente da polaridade (escuro sobre claro vs. claro sobre escuro), apesar de pesquisas mostrarem que a percepção humana de contraste difere significativamente entre os dois modos.10
O APCA (Accessible Perceptual Contrast Algorithm) aborda essas limitações ao considerar: - Sensibilidade de polaridade: texto escuro sobre fundos claros é mais legível do que texto claro sobre fundos escuros na mesma taxa matemática de contraste - Frequência espacial: fontes finas requerem mais contraste do que fontes em negrito no mesmo tamanho - Adaptação: o olho se adapta à luminância da página ao redor, afetando o contraste percebido
Espera-se que o APCA forme a base dos requisitos de contraste do WCAG 3.0. Meu site se beneficia da descoberta sobre polaridade: como uso exclusivamente claro sobre escuro, preciso de taxas de contraste mais altas do que um site em modo claro. Meu nível de texto mais baixo (40% de opacidade, taxa de 8,4:1) excede até mesmo o mínimo recomendado pelo APCA para texto de corpo em fundos escuros.
Cor semântica sem cor
Sistemas de cores de produção normalmente atribuem cores a funções (verde para sucesso, vermelho para erro). Meu site evita cor funcional inteiramente porque não possui UI transacional — sem formulários, sem mensagens de status, sem estados de sucesso/erro. O conteúdo é estático.
Se eu precisasse de cor semântica, a adicionaria cirurgicamente:
| Função | Abordagem típica | Minha abordagem hipotética |
|---|---|---|
| Sucesso | Verde | Texto branco + ícone de check |
| Erro | Vermelho | Texto branco + ícone X + ênfase de borda |
| Aviso | Âmbar | Texto branco + ícone de exclamação |
Combinar ícones com texto elimina a comunicação exclusivamente por cor que falha para aproximadamente 8% dos homens com deficiência na visão de cores. A abordagem também preserva meu sistema monocromático. A cor serviria como acento, não como elemento estrutural.11
A hierarquia tipografia-primeiro
Sem cor carregando a hierarquia, a tipografia carrega tudo no meu site:
:root {
--font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text",
"SF Pro Display", "Helvetica Neue", Arial, sans-serif;
--font-size-display: 5rem; /* 80px — hero headlines */
--font-size-7xl: 3.875rem; /* 62px */
--font-size-base: 1rem; /* 16px — body text */
--font-size-xs: 0.75rem; /* 12px — metadata */
}
Fontes do sistema, não web fonts personalizadas. A escolha é tanto uma decisão brutalista (usar o material nativo da plataforma) quanto uma decisão de performance (zero latência de carregamento de fontes, contribuindo para pontuação 100/100 no Lighthouse). Explico por completo o raciocínio por trás das fontes do sistema e a construção da escala no meu artigo sobre sistemas tipográficos. O tamanho display (80px) com espaçamento entre letras apertado (-0.03em) dá aos títulos gravidade sem decoração. O texto de corpo a 16px com altura de linha generosa (1.7) prioriza legibilidade sobre densidade.12
A escala tipográfica de 13 passos, de 0,75rem a 5rem, fornece granularidade suficiente para expressar hierarquia apenas pelo tamanho. Combinada com os quatro níveis de opacidade, tenho 52 combinações possíveis (13 tamanhos × 4 opacidades) — mais do que suficiente para expressar qualquer hierarquia de conteúdo sem recorrer à cor.
Principais conclusões
Para designers: - Defina paletas de cores em OKLCH em vez de sRGB; espaços de cores perceptualmente uniformes produzem hierarquia previsível e taxas de contraste consistentes em toda a paleta - Teste seu nível de texto terciário contra o WCAG AAA (7:1), não apenas o AA (4,5:1); o limiar AAA fornece margem suficiente para condições reais de tela (brilho baixo, reflexos, displays envelhecidos) - Considere se seu projeto realmente precisa de cor; meu site prova que tipografia e opacidade sozinhas podem sustentar uma hierarquia visual completa (veja meu artigo sobre design systems para startups para entender como equipes pequenas constroem sistemas de tokens sem uma equipe de design dedicada)
Para desenvolvedores:
- Use oklch() no CSS para definições de cores e teste taxas de contraste tanto com WCAG 2 (padrão atual) quanto com APCA (padrão futuro); o suporte a oklch() nos navegadores está disponível em todos os navegadores modernos
- Implemente o modo escuro ajustando luminosidade e saturação no espaço OKLCH em vez de inverter valores hexadecimais; o ajuste perceptual produz resultados melhores do que a inversão matemática
- A aplicação rigorosa de design tokens previne falhas silenciosas no CSS; se um token não existe, o design deve mudar, não o sistema de tokens (meu guia de design detalha a fundo os padrões de aplicação de tokens)
Perguntas frequentes
O que é um design sem cor e por que usar um?
Um design sem cor constrói toda a hierarquia visual por meio de tipografia e opacidade, em vez de cores de marca ou semânticas. Meu site usa texto branco sobre preto absoluto (#000000) em quatro níveis de opacidade: 100%, 65%, 40% e 10%. A abordagem força a coerência, porque cada elemento precisa conquistar seu peso visual pelo tamanho, pela espessura ou pela opacidade, em vez de se apoiar na cor como muleta. Ela também elimina por completo a armadilha de uniformidade perceptual do sRGB, já que a opacidade escala linearmente contra um fundo preto.2
O que é OKLCH e por que designers deveriam usá-lo no lugar do sRGB?
OKLCH (Oklab Lightness, Chroma, Hue) é um espaço de cores perceptualmente uniforme onde distâncias matemáticas iguais correspondem a diferenças percebidas iguais.7 No sRGB, mover 50 unidades no canal vermelho não produz a mesma mudança percebida que 50 unidades no canal verde, porque os olhos humanos contêm mais cones sensíveis ao verde. O OKLCH resolve isso: um passo de 10 unidades de luminosidade sempre parece a mesma quantidade de mudança, independentemente da cor inicial. O CSS moderno oferece suporte nativo a oklch() em todos os navegadores.
Como as custom properties do CSS ajudam a manter um sistema de cores?
As custom properties do CSS (variáveis) definidas em :root criam uma única fonte de verdade para cada valor de cor do sistema. Quando um valor não existe como token, quem deve mudar é o design, não o sistema de tokens. Aprendi isso ao usar --spacing-2xs, que eu nunca havia definido: o CSS falhou silenciosamente e a depuração custou 20 minutos.4 A aplicação rigorosa de tokens impede que valores arbitrários se acumulem e garante que cada decisão do design system seja intencional.
O modo escuro exclusivo prejudica a acessibilidade?
Não, desde que as taxas de contraste se mantenham suficientes. Meu nível de texto mais baixo, a 40% de opacidade, produz uma taxa de contraste de 8,4:1 contra o preto absoluto — quase o dobro do mínimo AA do WCAG, de 4,5:1, e acima do limiar AAA de 7:1.3 A pesquisa sobre o APCA (Accessible Perceptual Contrast Algorithm) mostra que texto claro sobre fundo escuro exige taxas de contraste mais altas do que texto escuro sobre fundo claro para alcançar legibilidade equivalente.10 Projetar bem um único modo, com folga em cada nível, produz resultados de acessibilidade melhores do que manter dois modos em que ambos saem prejudicados.
Qual é a diferença entre os requisitos de contraste do WCAG 2 e do APCA?
O WCAG 2 trata todas as cores igualmente, sem distinguir se o texto é escuro sobre claro ou claro sobre escuro, e usa uma simples razão de luminância (4,5:1 para AA, 7:1 para AAA).9 Já o APCA considera a sensibilidade de polaridade (texto escuro sobre fundos claros é mais legível na mesma razão matemática), a frequência espacial (fontes finas precisam de mais contraste) e a adaptação à luminância da página ao redor.10 Espera-se que o APCA forme a base do WCAG 3.0, e ele oferece previsões de legibilidade mais precisas para condições reais de tela.
Referências
-
WebAIM, “The WebAIM Million: 2023 Accessibility Analysis,” 2023. ↩
-
Author’s CSS custom properties from
critical.css. 10 color tokens, all derived from white-on-black opacity relationships. ↩↩ -
Author’s WCAG contrast calculations. Primary (21:1), secondary (13.7:1), tertiary (8.4:1), all exceeding AAA 7:1 minimum. ↩↩
-
Author’s debugging experience.
--spacing-2xswas used but never defined in:root. Documented in MEMORY.md error entries. ↩↩ -
Hunt, R.W.G., The Reproduction of Colour, Wiley, 2004. ↩
-
Poynton, Charles, Digital Video and HD, Morgan Kaufmann, 2012. Gamma correction and perceptual linearity. ↩
-
Ottosson, Bjorn, “A perceptual color space for image processing,” 2020. OKLCH specification. ↩↩
-
Author’s design decision. Single dark mode avoids visual compromise inherent in maintaining parallel light/dark systems. ↩
-
W3C, “Web Content Accessibility Guidelines (WCAG) 2.1,” 2018. ↩↩
-
Somers, Andrew, “APCA Contrast Calculator,” 2023. ↩↩↩
-
W3C, “WCAG 2.1 Success Criterion 1.4.1: Use of Color,” 2018. ↩
-
Author’s typography system. 13-step font scale from 0.75rem (12px) to 5rem (80px). System font stack eliminates FOIT/FOUT. ↩