Dark Mode
El proyecto soporta tema oscuro y claro con persistencia y detección automática.
Arquitectura
Sección titulada «Arquitectura»ThemeProvider (apps/web/components/ThemeProvider.tsx)
Sección titulada «ThemeProvider (apps/web/components/ThemeProvider.tsx)»Provider de contexto que gestiona el estado del tema:
- Al montar, busca en
localStorage("theme") - Si no hay valor guardado, detecta
prefers-color-scheme: dark - Aplica/remueve la clase
darken<html> - Persiste la elección en
localStorage
"use client";
export function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState<Theme>("light"); const [mounted, setMounted] = useState(false);
useEffect(() => { const stored = localStorage.getItem("theme") as Theme | null; if (stored) { setTheme(stored); } else { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; setTheme(prefersDark ? "dark" : "light"); } setMounted(true); }, []);
useEffect(() => { if (!mounted) return; document.documentElement.classList.toggle("dark", theme === "dark"); localStorage.setItem("theme", theme); }, [theme, mounted]); // ...}ThemeToggle (apps/web/components/ThemeToggle.tsx)
Sección titulada «ThemeToggle (apps/web/components/ThemeToggle.tsx)»Botón que alterna entre sol y luna con animación de rotación y escala.
<button onClick={toggleTheme}> <Sun className={theme === "dark" ? "opacity-0 rotate-90 scale-0" : "opacity-100 rotate-0 scale-100"} /> <Moon className={theme === "dark" ? "opacity-100 rotate-0 scale-100" : "opacity-0 -rotate-90 scale-0"} /></button>Cómo usar
Sección titulada «Cómo usar»Envolver la app
Sección titulada «Envolver la app»import { ThemeProvider } from "@/components/ThemeProvider";
export default function LocaleLayout({ children }) { return ( <ThemeProvider> {children} </ThemeProvider> );}Clases condicionales con Tailwind
Sección titulada «Clases condicionales con Tailwind»<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">Consumir el tema en componentes
Sección titulada «Consumir el tema en componentes»"use client";import { useTheme } from "@/components/ThemeProvider";
function MiComponente() { const { theme, toggleTheme } = useTheme(); return <p>Tema actual: {theme}</p>;}Archivos clave
Sección titulada «Archivos clave»| Archivo | Propósito |
|---|---|
components/ThemeProvider.tsx |
Contexto + lógica de persistencia |
components/ThemeToggle.tsx |
Botón de alternancia |
app/globals.css |
Variables CSS para modo oscuro |