Tailwind CSS v4 reescribe por completo el modelo de configuración. Ya no existe el archivo tailwind.config.js, la personalización del theme pasa al CSS mediante la directiva @theme y el setup de PostCSS se vuelve muchísimo más sencillo. Para quienes trabajan con React, esto habilita un patrón que antes no resultaba práctico: combinar sin fricciones las clases utilitarias de Tailwind con propiedades personalizadas de CSS para hacer theming en tiempo de ejecución. Aquí tienes todo lo que necesitas saber para construir componentes con la v4.
Qué cambió de la v3 a la v4
Los cambios principales afectan a cómo configuras Tailwind y a cómo funciona tu pipeline de build:
| Área | v3 | v4 |
|------|----|----|
| Archivo de configuración | tailwind.config.js | Sin config JS, solo CSS |
| Personalización del theme | theme.extend en JS | Directiva @theme en CSS |
| Plugin de PostCSS | tailwindcss | @tailwindcss/postcss |
| Detección de contenido | array content: [...] | Automática (escanea el proyecto) |
| Importación CSS | @tailwind base/components/utilities | @import "tailwindcss" |
| Propiedades personalizadas | Manual con la función theme() | De primera clase mediante @theme |
| Valores arbitrarios | bg-[#ff0000] | Sigue funcionando, sin cambios |
La detección de contenido ahora es automática: la v4 escanea los archivos de tu proyecto sin que tengas que listar globs. Esto por sí solo elimina una fuente habitual de bugs del tipo "mi clase no se aplica".
Setup de PostCSS
Instala un único paquete:
npm install -D @tailwindcss/postcss
Tu postcss.config.mjs:
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
Y ya está. No hace falta autoprefixer: la v4 gestiona los prefijos de proveedor internamente.
En tu archivo CSS global (app/globals.css):
@import "tailwindcss";
Una sola línea reemplaza las tres directivas @tailwind de la v3.
La directiva @theme
Tus design tokens personalizados viven en un bloque @theme dentro del CSS. Estos tokens se convierten a la vez en clases utilitarias de Tailwind y en propiedades personalizadas de CSS:
/* app/globals.css */
@import "tailwindcss";
@theme {
--color-brand: #818cf8;
--color-brand-hover: #6366f1;
--color-surface: #111111;
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--radius-sm: 0.5rem;
--radius-md: 0.75rem;
--radius-lg: 1rem;
--radius-full: 9999px;
--spacing-section: 6rem;
}
Una vez definidos en @theme, puedes usar estos tokens de dos formas:
// As Tailwind classes
<div className="bg-brand text-surface rounded-lg p-section" />
// As CSS custom properties in inline styles
<div style={{ background: "var(--color-brand)" }} />
Ambas son válidas. La elección depende de si necesitas sobreescribir valores en tiempo de ejecución.
Propiedades personalizadas de CSS para soporte multi-theme
La verdadera potencia de la v4 está en el theming en tiempo de ejecución. Las propiedades personalizadas de CSS se pueden sobreescribir en cualquier nivel del árbol del DOM: cambia el atributo data-theme de un elemento padre y todos sus hijos se actualizan al instante, con cero JavaScript y sin tocar ningún nombre de clase:
/* Theme A, light */
[data-theme="light"] {
--color-background: #ffffff;
--color-foreground: #18181b;
--color-accent: #a3e635;
--color-accent-hover: #84cc16;
--color-border: #e4e4e7;
}
/* Theme B, dark */
[data-theme="dark"] {
--color-background: #09090b;
--color-foreground: #fafafa;
--color-accent: #818cf8;
--color-accent-hover: #6366f1;
--color-border: #27272a;
}
Cambia de theme en React:
"use client";
import { useState } from "react";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<"light" | "dark">("light");
return (
<div data-theme={theme}>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle theme
</button>
{children}
</div>
);
}
Cualquier componente que use var(--color-background) o var(--color-accent) responde de forma automática al cambio de theme.
Combinar clases de Tailwind con variables CSS, el patrón correcto
El error más habitual al migrar a la v4 es mezclar los colores estáticos de Tailwind con tokens basados en variables CSS dentro del mismo componente. Eso rompe el theming:
// Wrong, mixes static colors with themed tokens
<button className="bg-indigo-500 text-white rounded-full px-6 py-3">
Click me
</button>
// Correct, uses only themed tokens
<button
style={{
background: "var(--color-accent)",
color: "var(--color-background)",
borderRadius: "var(--radius-full)",
padding: "0.75rem 1.5rem",
border: "none",
cursor: "pointer",
}}
>
Click me
</button>
Para las utilidades de layout y espaciado que no necesitan ser temáticas, las clases de Tailwind funcionan perfectamente:
// Layout classes, not theme-sensitive, use Tailwind
<section className="flex flex-col items-center gap-8 py-24">
{/* Content with themed colors */}
<h2 style={{ color: "var(--color-foreground)", fontWeight: 700 }}>
Section Heading
</h2>
<p style={{ color: "var(--color-foreground-muted)" }}>
Description text
</p>
</section>
Este enfoque híbrido te da la DX de Tailwind para el layout y el dimensionado, además de un theming completo en tiempo de ejecución para las propiedades visuales.
Los valores arbitrarios siguen funcionando
La v4 mantiene la sintaxis de valores arbitrarios [value] de la v3:
<div className="w-[calc(100%-2rem)] mt-[3.75rem] grid-cols-[1fr_2fr_1fr]" />
También puedes usar variables CSS directamente dentro de valores arbitrarios:
<div className="bg-[var(--color-accent)] text-[var(--color-background)]" />
Esto resulta útil cuando necesitas los prefijos responsive de Tailwind junto a valores de variables CSS:
<div className="p-4 md:p-[var(--container-padding-x)]" />
Ejemplo de componente: un botón con theming
Aquí tienes un componente de botón completo y con theming usando los patrones de la v4:
// components/Button.tsx
interface ButtonProps {
children: React.ReactNode;
variant?: "primary" | "secondary" | "ghost";
size?: "sm" | "md" | "lg";
href?: string;
onClick?: () => void;
disabled?: boolean;
}
const sizeStyles = {
sm: { padding: "0.5rem 1rem", fontSize: "0.8125rem" },
md: { padding: "0.75rem 1.5rem", fontSize: "0.875rem" },
lg: { padding: "0.875rem 2rem", fontSize: "0.9375rem" },
};
const variantStyles = {
primary: {
background: "var(--color-accent)",
color: "var(--color-foreground)",
border: "none",
},
secondary: {
background: "transparent",
color: "var(--color-foreground)",
border: "1px solid var(--color-border)",
},
ghost: {
background: "transparent",
color: "var(--color-foreground-muted)",
border: "none",
},
};
export function Button({ children, variant = "primary", size = "md", href, onClick, disabled }: ButtonProps) {
const style = {
...sizeStyles[size],
...variantStyles[variant],
borderRadius: "var(--radius-full)",
fontWeight: 600,
cursor: disabled ? "not-allowed" : "pointer",
opacity: disabled ? 0.5 : 1,
transition: `all var(--duration-normal) var(--ease-out)`,
textDecoration: "none",
display: "inline-flex",
alignItems: "center",
gap: "0.5rem",
};
if (href) {
return <a href={href} style={style}>{children}</a>;
}
return (
<button onClick={onClick} disabled={disabled} style={style}>
{children}
</button>
);
}
Este botón funciona correctamente en los 7 themes de Incubator porque lee de variables CSS, no de valores de color hardcodeados.
Migración de la v3 a la v4
Los pasos principales al migrar un proyecto existente:
- Reemplaza el plugin de PostCSS:
tailwindcss→@tailwindcss/postcss, eliminaautoprefixer - Reemplaza las importaciones CSS: tres directivas
@tailwind→@import "tailwindcss" - Mueve tu theme personalizado a
@theme: copia los valores detailwind.config.js→@theme {}en CSS - Actualiza las utilidades obsoletas:
bg-opacity-*→bg-black/50,ring-offset-*→ eliminado - Borra
tailwind.config.js: o consérvalo de forma temporal si necesitas compatibilidad hacia atrás
La guía de migración a la v4 en tailwindcss.com cubre los casos límite, pero para la mayoría de proyectos la actualización lleva menos de una hora.
Mira la v4 en acción
Las 449 secciones del catálogo de Incubator están construidas con Tailwind CSS v4, usando el enfoque híbrido descrito arriba: utilidades de Tailwind para el layout, propiedades personalizadas de CSS para el theming. Explora el catálogo para ver patrones de la v4 aplicados a componentes reales en cada tipo de sección.
Relacionado en incubator
- Catálogo completo de componentes: explora las 449 secciones construidas con Tailwind v4.
- Secciones hero: componentes hero con theming mediante propiedades personalizadas de CSS.
- Secciones de features: secciones con mucho layout que combinan Tailwind y variables CSS.
- Exploraciones de modo oscuro: secciones que muestran el cambio de theme en tiempo de ejecución en acción.
- Incubator vs Tailwind UI: cómo gestionan el theming ambas bibliotecas.