A versão curta
- 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.
Nesta página
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, expressa diretamente em CSS.
--violet-600: #7c3aedQuem a lê
Muda quando
--color-accent: var(--violet-600)Quem a lê
Muda quando
Exemplo
--button-bg: var(--color-accent)Quem a lê
Muda quando
: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); }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.
: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);
}light-dark() elimina a repetição.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 e os gradientes para modo escuro 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.
<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>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
lightoudark; 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.
[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); }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.
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.
Perguntas frequentes
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.
Escrito pela Gradiently
A equipa por trás da Gradiently, uma ferramenta de design construída em torno dos Marks: gradientes vivos que fazem tudo o que crias parecer teu.
Ver o nosso perfil