O problema de contraste dos botões que a maioria dos design systems ignora
A maioria dos design systems define as cores de fundo dos botões. Mas muitos deles esquecem da outra metade do botão: a cor do texto que precisa ser legível sobre esse fundo. É aí que os problemas de contraste começam.
Um botão primário pode parecer perfeito hoje:
.button-primary { background: var(--color-primary); color: white;}Isso funciona enquanto --color-primary for um azul escuro, um roxo escuro ou qualquer outra cor escura da marca.
Mas o que acontece quando a cor primária muda?
:root { --color-primary: #facc15;}Agora o fundo do botão é amarelo. Mas o texto continua branco.
O fundo mudou. O texto não.
Esse é o problema de contraste que muitos desenvolvedores deixam passar.
Tokens de background não são suficientes
Uma cor de fundo, sozinha, não conta toda a história de acessibilidade.
Este token está incompleto:
--color-primary: #2563eb;Ele diz qual deve ser o fundo do botão, mas não diz qual cor de texto deve aparecer sobre ele. Então, os desenvolvedores chutam.
Geralmente escrevem:
color: white;Isso cria uma suposição escondida:
Fundos primários sempre serão escuros o suficiente para texto branco.
Mas design systems mudam. Marcas mudam. Temas mudam. O dark mode muda.
Cores de warning, success, danger e secondary podem se comportar de formas completamente diferentes.
É por isso que todo token de fundo preenchido deveria ter um parceiro de texto legível.
Use pares de foreground
Uma estrutura mais segura é assim:
:root { --color-primary: #2563eb; --color-on-primary: #ffffff;
--color-danger: #dc2626; --color-on-danger: #ffffff;
--color-warning: #facc15; --color-on-warning: #111827;
--color-success: #16a34a; --color-on-success: #ffffff;}Agora cada papel (role) de fundo tem um papel de texto que pertence a ele.
O componente não decide o par de contraste.
O tema decide.
.button-primary { background: var(--color-primary); color: var(--color-on-primary);}
.button-danger { background: var(--color-danger); color: var(--color-on-danger);}
.button-warning { background: var(--color-warning); color: var(--color-on-warning);}É uma pequena mudança de nomenclatura, mas ela muda a responsabilidade do sistema.
Em vez de cada componente chutar a cor do texto, os tokens do design system define a relação legível.
O exemplo do botão de warning
Botões de warning são um lugar comum onde esse problema aparece.
Muitos desenvolvedores escrevem:
.button-warning { background: #facc15; color: white;}Visualmente, isso pode parecer limpo. Mas amarelo costuma ser uma cor clara.
Branco também é claro. Então o texto pode ficar difícil de ler.
Um par de warning melhor é:
:root { --color-warning: #facc15; --color-on-warning: #111827;}Depois:
.button-warning { background: var(--color-warning); color: var(--color-on-warning);}O fundo continua comunicando alerta, mas o texto permanece legível.
Isso também ajuda no dark mode
Pares de foreground se tornam ainda mais úteis quando os temas mudam.
:root { --color-primary: #2563eb; --color-on-primary: #ffffff;}
:root[data-theme="dark"] { --color-primary: #60a5fa; --color-on-primary: #0f172a;}O componente do botão não muda:
.button-primary { background: var(--color-primary); color: var(--color-on-primary);}Só os valores dos tokens mudam. Esse é o benefício dos tokens semânticos.
Componentes mantêm um contrato estável. Temas controlam o par legível.
Bons tokens de botão descrevem relações
Um sistema de tokens de botão mais forte deveria incluir pares como:
--color-primary--color-on-primary
--color-secondary--color-on-secondary
--color-danger--color-on-danger
--color-success--color-on-success
--color-warning--color-on-warningO token on-* significa:
Esta é a cor usada sobre esse fundo.
Essa ideia simples evita muitos erros de contraste.
Ela também torna o design system mais fácil de usar para os desenvolvedores.
Eles não precisam mais perguntar “Esse texto do botão deveria ser branco ou escuro?”. O token já responde.
E, se o objetivo for automatizar essa decisão diretamente no CSS, vale conhecer a função contrast-color(), que calcula um texto preto ou branco legível a partir da cor de fundo, sem depender de tokens definidos manualmente.
O problema real
O problema não é que os desenvolvedores escolhem cores ruins.
O problema é que muitos design systems definem só metade da relação.
Eles definem o fundo, mas deixam o texto por conta do componente.
Isso cria botões inconsistentes, temas frágeis e problemas de acessibilidade que aparecem mais tarde.
Uma regra melhor é:
Todo token de background com significado deveria ter um token de texto legível correspondente.
Especialmente para estados de UI preenchidos como:
primarysecondarydangersuccesswarninginfodisabledselectedinverseConclusão
Acessibilidade de botão não é só escolher uma boa cor de fundo.
É definir a cor do texto que pertence a esse fundo.
Então, em vez disso:
background: var(--color-primary);color: white;Prefira isto:
background: var(--color-primary);color: var(--color-on-primary);A primeira versão presume o contraste; a segunda, projeta o contraste.
Esse é o problema de contraste de botão que a maioria dos design systems ignora.