Utilidades personalizadas
Crea utilidades personalizadas con @utility, variantes personalizadas con @variant y usa plugins en Tailwind CSS v4.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
/* globals.css */
@import "tailwindcss";
/* Utilidad personalizada — genera una sola clase */
@utility container-prose {
max-width: 65ch;
margin-inline: auto;
padding-inline: 1.5rem;
}
/* Utilidad personalizada con soporte responsive/hover (automático) */
@utility text-balance {
text-wrap: balance;
}
/* Variante personalizada */
@variant hocus (&:hover, &:focus-visible);
@variant scrolled (&:where([data-scrolled]));
/* Usar un plugin estilo v3 mediante @config */
@config "./tailwind.config.js";// Uso
<div className="container-prose">
<h1 className="text-balance hocus:text-blue-600">Utilidades personalizadas</h1>
</div>
<header data-scrolled className="scrolled:bg-white scrolled:shadow">Cuándo usarlo: Cuando las utilidades integradas de Tailwind no cubren un patrón específico que repites — crea una utilidad personalizada una vez y úsala en todas partes.
Ejemplo funcional
/* globals.css */
@import "tailwindcss";
/* Utilidades de layout */
@utility stack {
display: flex;
flex-direction: column;
}
@utility center {
display: flex;
align-items: center;
justify-content: center;
}
@utility container-narrow {
max-width: 42rem;
margin-inline: auto;
padding-inline: 1rem;
}
/* Utilidades visuales */
@utility glass {
background: rgb(255 255 255 / 0.8);
backdrop-filter: blur(12px);
border: 1px solid rgb(255 255 255 / 0.2);
}
@utility gradient-text {
background: linear-gradient(to right, var(--color-primary), var(--color-secondary, #8b5cf6));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
@utility scrollbar-hidden {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
/* Variantes interactivas */
@variant hocus (&:hover, &:focus-visible);
@variant group-hocus (:merge(.group):hover &, :merge(.group):focus-visible &);
@variant aria-current (&[aria-current="page"]);
@variant keyboard-focus (&:focus-visible);
/* Variantes de estado */
@variant loading (&[data-loading]);
@variant empty-state (&:where(:empty, [data-empty]));
@theme {
--color-primary: #2563eb;
}export function CustomUtilitiesDemo() {
return (
<div className="container-narrow stack gap-8 py-12">
<h1 className="gradient-text text-4xl font-bold">
Utilidades personalizadas en acción
</h1>
<nav className="glass sticky top-0 z-10 rounded-xl p-4">
<ul className="flex gap-4">
<li>
<a href="#" className="rounded px-3 py-1 hocus:bg-gray-100 aria-current:font-bold" aria-current="page">
Inicio
</a>
</li>
<li>
<a href="#" className="rounded px-3 py-1 hocus:bg-gray-100">Acerca de</a>
</li>
<li>
<a href="#" className="rounded px-3 py-1 hocus:bg-gray-100">Contacto</a>
</li>
</ul>
</nav>
<div className="scrollbar-hidden flex gap-4 overflow-x-auto pb-2">
{Array.from({ length: 10 }, (_, i) => (
<div key={i} className="shrink-0 center size-24 rounded-lg bg-gray-100 text-sm font-medium">
Tarjeta {i + 1}
</div>
))}
</div>
<button
data-loading
className="rounded bg-blue-600 px-4 py-2 text-white loading:opacity-50 loading:cursor-wait"
>
Enviar
</button>
</div>
);
}Qué demuestra esto:
@utilitypara clases de utilidad compuestas reutilizables@variantpara selectores de estado/pseudo personalizados- Efecto de glass morphism como utilidad
- Utilidad de texto con gradiente usando variables del tema
- Variantes personalizadas
aria-current,data-loading - Todas las utilidades personalizadas admiten modificadores responsive/hover automáticamente
Profundización
Cómo funciona
@utility name { ... }crea una clase de utilidad.nameen la capa de utilidades- Las utilidades personalizadas funcionan automáticamente con modificadores responsive (
md:name), de estado (hover:name) y otros @variant name (selector)crea un modificador que se aplica cuando el selector coincide- Varios selectores en una variante se separan con comas:
@variant hocus (&:hover, &:focus-visible) - El
&en los selectores de variantes representa el elemento al que se aplica la variante - Las utilidades personalizadas en v4 reemplazan la mayoría de los casos de uso de
@layer componentsen v3
Variaciones
Utilidad funcional (con valores) mediante @theme:
/* No puedes crear utilidades personalizadas de valor arbitrario con @utility.
En su lugar, define valores del tema y usa las utilidades integradas. */
@theme {
--spacing-page: 2rem;
--spacing-section: 4rem;
--width-content: 65ch;
--width-wide: 90rem;
}
/* Ahora usa: p-page, gap-section, max-w-content, max-w-wide */Usar plugins v3:
// tailwind.config.js (solo necesario para plugins)
export default {
plugins: [
require("@tailwindcss/typography"),
require("@tailwindcss/forms"),
],
};/* globals.css */
@import "tailwindcss";
@config "./tailwind.config.js";
/* Ahora las clases prose y form-* están disponibles */Combinar con anidamiento CSS:
@utility card {
border-radius: 0.75rem;
border: 1px solid var(--color-border);
padding: 1.5rem;
background: var(--color-surface);
& > h2 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
& > p {
color: var(--color-muted-foreground);
}
}Selectores de variantes complejos:
/* Estado abierto (para details/dialog) */
@variant open (&[open], &[data-state="open"]);
/* Variante de estado del padre */
@variant sidebar-open (:merge(.sidebar-open) &);
/* La variante print está integrada, pero media personalizada: */
@variant portrait (@media (orientation: portrait));
@variant touch (@media (pointer: coarse));Notas de TypeScript
// Las utilidades personalizadas son CSS — sin impacto en TS
// Pero puedes crear un mapa de utilidades para documentación:
const customUtilities = {
"container-prose": "max-width: 65ch centrado con padding inline",
"glass": "fondo de vidrio esmerilado con desenfoque de backdrop",
"gradient-text": "relleno con gradiente en el texto",
"scrollbar-hidden": "oculta la barra de desplazamiento en todos los navegadores",
} as const;
type CustomUtility = keyof typeof customUtilities;Errores comunes
-
Los nombres de
@utilitydeben ser identificadores únicos — Sin puntos, dos puntos ni espacios.@utility my-cardfunciona;@utility my.cardno. -
Las utilidades personalizadas no pueden recibir argumentos —
@utility spacing($value)no existe. Solución: Define valores del tema y usa las utilidades integradas (p-page,gap-section). -
Compatibilidad de plugins — No todos los plugins v3 funcionan con v4 todavía. Solución: Consulta la documentación del plugin para soporte v4, o replica el CSS del plugin con
@utility. -
@configsolo para plugins — Usar@configcon una configuración v3 completa puede entrar en conflicto con@theme. Solución: Pon solo el registro de plugins en la configuración JS; mueve todos los valores del tema a@theme. -
Especificidad de variantes —
@variantusa el selector que proporcionas. Si tu selector tiene alta especificidad, puede sobrescribir otros estilos de forma inesperada. Solución: Usa:where()para anular la especificidad cuando sea necesario.
Alternativas
| Alternativa | Úsala cuando | No la uses cuando |
|---|---|---|
@apply | Quieres componer utilidades Tailwind existentes en una clase | Puedes usar @utility directamente (más limpio) |
| Abstracción de componente | El patrón incluye estructura HTML, no solo estilos | Solo necesitas una clase CSS |
| Plugins de Tailwind (JS) | Necesitas generación dinámica de utilidades con valores | Una @utility estática cubre tu necesidad |
| Plugins PostCSS | Necesitas transformaciones más allá del alcance de Tailwind | Las funciones integradas de Tailwind son suficientes |
FAQs
¿Qué hace @utility y en qué se diferencia de @apply?
@utility name { ... }crea una nueva clase de utilidad en la capa de utilidades@applycompone utilidades Tailwind existentes en una clase@utilityes más limpio y está diseñado específicamente para utilidades personalizadas en v4
¿Las utilidades personalizadas funcionan automáticamente con modificadores responsive y de estado?
Sí. Una utilidad definida con @utility funciona automáticamente con md:, hover:, dark: y todos los demás modificadores.
¿Cómo creas una variante personalizada en v4?
@variant hocus (&:hover, &:focus-visible);Uso: <a className="hocus:text-blue-600">Enlace</a>
¿Las utilidades personalizadas pueden aceptar argumentos como spacing(2rem)?
No. @utility no puede recibir argumentos. En su lugar, define valores del tema y usa las utilidades integradas:
@theme {
--spacing-page: 2rem;
}
/* Ahora usa p-page, gap-page, etc. */¿Cómo usas plugins v3 (como @tailwindcss/typography) en v4?
@import "tailwindcss";
@config "./tailwind.config.js";Pon solo el registro de plugins en la configuración JS. Mantén todos los valores del tema en @theme.
¿Puedes usar anidamiento CSS dentro de un bloque @utility?
@utility card {
border-radius: 0.75rem;
padding: 1.5rem;
& > h2 {
font-size: 1.25rem;
font-weight: 600;
}
}Sí, el anidamiento CSS nativo está soportado dentro de @utility.
¿Qué reglas de nomenclatura aplican a los nombres de @utility?
Los nombres deben ser identificadores únicos. Se permiten guiones (my-card), pero no puntos, dos puntos ni espacios (my.card fallará).
¿Cómo creas una variante de atributo data?
@variant loading (&[data-loading]);Uso: <button data-loading className="loading:opacity-50">
Trampa: Tu variante personalizada sobrescribe otros estilos de forma inesperada. ¿Qué pasó?
El selector de la variante tiene alta especificidad. Solución: usa :where() para anular la especificidad: @variant loading (&:where([data-loading])).
Trampa: Usaste @config con una configuración v3 completa junto a @theme. ¿Qué puede salir mal?
Los valores del tema de la configuración JS pueden entrar en conflicto con los valores de @theme en CSS. Usa @config solo para el registro de plugins; mueve todos los valores del tema a @theme.
¿Cómo documentarías utilidades personalizadas con TypeScript para que el equipo las conozca?
const customUtilities = {
"container-prose": "max-width: 65ch centrado con padding inline",
"glass": "fondo de vidrio esmerilado con desenfoque de backdrop",
} as const;
type CustomUtility = keyof typeof customUtilities;Relacionado
- Setup — configuración de @theme y @import
- Utilities — referencia de utilidades integradas
- Animations — utilidades de animación personalizadas
- Dark Mode — utilidades personalizadas conscientes del tema