Setup limpio de theming y modo oscuro en Next.js 16 + Tailwind v4 con next-themes y OKLCH — y el gotcha de `@theme inline` que rompe el dark mode en silencio.
El modo oscuro en Tailwind v4 es a la vez más simple y más tramposo que en v3. Más simple porque la config se movió al CSS y next-themes hace el trabajo pesado. Más tramposo porque la nueva directiva @theme tiene un comportamiento sutil que rompe el modo oscuro en silencio si te equivocas.
Aquí tienes un setup de theming limpio para Next.js 16 + Tailwind v4, y el gotcha que le cuesta una tarde a la gente.
Defines tus design tokens como variables CSS, los mapeas a tokens de Tailwind una vez, y dejas que next-themes cambie una clase en ``. Los componentes referencian tokens semánticos (bg-background, text-foreground, bg-primary) y nunca les importa qué tema está activo.
1. Tokens como variables CSS, por tema. En globals.css, define :root { --background: ...; --foreground: ...; --primary: ...; } y sobreescríbelos bajo .dark { ... }. Usa valores OKLCH para luminosidad predecible.
2. Mapealos con @theme inline. Esta es la línea crítica:
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
}La palabra inline le dice a Tailwind que referencie la variable en runtime, así que los overrides de .dark se aplican. El @theme a secas hornea el valor en build time y tu modo oscuro no hace nada en silencio — ese es el gotcha.
3. Provider de next-themes. Envuelve la app, attribute="class", defaultTheme="system", enableSystem. Cambia .dark en `` y gestiona el script anti-flash.
4. Un toggle. Un botón que llama a setTheme('dark' | 'light' | 'system'). Listo.
1. @theme vs @theme inline. Cubierto arriba — es el bug número uno del modo oscuro en v4. Si tus colores no cambian, casi siempre es esto.
2. Flash del tema incorrecto (FOUC). next-themes inyecta un script bloqueante para poner la clase antes del paint. No renderices UI dependiente del tema antes del mount si fuera a descuadrar; usa suppressHydrationWarning en `` y controla las lecturas de tema client-only.
3. Consistencia OKLCH. Define ambos temas en OKLCH. Mezclar espacios de color entre claro y oscuro hace que transiciones y mezclas alfa se vean raras.
4. Tokens semánticos, no colores crudos. Referencia bg-background, no bg-white dark:bg-zinc-950. Los tokens semánticos son el objetivo entero — un sitio que cambiar, ambos temas siguen.
Un tema basado en tokens es también lo que deja a un asistente de IA reestilar tu app con seguridad: cambia una variable, cada componente sigue. Los dark: hardcodeados y esparcidos por los componentes son justo el tipo de inconsistencia que se acumula cuando la IA (o un compañero) edita después.
Tailwind v4 + next-themes convierte el modo oscuro en un problema de tokens, no en una tarea por componente. Define tokens en OKLCH, mapealos con @theme inline (no @theme a secas), y deja que next-themes cambie la clase. Todo son ~30 líneas de CSS más un provider — una vez bien, no vuelves a pensar en ello.
CREA.MBA trae este setup exacto: tokens OKLCH para claro y oscuro, el mapeo @theme inline bien hecho, next-themes cableado sin flash, y un toggle de tema puesto. El modo oscuro funciona de serie, y como todo son tokens semánticos, reestilar — tú o tu asistente de IA — es un cambio de un solo archivo.
Suscríbete para más tutoriales y tips sobre crear productos con IA
La integración real de Tailwind v4 + shadcn 2.3.0 en Next.js 16 App Router. OKLCH, dark mode sin FOUC, Geist con next/font y el bug silencioso de hsl(var()) que rompe tus estilos sin avisar.
El boilerplate ahora incluye el endpoint receptor para publicar posts desde un provider custom de Postiz. Calendario editorial unificado con tus redes sociales en menos de 2 horas.