Como adicionar dark mode a um site (CSS, Tailwind, Next.js)
Como adicionar dark mode a um site
Como adicionar dark mode: CSS, Tailwind e Next.js | We.Inc
Adicione dark mode com prefers-color-scheme, variáveis CSS e light-dark(), um seletor Claro/Escuro/Sistema, Tailwind v4 e next-themes, sem piscar ao carregar.
O dark mode mais simples é só CSS: declare color-scheme: light dark, guarde as cores em custom properties e troque os valores dentro de @media (prefers-color-scheme: dark). Para um seletor manual, defina uma classe ou um atributo data no elemento html, salve a escolha no localStorage e aplique-a com um pequeno script inline no head, para que o tema errado nunca pisque na tela.
Qual abordagem combina com a sua stack
Stack
Segue o sistema
Seletor manual
Peça principal
CSS puro
Sim
Não
@media (prefers-color-scheme: dark)
CSS + script pequeno
Sim
Sim
data-theme no html + script inline no head
Tailwind CSS v4
Sim, por padrão
Com @custom-variant
utilitários dark:
Next.js App Router
Sim
Sim
ThemeProvider do next-themes
O dark mode tem três partes: cores que podem ser trocadas, uma forma de decidir qual conjunto usar e o momento certo para que a página seja pintada corretamente logo na primeira vez. A prévia abaixo permite alternar entre claro, escuro e sistema em um layout de exemplo, e cada seção traz o código exato para CSS puro, Tailwind e Next.js.
Escolha Claro, Escuro ou Sistema na prévia. Sistema segue o seu sistema operacional e atualiza ao vivo se você mudá-lo.
Repare que o campo do formulário e a barra de rolagem também mudam. É a propriedade color-scheme em ação.
Escolha a sua stack nos exemplos abaixo e copie o código. O script contra o piscar vai inline no head.
Teste recarregando em cada modo, e uma vez com o sistema operacional no modo escuro e nada salvo.
Não inverta simplesmente preto puro e branco puro. Cinzas escuros um pouco mais claros com texto em branco suave são mais fáceis de ler por muito tempo.
Confira o contraste de novo nos dois temas. Um azul da marca que passa no branco pode falhar em um fundo cinza escuro.
Dê às imagens e aos logos uma variante escura, ou um fundo transparente, para que uma caixa branca não brilhe em uma página escura.
Guarde a opção sistema como ausência de valor salvo, e não como uma palavra, para que uma mudança futura no sistema ainda seja seguida.
Adicione <meta name="color-scheme" content="light dark"> para que o navegador pinte um fundo escuro antes de o seu CSS chegar.
Perguntas frequentes
Como evito o flash branco quando a página carrega no modo escuro?
Decida o tema antes da primeira pintura. Coloque um pequeno script bloqueante no topo do head que leia a escolha salva e a preferência do sistema e defina a classe ou o atributo no elemento html. Scripts no fim do body ou dentro de um componente de framework executam tarde demais.
Como funciona o dark mode do Tailwind?
No Tailwind v4, a variante dark: usa prefers-color-scheme por padrão, então dark:bg-gray-900 é aplicado quando o sistema está no modo escuro. Para controlá-lo com um seletor, adicione @custom-variant dark (&:where(.dark, .dark *)); ao seu CSS e coloque a classe dark no elemento html.
Como adiciono dark mode no Next.js?
O caminho comum é o pacote next-themes, com licença MIT. Envolva o app no ThemeProvider dele dentro do root layout, adicione suppressHydrationWarning à tag html e chame setTheme a partir de useTheme em um client component. Ele injeta o script contra o piscar para você.
O que o color-scheme faz?
Ele diz ao navegador quais esquemas a sua página suporta, para que as partes nativas, como controles de formulário, barras de rolagem e o fundo da página, usem cores combinando. Definir color-scheme: light dark na raiz também é necessário para que a função light-dark() escolha o segundo valor.
O dark mode deve seguir o sistema ou ter um seletor?
Os dois. Use a preferência do sistema como padrão, porque as pessoas já fizeram essa escolha, e ofereça um seletor com Claro, Escuro e Sistema para que o visitante possa substituí-la só no seu site.
Monte a paleta primeiro com variáveis
Dê nome às cores pela função, não pela aparência: --bg, --surface, --text, --muted, --accent, --border. Os componentes usam sempre esses nomes. Trocar de tema passa a ser redefinir algumas variáveis em um só lugar.
A função light-dark() encurta isso ainda mais, guardando os dois valores em uma única declaração. A MDN a lista como Baseline 2024, então mantenha um fallback para navegadores antigos se o seu público precisar.
Deixando o seletor acessível
Um seletor é um botão de verdade com um nome claro. Para um interruptor de dois estados, use aria-pressed; para três opções, um pequeno grupo de radio buttons ou um select é mais claro que um ícone que alterna em ciclo.
Não anuncie nada a mais. A mudança visível é o retorno, e o próprio estado do controle informa aos usuários de leitor de tela o que está selecionado.
Renderização no servidor e hydration
O servidor não tem como saber o tema do visitante na primeira requisição, então o HTML renderizado não pode incluir texto específico do tema, como Mudar para claro. Renderize o rótulo do seletor só depois que o componente montar, ou renderize algo neutro.
É também por isso que o next-themes pede suppressHydrationWarning no html: o script dele muda um atributo ali antes de o React fazer a hydration.
1. CSS puro que segue o sistema
Variáveis trocadas por prefers-color-scheme
O quadro de resultado segue a configuração do seu aparelho. Mude o sistema para o modo escuro e ele atualiza sem recarregar.
A mesma ideia com light-dark()
Uma declaração por cor. Precisa de color-scheme na raiz, e a MDN a lista como Baseline 2024.
2. Um seletor Claro, Escuro e Sistema sem piscar
Script inline no topo do head
Executa antes de o body ser pintado. Sem nada salvo, ele não faz nada, então a media query continua seguindo o sistema.
CSS que respeita tanto o atributo quanto o sistema
O atributo vence quando está presente. Sem ele, a media query decide.
O seletor em si
Um select com três opções. Escolher Sistema remove o valor salvo.
3. Tailwind CSS v4
Utilitários dark: com um seletor por classe
Sem a linha @custom-variant, dark: segue prefers-color-scheme. Com ela, a classe dark no html controla o tema.
4. Next.js App Router com next-themes
Root layout e um provider no cliente
ThemeProvider é um client component, então fica em um arquivo próprio. attribute="class" o combina com a classe dark do Tailwind.
Seletor que espera até ser montado
O servidor não conhece o tema, então o controle é renderizado depois de montar, para evitar um hydration mismatch.
Prévia do tema
Alterne entre Claro, Escuro e Sistema para ver um layout de exemplo mudar, incluindo os controles de formulário.
Tema
Claro
Escuro
Sistema (escuro agora)
Sistema (claro agora)
Padaria do Porto
Exemplo
Cardápio
Visite
Fresquinho às 7h
Pães de fermentação natural, de centeio e com sementes toda manhã.