En esta página
Personalización avanzada en Tailwind v4
Personalización avanzada en Tailwind v4
Tailwind v4 ofrece un sistema de personalización profundo que va mucho más allá de cambiar colores. Con @theme, @source, @custom-variant, @utility y el sistema de plugins, puedes adaptar completamente el framework a las necesidades de cualquier proyecto sin sacrificar la coherencia ni el rendimiento.
@theme: el sistema de tokens de diseño
@theme es el reemplazo completo de tailwind.config.js. Todo lo que antes ibas en theme.extend, ahora va en @theme como variables CSS.
Anatomía de una variable en @theme
@theme {
/* Formato: --[categoría]-[nombre]: valor; */
--color-brand: #ff530f; /* Genera: text-brand, bg-brand, etc. */
--font-sans: 'Inter', sans-serif; /* Genera: font-sans */
--spacing-18: 4.5rem; /* Genera: p-18, m-18, gap-18, etc. */
--breakpoint-xs: 480px; /* Genera: xs:hidden, xs:flex, etc. */
--radius-card: 0.875rem; /* Genera: rounded-card */
--shadow-elevated: 0 8px 32px rgba(0,0,0,0.16); /* Genera: shadow-elevated */
--animate-fade-in: fade-in 0.3s ease-out both; /* Genera: animate-fade-in */
}Sobreescribir tokens existentes
@theme {
/* Sobreescribir el color "blue" de Tailwind con el azul de tu marca */
--color-blue-500: #3b5ff0;
--color-blue-600: #2d4fde;
/* Sobreescribir el breakpoint sm */
--breakpoint-sm: 560px;
/* Sobreescribir el font-sans por defecto */
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
}Eliminar tokens por defecto
@theme {
/* Eliminar todos los colores por defecto y empezar desde cero */
--color-*: initial;
/* Definir solo los colores de tu marca */
--color-brand-500: #ff530f;
--color-neutral-100: #f5f5f5;
--color-neutral-900: #111111;
}Tokens de escala completos
Para la paleta de colores de marca, define los tokens con todos los matices:
@theme {
--color-brand-50: oklch(97% 0.03 30);
--color-brand-100: oklch(94% 0.06 30);
--color-brand-200: oklch(88% 0.12 30);
--color-brand-300: oklch(80% 0.18 30);
--color-brand-400: oklch(70% 0.22 30);
--color-brand-500: oklch(60% 0.25 30); /* Color principal */
--color-brand-600: oklch(52% 0.23 30);
--color-brand-700: oklch(44% 0.20 30);
--color-brand-800: oklch(36% 0.16 30);
--color-brand-900: oklch(28% 0.12 30);
--color-brand-950: oklch(18% 0.08 30);
}Tailwind v4 soporta colores en formato oklch, hsl, rgb, y hex. oklch es recomendable para paletas generadas algorítmicamente ya que es perceptualmente uniforme.
@source: control de detección de contenido
Detección automática
En v4, Tailwind detecta automáticamente los archivos en el directorio del proyecto. No necesitas configuración manual para la mayoría de casos:
/* Esto es suficiente para un proyecto Angular/React/Vue estándar */
@import "tailwindcss";Agregar fuentes adicionales
@import "tailwindcss";
/* Escanear una librería de componentes de npm */
@source "../node_modules/@mi-empresa/ui/dist/**/*.{js,mjs}";
/* Escanear plantillas PHP en Laravel */
@source "../resources/views/**/*.blade.php";
/* Escanear archivos Python en Django */
@source "../templates/**/*.html";
/* Escanear archivos de datos con clases generadas */
@source "./content/**/*.md";Control total de las fuentes escaneadas
/* Deshabilitar la detección automática */
@import "tailwindcss" source(none);
/* Definir exactamente qué escanear */
@source "./src/**/*.{html,ts,tsx,jsx,vue,svelte}";
@source "./public/**/*.html";Safelist de clases en línea
Si generas clases dinámicamente y Tailwind no las puede detectar:
/* Forzar inclusión de clases específicas */
@source inline("bg-red-500 bg-green-500 bg-blue-500 bg-amber-500");
@source inline("{bg,text,border}-{brand,accent}-{50,100,500,600,900}");@custom-variant: variantes personalizadas
Las variantes personalizadas son una de las adiciones más poderosas de v4:
Variante de dark mode con clase
@import "tailwindcss";
/* Dark mode basado en clase en <html> */
@custom-variant dark (&:is(.dark *));
/* Ahora puedes usar dark: igual que siempre */
/* <div class="bg-white dark:bg-gray-900"> */Variantes basadas en data attributes
/* Variante para componentes en estado "loading" */
@custom-variant loading (&:is([data-loading] *));
/* Variante para modo compacto */
@custom-variant compact (&:is([data-density="compact"] *));
/* Variante para tema de alto contraste */
@custom-variant hc (&:is([data-theme="high-contrast"] *));Uso:
<!-- La aplicación en estado loading -->
<div data-loading class="opacity-100 loading:opacity-50 loading:pointer-events-none">
Contenido que se desactiva durante la carga
</div>
<!-- Modo compacto activado en el layout -->
<div data-density="compact">
<nav class="h-16 compact:h-10 transition-all">Navbar</nav>
<li class="py-3 compact:py-1.5 transition-all">Item de lista</li>
</div>Variantes de elementos padre
/* Variante cuando un ancestro específico tiene un estado */
@custom-variant sidebar-open (&:is(.sidebar-open *));
/* Variante cuando el documento tiene un modal abierto */
@custom-variant modal-open (&:is(body.modal-open *));@utility: utilidades personalizadas
@utility es la forma correcta de agregar utilidades CSS personalizadas en v4:
Utilidades simples
@utility scrollbar-hide {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
@utility text-gradient {
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
@utility pointer-events-all {
pointer-events: all;
}Utilidades con valores
/* Genera glass, glass-sm, glass-lg */
@utility glass-* {
backdrop-filter: blur(--value(--blur-*));
background-color: rgb(255 255 255 / 0.08);
border: 1px solid rgb(255 255 255 / 0.15);
}Glassmorphism con @utility
@utility glass {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
background: rgb(255 255 255 / 0.08);
border: 1px solid rgb(255 255 255 / 0.12);
box-shadow: 0 8px 32px rgb(0 0 0 / 0.12);
}
@utility glass-dark {
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
background: rgb(0 0 0 / 0.3);
border: 1px solid rgb(255 255 255 / 0.08);
}Uso:
<!-- Componente glassmorphism -->
<div class="glass rounded-2xl p-6 text-white hover:bg-white/10 transition-colors">
Tarjeta con efecto vidrio
</div>
<!-- Con variantes de Tailwind (funciona porque se registró con @utility) -->
<div class="glass dark:glass-dark sm:rounded-3xl transition-all">
Glass responsivo con dark mode
</div>Plugins en Tailwind v4
Los plugins en v4 se agregan directamente en el CSS con @plugin:
@import "tailwindcss";
/* Plugins oficiales */
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
@plugin "@tailwindcss/aspect-ratio";
/* Plugin personalizado (archivo local) */
@plugin "./plugins/my-plugin.js";Crear un plugin
// plugins/glass.js
import plugin from 'tailwindcss/plugin';
export default plugin(function({ addUtilities, theme }) {
addUtilities({
'.glass': {
'backdrop-filter': 'blur(12px)',
'-webkit-backdrop-filter': 'blur(12px)',
'background-color': 'rgba(255, 255, 255, 0.08)',
'border': '1px solid rgba(255, 255, 255, 0.15)',
},
'.glass-heavy': {
'backdrop-filter': 'blur(24px)',
'-webkit-backdrop-filter': 'blur(24px)',
'background-color': 'rgba(255, 255, 255, 0.15)',
'border': '1px solid rgba(255, 255, 255, 0.25)',
},
});
});Optimización de rendimiento y bundle size
Cómo funciona el purge en v4
A diferencia de v3, no hay una fase separada de "purge". Tailwind v4 solo genera CSS para las clases que encuentra al escanear los archivos. Esto funciona automáticamente sin configuración.
Evitar clases dinámicas no detectables
// ❌ MAL: Tailwind no puede detectar estas clases en tiempo de compilación
const color = isFeatured ? 'blue' : 'gray';
return `<div class="bg-${color}-500">...</div>`;
// ✅ BIEN: Usa clases completas que Tailwind puede detectar
const colorClass = isFeatured ? 'bg-blue-500' : 'bg-gray-500';
return `<div class="${colorClass}">...</div>`;
// ✅ ALTERNATIVA: Safelist en @source inline
// @source inline("bg-blue-500 bg-gray-500");Separar CSS crítico del no crítico
/* critical.css — inline en <head> */
@import "tailwindcss/base";
/* main.css — deferido */
@import "tailwindcss";
@import "tailwindcss/components";
@import "tailwindcss/utilities";Medir el tamaño del CSS generado
# Verificar el tamaño del CSS en producción
npm run build -- --report
# O usar una herramienta de análisis
npx bundle-analyzer dist/stats.jsonCSS nativo con container queries
Tailwind v4 soporta container queries nativamente:
<!-- Definir un container -->
<div class="@container rounded-xl p-6">
<!-- Los hijos pueden usar variantes @sm:, @md:, @lg: -->
<div class="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3 gap-4">
<!-- Se reorganiza según el tamaño del CONTENEDOR, no la pantalla -->
</div>
</div>
<!-- Container con nombre -->
<div class="@container/sidebar w-64">
<div class="@sm/sidebar:hidden">Oculto cuando sidebar es estrecha</div>
</div>@starting-style: animaciones de entrada
/* Anima elementos que acaban de aparecer en el DOM */
@layer utilities {
.animate-enter {
animation: enter 0.3s ease-out;
}
}
@keyframes enter {
@starting-style {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}<div class="animate-enter rounded-xl bg-white p-6 shadow-lg">
Aparece con animación desde el inicio
</div>Resumen del sistema de personalización v4
| Directiva | Propósito |
|---|---|
@theme |
Definir tokens de diseño (colores, fuentes, espaciado, etc.) |
@source |
Controlar qué archivos escanea Tailwind |
@custom-variant |
Crear variantes propias (loading:, compact:) |
@utility |
Agregar utilidades CSS custom que funcionan con variantes |
@plugin |
Activar plugins (oficiales o propios) |
@layer |
Organizar CSS en capas (base, components, utilities) |
El poder de v4 está en que todas estas directivas viven en tu CSS, no en un archivo de configuración JavaScript. Esto significa que tu editor tiene autocompletado nativo, el sistema de build es más simple, y la configuración es parte del código que versiones en git.
/* styles.css — @theme exhaustivo de un proyecto real */
@import "tailwindcss";
/* Plugin de tipografía */
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
/* Detección de contenido adicional */
@source "../node_modules/@mi-empresa/ui/dist/**/*.js";
@theme {
/* === COLORES === */
--color-brand-50: #fff3ee;
--color-brand-500: #ff530f;
--color-brand-600: #e63d00;
--color-brand-900: #7a1f00;
--color-accent: #e6286a;
/* Colores semánticos */
--color-surface: #0a0a0f;
--color-surface-alt: #14141a;
--color-on-surface: #f9fafb;
/* === TIPOGRAFÍA === */
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
--font-heading: 'Playfair Display', Georgia, serif;
--font-mono: 'JetBrains Mono', monospace;
/* === ESPACIADO EXTRA === */
--spacing-18: 4.5rem;
--spacing-22: 5.5rem;
--spacing-128: 32rem;
/* === BREAKPOINTS CUSTOM === */
--breakpoint-xs: 480px;
--breakpoint-3xl: 1920px;
/* === BORDER RADIUS === */
--radius-card: 0.875rem;
--radius-button: 0.625rem;
/* === ANIMACIONES === */
--animate-fade-in: fade-in 0.3s ease-out both;
--animate-scale-in: scale-in 0.2s ease-out both;
}
Inicia sesión para guardar tu progreso