Conceptos clave
Mejores prácticas para añadir tus propios estilos personalizados en proyectos Tailwind.
A menudo, el mayor desafío al trabajar con un framework es averiguar qué se supone que debes hacer cuando necesitas algo que el framework no maneja por ti.
Tailwind ha sido diseñado desde cero para ser extensible y personalizable, de modo que sin importar lo que estés construyendo, nunca sientas que estás luchando contra el framework.
Esta guía cubre temas como la personalización de tus tokens de diseño, cómo romper con esas limitaciones cuando sea necesario, añadir tu propio CSS personalizado y extender el framework con plugins.
Si deseas cambiar cosas como tu paleta de colores, la escala de espaciado, la escala de tipografía o los breakpoints (puntos de ruptura), añade tus personalizaciones usando la directiva @theme en tu CSS:
@theme { --font-display: "Satoshi", "sans-serif"; --breakpoint-3xl: 120rem; --color-avocado-100: oklch(0.99 0 0); --color-avocado-200: oklch(0.98 0.04 113.22); --color-avocado-300: oklch(0.94 0.11 115.03); --color-avocado-400: oklch(0.92 0.19 114.08); --color-avocado-500: oklch(0.84 0.18 117.33); --color-avocado-600: oklch(0.53 0.12 118.34); --ease-fluid: cubic-bezier(0.3, 0, 0, 1); --ease-snappy: cubic-bezier(0.2, 0, 0, 1); /* ... */}Obtén más información sobre cómo personalizar tu tema en la documentación de variables de tema.
Aunque normalmente puedes construir la mayor parte de un diseño bien elaborado utilizando un conjunto limitado de tokens de diseño, de vez en cuando necesitas salir de esas limitaciones para lograr que las cosas queden perfectas a nivel de píxel.
Cuando te encuentres realmente necesitando algo como top: 117px para colocar una imagen de fondo en el lugar correcto, utiliza la notación de corchetes de Tailwind para generar una clase al vuelo con cualquier valor arbitrario:
<div class="top-[117px]"> <!-- ... --></div>Esto es básicamente como los estilos en línea, con la gran ventaja de que puedes combinarlo con modificadores interactivos como hover y modificadores responsivos como lg:
<div class="top-[117px] lg:top-[344px]"> <!-- ... --></div>Esto funciona para todo en el framework, incluyendo cosas como colores de fondo, tamaños de fuente, contenido de pseudo-elementos y más:
<div class="bg-[#bada55] text-[22px] before:content-['Festivus']"> <!-- ... --></div>Si estás haciendo referencia a una variable CSS como un valor arbitrario, puedes usar la sintaxis de propiedad personalizada:
<div class="fill-(--my-brand-color) ..."> <!-- ... --></div>Esto es solo un atajo para fill-[var(--my-brand-color)] que añade la función var() por ti automáticamente.
Si alguna vez necesitas usar una propiedad CSS para la cual Tailwind no incluye una utilidad de fábrica, también puedes usar la notación de corchetes para escribir CSS completamente arbitrario:
<div class="[mask-type:luminance]"> <!-- ... --></div>Esto es realmente como los estilos en línea, pero nuevamente con el beneficio de que puedes usar modificadores:
<div class="[mask-type:luminance] hover:[mask-type:alpha]"> <!-- ... --></div>Esto también puede ser útil para cosas como variables CSS, especialmente cuando necesitan cambiar bajo diferentes condiciones:
<div class="[--scroll-offset:56px] lg:[--scroll-offset:44px]"> <!-- ... --></div>Las variantes arbitrarias son como los valores arbitrarios pero para realizar modificaciones de selectores al vuelo, de manera similar a como lo haces con variantes de pseudo-clases integradas como hover:{utility} o variantes responsivas como md:{utility}, pero usando la notación de corchetes directamente en tu HTML.
<ul role="list"> {#each items as item} <li class="lg:[&:nth-child(-n+3)]:hover:underline">{item}</li> {/each}</ul>Obtén más información en la documentación de variantes arbitrarias.
Cuando un valor arbitrario necesita contener un espacio, utiliza un guion bajo (_) en su lugar y Tailwind lo convertirá automáticamente en un espacio en tiempo de compilación:
<div class="grid grid-cols-[1fr_500px_2fr]"> <!-- ... --></div>En situaciones donde los guiones bajos son comunes pero los espacios no son válidos, Tailwind conservará el guion bajo en lugar de convertirlo en un espacio, por ejemplo en URLs:
<div class="bg-[url('/what_a_rush.png')]"> <!-- ... --></div>En el raro caso de que realmente necesites usar un guion bajo pero sea ambiguo porque un espacio también es válido, escapa el guion bajo con una barra diagonal inversa (\) y Tailwind no lo convertirá en un espacio:
<div class="before:content-['hello\_world']"> <!-- ... --></div>Si estás usando algo como JSX donde la barra diagonal inversa se elimina del HTML renderizado, utiliza String.raw() para que la barra diagonal inversa no sea tratada como un carácter de escape de JavaScript:
<div className={String.raw`before:content-['hello\_world']`}> <!-- ... --></div>Muchas utilidades en Tailwind comparten un espacio de nombres común pero se mapean a diferentes propiedades CSS. Por ejemplo, text-lg y text-black comparten el espacio de nombres text-, pero uno es para font-size y el otro es para color.
Al usar valores arbitrarios, Tailwind generalmente puede manejar esta ambigüedad automáticamente basándose en el valor que pases:
<!-- Generará una utilidad de tamaño de fuente --><div class="text-[22px]">...</div><!-- Generará una utilidad de color --><div class="text-[#bada55]">...</div>Sin embargo, a veces realmente es ambiguo, por ejemplo al usar variables CSS:
<div class="text-(--my-var)">...</div>En estas situaciones, puedes "dar una pista" del tipo subyacente a Tailwind añadiendo un tipo de datos CSS antes del valor:
<!-- Generará una utilidad de tamaño de fuente --><div class="text-(length:--my-var)">...</div><!-- Generará una utilidad de color --><div class="text-(color:--my-var)">...</div>Aunque Tailwind está diseñado para manejar la mayor parte de tus necesidades de diseño, nada te impide escribir CSS puro cuando lo necesites:
@import "tailwindcss";.my-custom-style { /* ... */}Si solo deseas establecer algunos valores predeterminados para la página (como el color del texto, el color de fondo o la familia de fuentes), la opción más sencilla es simplemente añadir algunas clases a los elementos html o body:
<!doctype html><html lang="es" class="bg-gray-100 font-serif text-gray-900"> <!-- ... --></html>Esto mantiene tus decisiones de estilos base en tu marcado junto con todos tus otros estilos, en lugar de ocultarlas en un archivo separado.
Si deseas añadir tus propios estilos base predeterminados para elementos HTML específicos, utiliza la directiva @layer para añadir esos estilos a la capa base de Tailwind:
@layer base { h1 { font-size: var(--text-2xl); } h2 { font-size: var(--text-xl); }}Utiliza la capa components para cualquier clase más complicada que desees añadir a tu proyecto y que aún así te gustaría poder anular con clases de utilidad.
Tradicionalmente estas serían clases como card, btn, badge — ese tipo de cosas.
@layer components { .card { background-color: var(--color-white); border-radius: var(--radius-lg); padding: --spacing(6); box-shadow: var(--shadow-xl); }}Al definir clases de componentes en la capa components, aún puedes usar clases de utilidad para anularlas cuando sea necesario:
<!-- Se verá como una tarjeta, pero con esquinas cuadradas --><div class="card rounded-none"> <!-- ... --></div>Usando Tailwind, probablemente no necesites este tipo de clases con tanta frecuencia como crees. Lee nuestra guía sobre gestión de duplicación para ver nuestras recomendaciones.
La capa components también es un buen lugar para colocar estilos personalizados para cualquier componente de terceros que estés utilizando:
@layer components { .select2-dropdown { /* ... */ }}Utiliza la directiva @variant para aplicar una variante de Tailwind dentro de CSS personalizado:
.my-element { background: white; @variant dark { background: black; }}.my-element { background: white; @media (prefers-color-scheme: dark) { background: black; }}Si necesitas aplicar múltiples variantes al mismo tiempo, apílalas utilizando la misma sintaxis que usarías en HTML:
.my-element { background: white; @variant hover:focus { background: black; }}.my-element { background: white; &:hover { @media (hover: hover) { &:focus { background: black; } } }}Para aplicar los mismos estilos a múltiples variantes, separa cada variante con una coma:
.my-element { background: white; @variant hover, focus { background: black; }}.my-element { background: white; &:hover { @media (hover: hover) { background: black; } } &:focus { background: black; }}Además de usar las utilidades que vienen con Tailwind, también puedes añadir tus propias utilidades personalizadas. Esto puede ser útil cuando hay una característica de CSS que te gustaría usar en tu proyecto para la cual Tailwind no incluye utilidades de fábrica.
Utiliza la directiva @utility para añadir una utilidad personalizada a tu proyecto:
@utility content-auto { content-visibility: auto;}Ahora puedes usar esta utilidad en tu HTML:
<div class="content-auto"> <!-- ... --></div>También funcionará con variantes como hover, focus y lg:
<div class="hover:content-auto"> <!-- ... --></div>Las utilidades personalizadas se insertan automáticamente en la capa utilities junto con todas las utilidades integradas del framework.
Si tu utilidad personalizada es más compleja que un solo nombre de clase, utiliza la anidación para definir la utilidad:
@utility scrollbar-hidden { &::-webkit-scrollbar { display: none; }}Además de registrar utilidades simples con la directiva @utility, también puedes registrar utilidades funcionales que acepten un argumento:
@utility tab-* { tab-size: --value(--tab-size-*);}La función especial --value() se utiliza para resolver el valor de la utilidad.
Utiliza la sintaxis --value(--theme-key-*) para resolver el valor de la utilidad frente a un conjunto de claves de tema:
@theme { --tab-size-2: 2; --tab-size-4: 4; --tab-size-github: 8;}@utility tab-* { tab-size: --value(--tab-size-*);}Esto coincidirá con utilidades como tab-2, tab-4 y tab-github.
Para resolver el valor como un valor directo, utiliza la sintaxis --value({type}), donde {type} es el tipo de datos como el cual deseas validar el valor directo:
@utility tab-* { tab-size: --value(integer);}Esto coincidirá con utilidades como tab-1 y tab-76.
Los tipos de datos de valores directos disponibles son: number, integer, ratio y percentage.
Para admitir valores literales, utiliza la sintaxis --value('literal') (observa las comillas):
@utility tab-* { tab-size: --value("inherit", "initial", "unset");}Esto coincidirá con utilidades como tab-inherit, tab-initial y tab-unset.
Para admitir valores arbitrarios, utiliza la sintaxis --value([{type}]) (observa los corchetes) para indicarle a Tailwind qué tipos se admiten como valor arbitrario:
@utility tab-* { tab-size: --value([integer]);}Esto coincidirá con utilidades como tab-[1] y tab-[76].
Los tipos de datos de valores arbitrarios disponibles son: absolute-size, angle, bg-size, color, family-name, generic-name, image, integer, length, line-width, number, percentage, position, ratio, relative-size, url, vector y *.
Las tres formas de la función --value() se pueden utilizar dentro de una regla como múltiples declaraciones, y cualquier declaración que no logre resolverse se omitirá en la salida:
@theme { --tab-size-github: 8;}@utility tab-* { tab-size: --value([integer]); tab-size: --value(integer); tab-size: --value(--tab-size-*);}Esto hace posible tratar el valor de manera diferente en cada caso si es necesario, por ejemplo, traduciendo un entero directo a un porcentaje:
@utility opacity-* { opacity: --value([percentage]); opacity: calc(--value(integer) * 1%); opacity: --value(--opacity-*);}La función --value() también puede tomar múltiples argumentos y resolverlos de izquierda a derecha si no necesitas tratar el valor de retorno de manera diferente en diferentes casos:
@theme { --tab-size-github: 8;}@utility tab-* { tab-size: --value(--tab-size-*, integer, [integer]);}@utility opacity-* { opacity: calc(--value(integer) * 1%); opacity: --value(--opacity-*, [percentage]);}Utiliza --default() dentro de --value() para admitir un valor predeterminado cuando la utilidad se use sin un valor explícito:
@utility tab-* { tab-size: --value(integer, --default(4));}Esto coincide tanto con tab-2 y tab-4, como con simplemente tab, que utiliza el valor predeterminado de 4:
.tab { tab-size: 4;}.tab-2 { tab-size: 2;}La función --default() también se puede utilizar dentro de --modifier() para proporcionar un valor predeterminado cuando no haya ningún modificador presente:
@utility tab-* { tab-size: --value(integer); line-height: --modifier(integer, --default(1));}Esto coincide con tab-2/3 con una altura de línea (line-height) de 3, y con tab-2 con una altura de línea de 1.
Para admitir valores negativos, registra utilidades positivas y negativas separadas en declaraciones distintas:
@utility inset-* { inset: --spacing(--value(integer)); inset: --value([percentage], [length]);}@utility -inset-* { inset: --spacing(--value(integer) * -1); inset: calc(--value([percentage], [length]) * -1);}Los modificadores se manejan mediante la función --modifier(), que funciona exactamente igual que la función --value() pero opera sobre un modificador si está presente:
@utility text-* { font-size: --value(--text-*, [length]); line-height: --modifier(--leading-*, [length], [*]);}Si no hay un modificador presente, cualquier declaración que dependa de un modificador simplemente no se incluirá en la salida.
Para manejar fracciones, nos apoyamos en el tipo de datos CSS ratio. Si este se usa con --value(), es una señal para Tailwind para tratar el valor y el modificador como un solo valor:
@utility aspect-* { aspect-ratio: --value(--aspect-ratio-*, ratio, [ratio]);}Esto coincidirá con utilidades como aspect-square, aspect-3/4 y aspect-[7/9].
Además de usar las variantes que vienen con Tailwind, también puedes añadir tus propias variantes personalizadas usando la directiva @custom-variant:
@custom-variant theme-midnight { &:where([data-theme="midnight"] *) { @slot; }}Ahora puedes usar la variante theme-midnight:<utility> en tu HTML:
<html data-theme="midnight"> <button class="theme-midnight:bg-black ..."></button></html>Puedes crear variantes usando la sintaxis abreviada cuando no se requiera anidación:
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));Cuando una variante personalizada tiene múltiples reglas, se pueden anidar unas dentro de otras:
@custom-variant any-hover { @media (any-hover: hover) { &:hover { @slot; } }}