Capítulo 81 de 108

Dark Mode (Astro)

Core Idea

Astro usa um <script is:inline> que roda antes da hidratação de qualquer componente React, aplicando a classe dark no <html> a partir de localStorage/prefers-color-scheme, e observa mudanças de classe para persistir o toggle feito por um ilha React.

Key Concepts

  • script is:inline: executa síncrono no HTML, sem esperar bundle JS, evitando flash de tema errado (FOUC).
  • MutationObserver: observa mudanças na classe do <html> (feitas pelo ModeToggle React) e persiste em localStorage.
  • client:load: diretiva Astro necessária no <ModeToggle client:load /> para hidratar o componente React na ilha.
  • O componente ModeToggle mantém seu próprio state React que só sincroniza indiretamente via manipulação direta do classList.

Code Examples

<script is:inline>
  const getThemePreference = () => {
    if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
      return localStorage.getItem('theme');
    }
    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  };
  const isDark = getThemePreference() === 'dark';
  document.documentElement.classList[isDark ? 'add' : 'remove']('dark');

  if (typeof localStorage !== 'undefined') {
    const observer = new MutationObserver(() => {
      const isDark = document.documentElement.classList.contains('dark');
      localStorage.setItem('theme', isDark ? 'dark' : 'light');
    });
    observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
  }
</script>
  • O que demonstra: Aplicação de tema pré-hidratação + observador que persiste toggles feitos depois pela ilha React.
React.useEffect(() => {
  const isDark = theme === "dark" || (theme === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches)
  document.documentElement.classList[isDark ? "add" : "remove"]("dark")
}, [theme])
<ModeToggle client:load />
  • O que demonstra: A ilha React manipula a classe diretamente; o MutationObserver do script inline cuida da persistência.

Anti-patterns

  • Esquecer client:load no <ModeToggle />: sem a diretiva, o componente React não hidrata e o toggle não funciona.

Key Takeaways

  1. O script inline é a peça-chave para evitar flash de tema incorreto em sites estáticos/SSR do Astro.
  2. Persistência não é feita diretamente pelo componente React, e sim pelo MutationObserver no script inline observando a classe do <html>.
  3. Padrão útil sempre que o framework não oferece hooks de hidratação síncrona antes do primeiro paint.

Connects To

  • dark-mode: índice geral.