Modo Oscuro
Reglas
- Usa
next-themes para aplicaciones Next.js — envuelve la aplicación en <ThemeProvider attribute="class" defaultTheme="system">
- Variables CSS para colores — define en
:root (claro) y .dark (oscuro), referencia como var(--background)
- Detección de preferencia del sistema:
defaultTheme="system" respeta la media query prefers-color-scheme
- Componente de alternancia de tema: cicla entre claro/oscuro/sistema — usa el hook
useTheme() de next-themes
- Integración con Tailwind: habilita
darkMode: "class" en la configuración de Tailwind, usa el prefijo dark: para anulaciones
- Corrección de hidratación: evita la discrepancia de hidratación montando el alternador de tema solo después de
useEffect o usando suppressHydrationWarning
- Persistencia:
next-themes almacena la preferencia en localStorage y la aplica como una clase en <html>
- Transición: agrega
transition-colors duration-200 al body para un cambio suave de tema
Patrones
import { ThemeProvider } from "next-themes";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
</body>
</html>
);
}
"use client";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null;
return (
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
{theme === "dark" ? "☀️" : "🌙"}
</button>
);
}
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
}
Evitar
- Colores hardcodeados en lugar de variables CSS — imposibilita el cambio de tema
- Olvidar
suppressHydrationWarning en <html> — causa errores de hidratación
- Destello del tema incorrecto: asegura que el script de
next-themes se ejecute antes del renderizado (lo hace por defecto)
- Soporte solo claro/oscuro sin opción de sistema — respeta la preferencia del SO del usuario