# O problema de contraste dos botões que a maioria dos design systems ignora

> A maioria dos design systems define só o fundo dos botões, esquecendo do texto. Veja como tokens semânticos resolvem esse problema de contraste.

URL: https://dpw.dev/design-system/problema-contraste-botoes-design-systems/  
Publicado em: 2026-07-26  
Categoria: design-system  
Tags: interface, acessibilidade  
Autor: Tárcio Zemel

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.

> Artigo baseado em [The Button Contrast Problem Most Design Systems Ignore](https://dev.to/hasansarwer/the-button-contrast-problem-most-design-systems-ignore-4odc).

Um botão primário pode parecer perfeito hoje:

```css
.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?

```css
: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 <a href="/categoria/acessibilidade" target="_blank">acessibilidade</a>.

Este token está incompleto:

```css
--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:

```css
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:

```css
: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.

```css
.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:

```css
.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 é:

```css
:root {
  --color-warning: #facc15;
  --color-on-warning: #111827;
}
```

Depois:

```css
.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.

```css
: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:

```css
.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.

> **Dica:** Ainda sobre dark mode, veja nosso vídeo sobre CSS `light-dark()`:
>
> [Vídeo no YouTube](https://www.youtube.com/watch?v=KMo6MactKwM)

## 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-warning
```

O 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 <a href="/acessibilidade/css-contrast-color/" target="_blank">`contrast-color()`</a>, 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:

```
primary
secondary
danger
success
warning
info
disabled
selected
inverse
```

## Conclusã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:

```css
background: var(--color-primary);
color: white;
```

Prefira isto:

```css
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.

---

Fonte: [dpw - desenvolvimento para web](https://dpw.dev/) (pt-BR).
Guia para agentes: https://dpw.dev/llms.txt
Índice completo: https://dpw.dev/sitemap-index.xml
Feed: https://dpw.dev/rss.xml
