Ir al contenido

Dark Mode

El proyecto soporta tema oscuro y claro con persistencia y detección automática.

ThemeProvider (apps/web/components/ThemeProvider.tsx)

Sección titulada «ThemeProvider (apps/web/components/ThemeProvider.tsx)»

Provider de contexto que gestiona el estado del tema:

  1. Al montar, busca en localStorage("theme")
  2. Si no hay valor guardado, detecta prefers-color-scheme: dark
  3. Aplica/remueve la clase dark en <html>
  4. 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>
app/[locale]/layout.tsx
import { ThemeProvider } from "@/components/ThemeProvider";
export default function LocaleLayout({ children }) {
return (
<ThemeProvider>
{children}
</ThemeProvider>
);
}
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
"use client";
import { useTheme } from "@/components/ThemeProvider";
function MiComponente() {
const { theme, toggleTheme } = useTheme();
return <p>Tema actual: {theme}</p>;
}
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