# Tema com variáveis CSS: claro, escuro e cores de marca

[Canonical HTML page](https://gradiently.design/pt-pt/guide/css-custom-properties-theming)

Os temas correm mal quando as cores têm o nome do que parecem em vez do que fazem. Dá nome às funções, aponta-as para uma paleta, e o modo claro, o modo escuro e uma segunda marca passam a ser poucas linhas cada.

## The short version

- Um tema com variáveis CSS funciona melhor em duas camadas: variáveis de paleta que guardam as cores em bruto e variáveis semânticas que descrevem para que serve cada cor.
- Os componentes só devem ler variáveis semânticas como --color-text ou --color-surface, nunca valores de paleta diretamente.
- O modo escuro resume-se então a apontar as variáveis semânticas para outros valores da paleta, sob uma media query ou um atributo de dados.
- A função light-dark() e a propriedade color-scheme deixam uma só declaração guardar os dois modos nos navegadores atuais.
- Um pequeno script inline no head que define o tema guardado antes de a página ser pintada evita o piscar do tema errado.

Um **tema com variáveis CSS** é um conjunto de propriedades personalizadas, como `--color-surface` e `--color-text`, que cada componente lê em vez de cores fixas no código. Para mudar o tema mudas as variáveis, normalmente em `:root` ou num atributo `data-theme`, e a interface inteira acompanha. O truque que o torna escalável é dividir as variáveis em duas camadas: uma paleta de cores em bruto e funções semânticas que apontam para essa paleta.

## Duas camadas: paleta e funções

As variáveis de paleta têm o nome do que são: `--violet-600`, `--ink-900`. As variáveis semânticas têm o nome do que fazem: `--color-accent`, `--color-text-muted`. Os componentes leem só o segundo tipo. Quando chega o modo escuro, voltas a apontar as funções e deixas todos os componentes intocados. É a mesma ideia dos [tokens de design](https://gradiently.design/pt-pt/guide/design-tokens), expressa diretamente em CSS.

| Camada | Exemplo | Quem a lê | Muda quando |
| --- | --- | --- | --- |
| Paleta | `--violet-600: #7c3aed` | Só a camada de funções | A marca é redesenhada |
| Função | `--color-accent: var(--violet-600)` | Todos os componentes | O tema ou o modo muda |
| Componente | `--button-bg: var(--color-accent)` | Um componente | Um só componente precisa de um toque local |

A camada de componente é opcional. Usa-a só onde um componente precisa mesmo de diferir da função.

```css
:root {
  /* Palette: raw values, named for what they are */
  --paper-50: #faf8f5;
  --paper-100: #f1ede6;
  --ink-900: #14121f;
  --ink-700: #3d3a4f;
  --ink-500: #6b6880;
  --violet-600: #6d28d9;
  --violet-300: #c4b5fd;
  --night-950: #0d0b16;
  --night-900: #17142a;
  --night-800: #221e3a;

  /* Roles: what each colour is for (light mode) */
  --color-bg: var(--paper-50);
  --color-surface: var(--paper-100);
  --color-text: var(--ink-900);
  --color-text-muted: var(--ink-500);
  --color-border: color-mix(in oklab, var(--ink-900) 12%, transparent);
  --color-accent: var(--violet-600);
  --color-on-accent: #ffffff;
}

body { background: var(--color-bg); color: var(--color-text); }
.card { background: var(--color-surface); border: 1px solid var(--color-border); }
.button { background: var(--color-accent); color: var(--color-on-accent); }
```

Os componentes nunca mencionam violeta nem tinta. Só esta regra é que torna o tema substituível.

- bg: #faf8f5
- surface: #f1ede6
- text: #14121f
- text-muted: #6b6880
- accent: #6d28d9

As funções do tema claro. Um fundo de papel quente em vez de branco puro faz o destaque violeta parecer mais calmo.

### Dar nomes às funções que sobrevivem a um redesenho

O nome de uma função deve continuar verdadeiro depois de as cores mudarem. `--light-grey` deixa de ser claro no modo escuro, e `--blue` mente no dia em que a marca passa a verde. Nomeia antes a tarefa e o par, para que quem ler a folha de estilos a seguir saiba que cor vai com qual.

### Nomes que se partem

- `--blue`, `--purple-button`
- `--light-grey-bg`
- `--white-text`
- `--dark-border`

### Nomes que duram

- `--color-accent`, `--color-on-accent`
- `--color-surface`
- `--color-text`
- `--color-border`

## Acrescentar modo escuro com variáveis CSS

O modo escuro volta a apontar as funções. Respeita por predefinição a definição do sistema operativo e deixa um atributo `data-theme` substituí-la quando o leitor escolhe. Define também `color-scheme`, para que as barras de deslocamento, os controlos de formulário e a tela predefinida condigam.

```css
:root { color-scheme: light; }

/* Follow the system unless the reader picked light */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --color-bg: var(--night-950);
    --color-surface: var(--night-900);
    --color-text: #f2f0fa;
    --color-text-muted: #a5a1bd;
    --color-border: color-mix(in oklab, #ffffff 12%, transparent);
    --color-accent: var(--violet-300);
    --color-on-accent: var(--ink-900);
  }
}

/* The reader picked dark explicitly */
:root[data-theme="dark"] {
  color-scheme: dark;
  --color-bg: var(--night-950);
  --color-surface: var(--night-900);
  --color-text: #f2f0fa;
  --color-text-muted: #a5a1bd;
  --color-border: color-mix(in oklab, #ffffff 12%, transparent);
  --color-accent: var(--violet-300);
  --color-on-accent: var(--ink-900);
}
```

Os valores escuros aparecem duas vezes: uma para a preferência do sistema e outra para uma escolha explícita. Um mixin de pré-processador ou o `light-dark()` elimina a repetição.

- bg: #0d0b16
- surface: #17142a
- text: #f2f0fa
- text-muted: #a5a1bd
- accent: #c4b5fd

As funções do tema escuro. O destaque passa a um violeta mais claro, porque o violeta do modo claro é demasiado escuro para se ler sobre um fundo quase preto.

O modo escuro não é o modo claro invertido. As superfícies ficam mais claras à medida que sobem, os destaques aclaram e as sombras cedem lugar às bordas. O [design para modo escuro](https://gradiently.design/pt-pt/guide/dark-mode-design) e os [gradientes para modo escuro](https://gradiently.design/pt-pt/guide/dark-mode-gradients) tratam do lado visual.

### O atalho light-dark()

Os navegadores atuais suportam `light-dark()`, que recebe um valor claro e um escuro e escolhe um com base no `color-scheme` do elemento. Define `color-scheme: light dark` na raiz para seguir o sistema, ou força um com o atributo, e cada função passa a uma só linha: `--color-bg: light-dark(var(--paper-50), var(--night-950))`. Mantém a forma mais longa se tiveres de suportar navegadores anteriores a 2024.

## Um botão de tema que não pisca

Se a escolha guardada for aplicada depois de a página ser desenhada, os leitores que escolheram escuro veem um clarão branco em cada carregamento. Define o atributo num pequeno script inline no `head`, antes de qualquer folha de estilos pintar o corpo.

```html
<head>
  <script>
    (function () {
      var saved = localStorage.getItem("theme");
      if (saved === "light" || saved === "dark") {
        document.documentElement.dataset.theme = saved;
      }
    })();
  </script>
  <link rel="stylesheet" href="/styles.css">
</head>
```

Corre antes da primeira pintura. O botão só precisa então de definir `dataset.theme` e guardar o valor em `localStorage`.

1. **Oferece três escolhas** Claro, escuro e sistema. Remover o atributo devolve o controlo ao `prefers-color-scheme`.
2. **Guarda só as escolhas explícitas** Guarda `light` ou `dark`; apaga a chave para sistema, para que uma alteração posterior no sistema operativo seja respeitada.
3. **Evita transições ao carregar** Se as cores têm transição, ativa-a só depois da primeira pintura, ou a página anima de claro para escuro enquanto carrega.

## Temas de marca para além de claro e escuro

As mesmas duas camadas tratam várias marcas numa só base de código. Cada marca fornece a sua própria paleta e valores de função sob um atributo `data-brand`, e os modos continuam a funcionar por cima. Com o `color-mix()` podes derivar estados de rato por cima e tons a partir de um destaque por marca, como se mostra em [color-mix() em CSS](https://gradiently.design/pt-pt/guide/css-color-mix).

```css
[data-brand="harbour"] {
  --color-accent: #0f766e;
  --gradient-hero: linear-gradient(150deg, #042f2e 0%, #0f766e 55%, #5eead4 100%);
}

[data-brand="ember"] {
  --color-accent: #c2410c;
  --gradient-hero: linear-gradient(150deg, #431407 0%, #c2410c 55%, #fdba74 100%);
}

.button:hover { background: color-mix(in oklab, var(--color-accent), black 12%); }
.hero { background: var(--gradient-hero); }
```

Os gradientes também podem ser tokens. Guarda o valor inteiro numa variável e o herói muda com a marca.

- Herói Harbour: `linear-gradient(150deg, #042f2e 0%, #0f766e 55%, #5eead4 100%)`
- Herói Ember: `linear-gradient(150deg, #431407 0%, #c2410c 55%, #fdba74 100%)`

Os dois tokens de herói do código acima. Mesma estrutura, mesmo ângulo, marca diferente.

Como as propriedades personalizadas são herdadas, um tema não tem de cobrir a página inteira. Põe `data-theme="dark"` numa secção, como uma faixa promocional ou um rodapé, e tudo lá dentro lê as funções escuras enquanto o resto da página fica claro. Escreve o seletor escuro como `[data-theme="dark"]` em vez de `:root[data-theme="dark"]` se quiseres que isso funcione, e define também `color-scheme` na secção.

> **Os gradientes não fazem transição sozinhos** Alterar uma variável de gradiente troca o fundo de imediato. Para animar entre dois gradientes, regista as paragens de cor com `@property`, explicado no [CSS @property](https://gradiently.design/pt-pt/guide/css-property-animation).

## Mantém a web e os teus designs na mesma paleta

Um tema em CSS é só metade de uma marca. As mesmas cores devem aparecer nas tuas publicações sociais, apresentações e emails. O kit de marca do Gradiently guarda as tuas paletas, logótipos e letras de títulos e de texto, e o Designer aplica-o ao compor um design, por isso a publicação que fazes na segunda usa o mesmo destaque do teu site. Se ainda estás a escolher essas cores, começa por [como escolher as cores da marca](https://gradiently.design/pt-pt/guide/how-to-choose-brand-colors).

## FAQ

### Como faço um tema com variáveis CSS?

Define variáveis de paleta com cores em bruto e depois variáveis semânticas como `--color-bg` que apontam para elas. Os componentes leem só as semânticas e os temas voltam a apontá-las.

### Como acrescento modo escuro com variáveis CSS?

Substitui as variáveis semânticas dentro de `@media (prefers-color-scheme: dark)` e sob um seletor `[data-theme="dark"]` para os leitores que o escolhem.

### Como evito que o tema pisque ao carregar a página?

Lê o tema guardado num pequeno script inline no `head` e define o atributo antes de a folha de estilos pintar a página.

### O que faz o light-dark() em CSS?

Devolve o primeiro valor no modo claro e o segundo no modo escuro, com base no `color-scheme` do elemento. Funciona nos principais navegadores atuais.

### Posso guardar um gradiente numa variável CSS?

Sim. Guarda o gradiente inteiro como valor e usa-o em `background`. Para animar entre gradientes, regista as paragens com `@property`.
