Como implementar dark mode com CSS variables e sem JavaScript
1. Fundamentos: Por que CSS Variables e Sem JavaScript?
A implementação de dark mode com CSS Variables e sem JavaScript representa uma abordagem moderna que prioriza performance, acessibilidade e simplicidade. Diferente das soluções tradicionais que dependem de JavaScript para manipular classes no DOM ou armazenar preferências em localStorage, esta técnica utiliza recursos nativos do CSS para oferecer uma experiência instantânea e respeitosa com as preferências do usuário.
Performance e acessibilidade: O carregamento é instantâneo porque não há scripts bloqueando a renderização. A regra prefers-color-scheme respeita automaticamente a configuração do sistema operacional do usuário, eliminando a necessidade de perguntar ou adivinhar sua preferência.
Manutenibilidade: Com as variáveis centralizadas em :root e escopos específicos, alterar um tema inteiro requer modificar apenas alguns valores. Não há código JavaScript para depurar ou atualizar.
Comparação com abordagens tradicionais: Soluções com JavaScript frequentemente causam flash de conteúdo não estilizado (FOUC) durante o carregamento, enquanto a abordagem puramente CSS elimina completamente esse problema.
2. Configuração Inicial: Definindo a Paleta de Cores com :root
A base de qualquer sistema de temas com CSS Variables é uma paleta de cores bem estruturada no seletor :root. A nomenclatura semântica é crucial para facilitar a troca de temas e a manutenção do código.
:root {
/* Cores de fundo e texto */
--color-bg: #ffffff;
--color-text: #1a1a1a;
--color-text-secondary: #666666;
/* Cores de destaque */
--color-primary: #0066cc;
--color-primary-hover: #0052a3;
--color-primary-text: #ffffff;
/* Cores de superfície */
--color-surface: #f5f5f5;
--color-surface-hover: #e8e8e8;
--color-border: #d1d1d1;
/* Cores de estado */
--color-success: #28a745;
--color-warning: #ffc107;
--color-error: #dc3545;
/* Sombras */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
}
Nomenclatura semântica vs. funcional: Prefira nomes como --color-bg em vez de --color-white. Isso permite que você mude o tema sem renomear variáveis. Valores padrão (fallback) garantem compatibilidade com navegadores legados:
body {
background-color: var(--color-bg, #ffffff);
color: var(--color-text, #1a1a1a);
}
3. Implementação do Dark Mode com prefers-color-scheme
A regra @media (prefers-color-scheme: dark) é o coração desta implementação. Ela detecta automaticamente a preferência do sistema operacional e aplica os estilos correspondentes.
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #121212;
--color-text: #e0e0e0;
--color-text-secondary: #a0a0a0;
--color-primary: #4da6ff;
--color-primary-hover: #66b3ff;
--color-primary-text: #121212;
--color-surface: #1e1e1e;
--color-surface-hover: #2d2d2d;
--color-border: #404040;
--color-success: #2ecc71;
--color-warning: #f39c12;
--color-error: #e74c3c;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
}
}
Garantindo compatibilidade: Para navegadores que não suportam prefers-color-scheme, as variáveis definidas em :root fora da media query servem como fallback. Você pode também definir um tema claro explícito:
@media (prefers-color-scheme: no-preference) {
:root {
/* Tema claro padrão */
}
}
4. Estratégias de Escopo: Temas por Componente ou Seção
Embora o prefers-color-scheme funcione globalmente, você pode querer temas específicos para componentes ou seções. Classes contextuais permitem override manual sem JavaScript:
/* Tema escuro forçado em um componente específico */
.card.dark-mode {
--color-bg: #2d2d2d;
--color-text: #e0e0e0;
--color-border: #404040;
}
/* Tema claro forçado */
.card.light-mode {
--color-bg: #ffffff;
--color-text: #1a1a1a;
--color-border: #d1d1d1;
}
/* Uso no componente */
.card {
background-color: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
}
Exemplo prático: Um modal que deve ter tema escuro independentemente da preferência do sistema:
.modal.dark-mode {
--color-bg: #1e1e1e;
--color-text: #e0e0e0;
--color-surface: #2d2d2d;
background-color: var(--color-bg);
color: var(--color-text);
}
.modal-header {
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
}
5. Tratamento de Imagens e Mídia no Dark Mode
Imagens e ícones precisam de tratamento especial no dark mode para manter a legibilidade e a estética. Filtros CSS são uma solução elegante:
@media (prefers-color-scheme: dark) {
/* Reduz brilho de imagens para evitar ofuscamento */
img:not(.no-dark-mode) {
filter: brightness(0.8) contrast(1.1);
}
/* Ajusta ícones SVG */
.icon {
filter: invert(1) hue-rotate(180deg);
}
/* Logotipos que não devem ser alterados */
.logo.no-dark-mode {
filter: none;
}
}
Elemento picture para imagens específicas: Quando você precisa de imagens diferentes para cada tema:
<picture>
<source srcset="logo-dark.svg" media="(prefers-color-scheme: dark)">
<img src="logo-light.svg" alt="Logotipo" class="no-dark-mode">
</picture>
6. Transições Suaves e Experiência do Usuário
Transições suaves evitam mudanças bruscas e melhoram a experiência do usuário. Aplique transition nas propriedades que mudam com o tema:
/* Transição global para elementos que usam variáveis de tema */
* {
transition: background-color 0.3s ease,
color 0.3s ease,
border-color 0.3s ease,
box-shadow 0.3s ease;
}
/* Evita transição em elementos que não devem animar */
.no-transition {
transition: none !important;
}
Prevenindo FOUC: Como não há JavaScript, não há risco de flash de conteúdo não estilizado. O tema é aplicado antes mesmo do primeiro paint.
Dica avançada com color-scheme: Esta propriedade estiliza scrollbars e inputs nativos:
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
}
}
@media (prefers-color-scheme: light) {
:root {
color-scheme: light;
}
}
7. Manutenção e Extensibilidade: Adicionando Novos Temas
A estrutura com variáveis facilita a adição de novos temas. Você pode combinar prefers-color-scheme com prefers-contrast para temas de alto contraste:
/* Tema escuro de alto contraste */
@media (prefers-color-scheme: dark) and (prefers-contrast: more) {
:root {
--color-bg: #000000;
--color-text: #ffffff;
--color-primary: #66b3ff;
--color-border: #ffffff;
--color-surface: #1a1a1a;
}
}
/* Tema sépia para leitura */
@media (prefers-color-scheme: light) and (prefers-contrast: less) {
:root {
--color-bg: #f5f0e8;
--color-text: #5c4a3a;
--color-primary: #8b6914;
--color-border: #d4c5a9;
}
}
Checklist de acessibilidade WCAG:
- Contraste mínimo de 4.5:1 para texto normal
- Contraste de 3:1 para texto grande
- Testar em ambos os modos com ferramentas como axe DevTools
- Verificar foco visível em elementos interativos
Versionamento de variáveis: Mantenha um arquivo único com todas as variáveis e use comentários para documentar mudanças:
/*
* Tema v2.1 - 2024
* Adicionado: --color-surface-hover
* Modificado: --color-primary para melhor contraste no dark mode
*/
Conclusão
Implementar dark mode com CSS Variables e sem JavaScript não é apenas uma questão técnica, mas uma decisão de design que prioriza performance, acessibilidade e manutenibilidade. Esta abordagem respeita as preferências do usuário desde o primeiro carregamento, elimina dependências de scripts e oferece uma base sólida para escalar para múltiplos temas. Com as técnicas apresentadas — desde a estruturação de variáveis até o tratamento de mídia e transições suaves — você tem tudo o que precisa para implementar um sistema de temas completo e profissional.
Referências
- MDN Web Docs: Using CSS custom properties (variables) — Documentação oficial completa sobre variáveis CSS, incluindo fallbacks e escopos.
- MDN Web Docs: prefers-color-scheme — Especificação completa da media query para detecção de preferência de tema.
- CSS-Tricks: A Complete Guide to Dark Mode on the Web — Guia abrangente sobre implementação de dark mode, incluindo estratégias com CSS Variables.
- Web.dev: prefers-color-scheme: Hello darkness, my old friend — Artigo do Google sobre boas práticas para implementar dark mode respeitando preferências do sistema.
- Smashing Magazine: Dark Mode In CSS: A Complete Guide — Guia detalhado com exemplos práticos de implementação, incluindo tratamento de imagens e transições.
- W3C: CSS Color Adjustment Module Level 1 — Especificação oficial da propriedade
color-schemepara estilização de elementos nativos.