Conceptos básicos
Usando utilidades para dar estilo a elementos en hover, focus y más.
Cada clase de utilidad en Tailwind puede aplicarse condicionalmente añadiendo una variante al principio del nombre de la clase que describe la condición a la que quieres apuntar.
Por ejemplo, para aplicar la clase bg-sky-700
en hover, usa la clase hover:bg-sky-700
:
Pasa el cursor sobre este botón para ver el cambio de color de fondo
<button class="bg-sky-500 hover:bg-sky-700 ...">Guardar cambios</button>
Cuando se escribe CSS de la manera tradicional, un solo nombre de clase haría cosas diferentes según el estado actual:
Tradicionalmente, el mismo nombre de clase aplica diferentes estilos en hover
.btn-primary { background-color: #0ea5e9;}.btn-primary:hover { background-color: #0369a1;}
En Tailwind, en lugar de añadir los estilos para un estado hover a una clase existente, añades otra clase al elemento que solo hace algo en hover:
En Tailwind, se utilizan clases separadas para el estado predeterminado y el estado hover
.bg-sky-500 { background-color: #0ea5e9;}.hover\:bg-sky-700:hover { background-color: #0369a1;}
¿Te das cuenta de cómo hover:bg-sky-700
solo define estilos para el estado :hover
? No hace nada por defecto, pero tan pronto como pasas el cursor sobre un elemento con esa clase, el color de fondo cambiará a sky-700
.
Esto es lo que queremos decir cuando decimos que una clase de utilidad puede aplicarse condicionalmente — usando variantes puedes controlar exactamente cómo se comporta tu diseño en diferentes estados, sin salir nunca de tu HTML.
Tailwind incluye variantes para casi todo lo que necesitarás, incluyendo:
:hover
, :focus
, :first-child
y :required
::before
, ::after
, ::placeholder
y ::selection
prefers-reduced-motion
[dir="rtl"]
y [open]
& > *
y & *
Estas variantes incluso se pueden apilar para apuntar a situaciones más específicas, por ejemplo, cambiando el color de fondo en modo oscuro, en el breakpoint mediano, en hover:
<button class="dark:md:hover:bg-fuchsia-600 ...">Guardar cambios</button>
En esta guía aprenderás sobre cada variante disponible en el framework, cómo usarlas con tus propias clases personalizadas e incluso cómo crear las tuyas propias.
Da estilo a los elementos en hover, focus y active usando las variantes hover
, focus
y active
:
Intenta interactuar con este botón para ver los estados hover, focus y active
<button class="bg-violet-500 hover:bg-violet-600 focus:outline-2 focus:outline-offset-2 focus:outline-violet-500 active:bg-violet-700 ..."> Guardar cambios</button>
Tailwind también incluye variantes para otros estados interactivos como :visited
, :focus-within
, :focus-visible
y más.
Consulta la referencia de pseudo-clases para obtener una lista completa de las variantes de pseudo-clases disponibles.
Da estilo a un elemento cuando es el primer hijo o el último hijo usando las variantes first
y last
:
Kristen Ramos
kristen.ramos@example.com
Floyd Miles
floyd.miles@example.com
Courtney Henry
courtney.henry@example.com
Ted Fox
ted.fox@example.com
<ul role="list"> {#each people as person} <!-- Elimina el padding superior/inferior cuando es el primer/último hijo --> <li class="flex py-4 first:pt-0 last:pb-0"> <img class="h-10 w-10 rounded-full" src={person.imageUrl} alt="" /> <div class="ml-3 overflow-hidden"> <p class="text-sm font-medium text-gray-900 dark:text-white">{person.name}</p> <p class="truncate text-sm text-gray-500 dark:text-gray-400">{person.email}</p> </div> </li> {/each}</ul>
También puedes dar estilo a un elemento cuando es un hijo par o impar usando las variantes odd
y even
:
Nombre | Título | |
---|---|---|
Jane Cooper | Regional Paradigm Technician | jane.cooper@example.com |
Cody Fisher | Product Directives Officer | cody.fisher@example.com |
Leonard Krasner | Senior Designer | leonard.krasner@example.com |
Emily Selman | VP, Hardware Engineering | emily.selman@example.com |
Anna Roberts | Chief Strategy Officer | anna.roberts@example.com |
<table> <!-- ... --> <tbody> {#each people as person} <!-- Usa diferentes colores de fondo para filas pares e impares --> <tr class="odd:bg-white even:bg-gray-50 dark:odd:bg-gray-900/50 dark:even:bg-gray-950"> <td>{person.name}</td> <td>{person.title}</td> <td>{person.email}</td> </tr> {/each} </tbody></table>
Usa las variantes nth-*
y nth-last-*
para dar estilo a los hijos según su posición en la lista:
<div class="nth-3:underline"> <!-- ... --></div><div class="nth-last-5:underline"> <!-- ... --></div><div class="nth-of-type-4:underline"> <!-- ... --></div><div class="nth-last-of-type-6:underline"> <!-- ... --></div>
Puedes pasarles cualquier número que quieras por defecto, y usar valores arbitrarios para expresiones más complejas como nth-[2n+1_of_li]
.
Tailwind también incluye variantes para otras pseudo-clases estructurales como :only-child
, :first-of-type
, :empty
y más.
Consulta la referencia de pseudo-clases para obtener una lista completa de las variantes de pseudo-clases disponibles.
Da estilo a los elementos de formulario en diferentes estados usando variantes como required
, invalid
y disabled
:
Intenta hacer que la dirección de correo electrónico sea válida para ver cómo cambian los estilos
<input type="text" value="tbone" disabled class="invalid:border-pink-500 invalid:text-pink-600 focus:border-sky-500 focus:outline focus:outline-sky-500 focus:invalid:border-pink-500 focus:invalid:outline-pink-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none dark:disabled:border-gray-700 dark:disabled:bg-gray-800/20 ..."/>
Usar variantes para este tipo de cosas puede reducir la cantidad de lógica condicional en tus plantillas, permitiéndote usar el mismo conjunto de clases independientemente del estado en que se encuentre una entrada y dejando que el navegador aplique los estilos correctos por ti.
Tailwind también incluye variantes para otros estados de formulario como :read-only
, :indeterminate
, :checked
y más.
Consulta la referencia de pseudo-clases para obtener una lista completa de las variantes de pseudo-clases disponibles.
Usa la variante has-*
para dar estilo a un elemento basado en el estado o contenido de sus descendientes:
<label class="has-checked:bg-indigo-50 has-checked:text-indigo-900 has-checked:ring-indigo-200 dark:has-checked:bg-indigo-950 dark:has-checked:text-indigo-200 dark:has-checked:ring-indigo-900 ..."> <svg fill="currentColor"> <!-- ... --> </svg> Google Pay <input type="radio" class="checked:border-indigo-500 ..." /></label>
Puedes usar has-*
con una pseudo-clase, como has-[:focus]
, para dar estilo a un elemento basado en el estado de sus descendientes. También puedes usar selectores de elementos, como has-[img]
o has-[a]
, para dar estilo a un elemento basado en el contenido de sus descendientes.
Si necesitas dar estilo a un elemento basado en los descendientes de un elemento padre, puedes marcar el padre con la clase group
y usar la variante group-has-*
para dar estilo al elemento objetivo:
Solo feliz de estar aquí.
Un diseñador multidisciplinario, trabajando en la intersección del arte y la tecnología.
alex-reed.com
Pushing pixels. Lanzando divs.
<div class="group ..."> <img src="..." /> <h4>Spencer Sharp</h4> <svg class="hidden group-has-[a]:block ..."><!-- ... --></svg> <p>Diseñador de Producto en <a href="...">planeteria.tech</a></p></div>
Si necesitas dar estilo a un elemento basado en los descendientes de un elemento hermano, puedes marcar el hermano con la clase peer
y usar la variante peer-has-*
para dar estilo al elemento objetivo:
<div> <label class="peer ..."> <input type="checkbox" name="todo[1]" checked /> Crear una lista de tareas </label> <svg class="peer-has-checked:hidden ..."><!-- ... --></svg></div>
Usa la variante not-
para dar estilo a un elemento cuando una condición no es verdadera.
Es particularmente poderosa cuando se combina con otras variantes de pseudo-clase, por ejemplo, combinando not-focus:
con hover:
para aplicar estilos hover solo cuando un elemento no tiene foco:
Intenta enfocar el botón y luego pasar el cursor sobre él
<button class="bg-indigo-600 hover:not-focus:bg-indigo-700"> <!-- ... --></button>
También puedes combinar la variante not-
con variantes de media query como forced-colors
o supports
para dar estilo a un elemento solo cuando algo sobre el entorno del usuario no es verdadero:
<div class="not-supports-[display:grid]:flex"> <!-- ... --></div>
Cuando necesites dar estilo a un elemento basado en el estado de algún elemento padre, marca el padre con la clase group
, y usa variantes group-*
como group-hover
para dar estilo al elemento objetivo:
Hover over the card to see both text elements change color
<a href="#" class="group ..."> <div> <svg class="stroke-sky-500 group-hover:stroke-white ..." fill="none" viewBox="0 0 24 24"> <!-- ... --> </svg> <h3 class="text-gray-900 group-hover:text-white ...">Nuevo proyecto</h3> </div> <p class="text-gray-500 dark:text-gray-400 ...">Crea un nuevo proyecto a partir de una variedad de plantillas iniciales.</p></a>
Este patrón funciona con cualquier variante de pseudo-clase, por ejemplo group-focus
, group-active
, o incluso group-odd
.
Cuando anidas grupos, puedes estilizar algo basado en el estado de un grupo específico de padres dándole a ese padre un nombre único de grupo usando una clase group/{name}
, e incluyendo ese nombre en variantes usando clases como group-hover/{name}
:
<ul role="list"> {#each people as person} <li class="group/item ..."> <!-- ... --> <a class="group/edit invisible group-hover/item:visible ..." href="tel:{person.phone}"> <span class="group-hover/edit:text-gray-700 ...">Llamar</span> <svg class="group-hover/edit:translate-x-0.5 group-hover/edit:text-gray-500 ..."><!-- ... --></svg> </a> </li> {/each}</ul>
Los grupos pueden nombrarse como quieras y no necesitan configurarse de ninguna manera — solo nómbralos directamente en tu markup y Tailwind generará automáticamente el CSS necesario.
Puedes crear variantes group-*
únicas al vuelo proporcionando tu propio selector como un valor arbitrario entre corchetes:
<div class="group is-published"> <div class="hidden group-[.is-published]:block"> Publicado </div></div>
Para mayor control, puedes usar el carácter &
para marcar dónde debe terminar .group
en el selector final relativo al selector que estás pasando:
<div class="group"> <div class="group-[:nth-of-type(3)_&]:block"> <!-- ... --> </div></div>
La variante in-*
funciona de manera similar a group
excepto que no necesitas agregar group
al elemento padre:
<div tabindex="0" class="group"> <div class="opacity-50 group-focus:opacity-100"><div tabindex="0"> <div class="opacity-50 in-focus:opacity-100"> <!-- ... --> </div></div>
La variante in-*
responde a cambios de estado en cualquier padre, así que si quieres un control más detallado, necesitarás usar group
en su lugar.
Cuando necesites estilizar un elemento basado en el estado de un elemento hermano, marca al hermano con la clase peer
, y usa variantes peer-*
como peer-invalid
para estilizar el elemento objetivo:
Intenta hacer que la dirección de email sea válida para ver desaparecer la advertencia
<form> <label class="block"> <span class="...">Email</span> <input type="email" class="peer ..." /> <p class="invisible peer-invalid:visible ...">Por favor, proporciona una dirección de correo electrónico válida.</p> </label></form>
Esto hace posible hacer todo tipo de trucos geniales, como etiquetas flotantes por ejemplo sin ningún JS.
Este patrón funciona con cada variante de pseudo-clase, por ejemplo peer-focus
, peer-required
y peer-disabled
.
Es importante notar que el marcador peer
solo puede usarse en hermanos anteriores debido a cómo funciona el combinador de hermanos subsiguientes en CSS:
No funcionará, solo los hermanos anteriores pueden marcarse como peers
<label> <span class="peer-invalid:text-red-500 ...">Email</span> <input type="email" class="peer ..." /></label>
Cuando uses múltiples peers, puedes estilizar algo basado en el estado de un peer específico dándole a ese peer un nombre único usando una clase peer/{name}
, e incluyendo ese nombre en variantes usando clases como peer-checked/{name}
:
<fieldset> <legend>Estado de publicación</legend> <input id="draft" class="peer/draft" type="radio" name="status" checked /> <label for="draft" class="peer-checked/draft:text-sky-500">Borrador</label> <input id="published" class="peer/published" type="radio" name="status" /> <label for="published" class="peer-checked/published:text-sky-500">Publicado</label> <div class="hidden peer-checked/draft:block">Los borradores solo son visibles para los administradores.</div> <div class="hidden peer-checked/published:block">Tu publicación será visible públicamente en tu sitio.</div></fieldset>
Los peers pueden nombrarse como quieras y no necesitan configurarse de ninguna manera — solo nómbralos directamente en tu markup y Tailwind generará automáticamente el CSS necesario.
Puedes crear variantes peer-*
únicas al vuelo proporcionando tu propio selector como un valor arbitrario entre corchetes:
<form> <label for="email">Email:</label> <input id="email" name="email" type="email" class="is-dirty peer" required /> <div class="peer-[.is-dirty]:peer-required:block hidden">Este campo es obligatorio.</div> <!-- ... --></form>
Para mayor control, puedes usar el carácter &
para marcar dónde debe terminar .peer
en el selector final relativo al selector que estás pasando:
<div> <input type="text" class="peer" /> <div class="hidden peer-[:nth-of-type(3)_&]:block"> <!-- ... --> </div></div>
Estiliza los pseudo-elementos ::before
y ::after
usando las variantes before
y after
:
<label> <span class="text-gray-700 after:ml-0.5 after:text-red-500 after:content-['*'] ...">Email</span> <input type="email" name="email" class="..." placeholder="tu@ejemplo.com" /></label>
Cuando uses estas variantes, Tailwind automáticamente agregará content: ''
por defecto, así que no tienes que especificarlo a menos que quieras un valor diferente:
Cuando pareces molesto todo el tiempo, la gente piensa que estás ocupado.
<blockquote class="text-center text-2xl font-semibold text-gray-900 italic dark:text-white"> Cuando pareces <span class="relative inline-block before:absolute before:-inset-1 before:block before:-skew-y-3 before:bg-pink-500"> <span class="relative text-white dark:text-gray-950">molesto</span> </span> todo el tiempo, la gente piensa que estás ocupado.</blockquote>
Vale la pena notar que realmente no necesitas pseudo-elementos ::before
y ::after
para la mayoría de las cosas en proyectos de Tailwind — usualmente es más simple usar un elemento HTML real.
Por ejemplo, aquí está el mismo diseño de arriba pero usando un <span>
en lugar del pseudo-elemento ::before
, lo cual es un poco más fácil de leer y de hecho es menos código:
<blockquote class="text-center text-2xl font-semibold text-gray-900 italic"> Cuando pareces <span class="relative"> <span class="absolute -inset-1 block -skew-y-3 bg-pink-500" aria-hidden="true"></span> <span class="relative text-white">molesto</span> </span> todo el tiempo, la gente piensa que estás ocupado.</blockquote>
Guarda before
y after
para situaciones donde sea importante que el contenido del pseudo-elemento no esté realmente en el DOM y no pueda ser seleccionado por el usuario.
Estiliza el texto placeholder de cualquier input o textarea usando la variante placeholder
:
<input class="placeholder:text-gray-500 placeholder:italic ..." placeholder="Buscar cualquier cosa..." type="text" name="search"/>
Estiliza el botón en inputs de archivo usando la variante file
:
<input type="file" class="file:mr-4 file:rounded-full file:border-0 file:bg-violet-50 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-violet-700 hover:file:bg-violet-100 dark:file:bg-violet-600 dark:file:text-violet-100 dark:hover:file:bg-violet-500 ..."/>
Estiliza los contadores o viñetas en listas usando la variante marker
:
<ul role="list" class="list-disc marker:text-sky-400 ..."> <li>5 tazas de champiñones Porcini picados</li> <li>1/2 taza de aceite de oliva</li> <li>3 libras de apio</li></ul>
Hemos diseñado la variante marker
para que sea heredable, así que aunque puedes usarla directamente en un elemento <li>
, también puedes usarla en un padre para evitar repetirte.
Estiliza la selección de texto activa usando la variante selection
:
Intenta seleccionar algo de este texto con tu mouse
Así que empecé a caminar hacia el agua. No les mentiré, chicos, estaba aterrorizado. Pero seguí adelante, y mientras pasaba las rompientes una extraña calma me invadió. No sé si fue intervención divina o la hermandad de todos los seres vivos pero te digo Jerry en ese momento, era un biólogo marino.
<div class="selection:bg-fuchsia-300 selection:text-fuchsia-900"> <p> Así que empecé a caminar hacia el agua. No les mentiré, chicos, estaba aterrorizado. Pero seguí adelante, y mientras pasaba las rompientes una extraña calma me invadió. No sé si fue intervención divina o la hermandad de todos los seres vivos pero te digo Jerry en ese momento, <em>era</em> un biólogo marino. </p></div>
Hemos diseñado la variante selection
para que sea heredable, así que puedes agregarla en cualquier lugar del árbol y se aplicará a todos los elementos descendientes.
Esto facilita establecer el color de selección selection para que coincida con tu marca en todo tu sitio:
<html> <head> <!-- ... --> </head> <body class="selection:bg-pink-300"> <!-- ... --> </body></html>
Estiliza la primera línea en un bloque de contenido usando la variante first-line
, y la primera letra usando la variante first-letter
:
Bueno, déjame decirte algo, chico gracioso. ¿Sabes ese pequeño sello, el que dice "Biblioteca Pública de Nueva York"? Bueno, eso puede no significar nada para ti, pero significa mucho para mí. Un montón entero.
Claro, adelante, ríete si quieres. Ya he visto a tu tipo antes: Llamativos, haciendo la escena, haciendo alarde de la convención. Sí, sé lo que estás pensando. ¿Por qué este tipo hace tanto escándalo por unos viejos libros de biblioteca? Bueno, déjame darte una pista, junior.
<div class="text-gray-700"> <p class="first-letter:float-left first-letter:mr-3 first-letter:text-7xl first-letter:font-bold first-letter:text-gray-900 first-line:tracking-widest first-line:uppercase" > Bueno, déjame decirte algo, chico gracioso. ¿Sabes ese pequeño sello, el que dice "Biblioteca Pública de Nueva York"? </p> <p class="mt-6">Bueno, eso puede no significar nada para ti, pero significa mucho para mí. Un montón entero.</p></div>
Estiliza el fondo de un elemento nativo <dialog>
usando la variante backdrop
:
<dialog class="backdrop:bg-gray-50"> <form method="dialog"> <!-- ... --> </form></dialog>
Si estás usando elementos nativos <dialog>
en tu proyecto, también podrías querer leer sobre estilizar estados abierto/cerrado usando la variante open
.
Para estilizar un elemento en un breakpoint específico, usa variantes responsive como md
y lg
.
Por ejemplo, esto renderizará una cuadrícula de 3 columnas en móvil, una cuadrícula de 4 columnas en pantallas de ancho medio, y una cuadrícula de 6 columnas en pantallas de ancho grande:
<div class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6"> <!-- ... --></div>
Para estilizar un elemento basado en el ancho de un elemento padre en lugar del viewport, usa variantes como @md
y @lg
:
<div class="@container"> <div class="flex flex-col @md:flex-row"> <!-- ... --> </div></div>
Consulta la documentación de Responsive design para una mirada en profundidad sobre cómo funcionan estas características.
La consulta de medios prefers-color-scheme
te dice si el usuario prefiere un tema claro u oscuro, y usualmente se configura a nivel del sistema operativo.
Usa utilidades sin variante para apuntar al modo claro, y usa la variante dark
para proporcionar anulaciones para el modo oscuro:
Modo claro
El Lápiz de Gravedad Cero se puede usar para escribir en cualquier orientación, incluso al revés. Incluso funciona en el espacio exterior.
Modo oscuro
El Lápiz de Gravedad Cero se puede usar para escribir en cualquier orientación, incluso al revés. Incluso funciona en el espacio exterior.
<div class="bg-white dark:bg-gray-900 ..."> <!-- ... --> <h3 class="text-gray-900 dark:text-white ...">Escribe al revés</h3> <p class="text-gray-500 dark:text-gray-400 ..."> El Lápiz de Gravedad Cero se puede usar para escribir en cualquier orientación, incluso al revés. Incluso funciona en el espacio exterior. </p></div>
Consulta la documentación de Dark Mode para una mirada en profundidad sobre cómo funciona esta característica.
La media query prefers-reduced-motion
te dice si el usuario ha solicitado que minimices el movimiento no esencial.
Usa la variante motion-reduce
para agregar estilos condicionalmente cuando el usuario ha solicitado movimiento reducido:
Intenta emular `prefers-reduced-motion: reduce` en tus herramientas de desarrollador para ocultar el spinner
<button type="button" class="bg-indigo-500 ..." disabled> <svg class="animate-spin motion-reduce:hidden ..." viewBox="0 0 24 24"><!-- ... --></svg> Procesando...</button>
Tailwind también incluye una variante motion-safe
que solo agrega estilos cuando el usuario no ha solicitado movimiento reducido. Esto puede ser útil cuando usar el ayudante motion-reduce
significaría tener que "deshacer" muchos estilos:
<!-- Usar `motion-reduce` puede significar muchos estilos de "deshacer" --><button class="transition hover:-translate-y-0.5 motion-reduce:transition-none motion-reduce:hover:translate-y-0 ..."> Guardar cambios</button><!-- Usar `motion-safe` es menos código en estas situaciones --><button class="motion-safe:transition motion-safe:hover:-translate-x-0.5 ...">Guardar cambios</button>
La media query prefers-contrast
te dice si el usuario ha solicitado más o menos contraste.
Usa la variante contrast-more
para agregar estilos condicionalmente cuando el usuario ha solicitado más contraste:
Intenta emular `prefers-contrast: more` en tus herramientas de desarrollador para ver los cambios
<label class="block"> <span class="block text-sm font-medium text-gray-700">Número de Seguro Social</span> <input class="border-gray-200 placeholder-gray-400 contrast-more:border-gray-400 contrast-more:placeholder-gray-500 ..." /> <p class="text-gray-600 opacity-10 contrast-more:opacity-100 ...">Necesitamos esto para robar tu identidad.</p></label>
Tailwind también incluye una variante contrast-less
que puedes usar para agregar estilos condicionalmente cuando el usuario ha solicitado menos contraste.
La media query forced-colors
indica si el usuario está usando un modo de colores forzados. Estos modos sobrescriben los colores de tu sitio con una paleta definida por el usuario para texto, fondos, enlaces y botones.
Usa la variante forced-colors
para agregar estilos condicionalmente cuando el usuario ha habilitado un modo de color forzado:
Intenta emular `forced-colors: active` en tus herramientas de desarrollador para ver los cambios
<label> <input type="radio" class="appearance-none forced-colors:appearance-auto" /> <p class="hidden forced-colors:block">Cyan</p> <div class="bg-cyan-200 forced-colors:hidden ..."></div> <div class="bg-cyan-500 forced-colors:hidden ..."></div></label>
Usa la variante not-forced-colors
para aplicar estilos basados en cuando el usuario no está usando un modo de colores forzados:
<div class="not-forced-colors:appearance-none ..."> <!-- ... --></div>
Tailwind también incluye utilidades forced color adjust para optar por entrar y salir de los colores forzados.
Usa la variante inverted-colors
para agregar estilos condicionalmente cuando el usuario ha habilitado un esquema de color invertido:
<div class="shadow-xl inverted-colors:shadow-none ..."> <!-- ... --></div>
La media query pointer
te dice si el usuario tiene un dispositivo señalador principal, como un mouse, y la precisión de ese dispositivo señalador.
Usa la variante pointer-fine
para apuntar a un dispositivo señalador preciso, como un mouse o trackpad, o la variante pointer-coarse
para apuntar a un dispositivo señalador menos preciso, como una pantalla táctil, lo que puede ser útil para proporcionar objetivos de clic más grandes en dispositivos táctiles:
Intenta emular un dispositivo táctil en tus herramientas de desarrollador para ver los cambios
<fieldset aria-label="Elige una opción de memoria"> <div class="flex items-center justify-between"> <div>RAM</div> <a href="#"> Ver especificaciones de rendimiento </a> </div> <div class="mt-4 grid grid-cols-6 gap-2 pointer-coarse:mt-6 pointer-coarse:grid-cols-3 pointer-coarse:gap-4"> <label class="p-2 pointer-coarse:p-4 ..."> <input type="radio" name="memory-option" value="4 GB" className="sr-only" /> <span>4 GB</span> </label> <!-- ... --> </div></fieldset>
Mientras que pointer
solo apunta al dispositivo señalador principal, any-pointer
se usa para apuntar a cualquiera de los dispositivos señaladores que puedan estar disponibles. Usa las variantes any-pointer-fine
y any-pointer-coarse
para proporcionar diferentes estilos si al menos un dispositivo señalador conectado cumple con los criterios.
Puedes usar pointer-none
y any-pointer-none
para apuntar a la ausencia de un dispositivo señalador.
Usa las variantes portrait
y landscape
para agregar estilos condicionalmente cuando el viewport está en una orientación específica:
<div> <div class="portrait:hidden"> <!-- ... --> </div> <div class="landscape:hidden"> <p>Esta experiencia está diseñada para ser vista en modo landscape. Por favor, rota tu dispositivo para ver el sitio.</p> </div></div>
Usa la variante noscript
para agregar estilos condicionalmente basados en si el usuario tiene scripting, como JavaScript, habilitado:
<div class="hidden noscript:block"> <p>Esta experiencia requiere JavaScript para funcionar. Por favor, habilita JavaScript en la configuración de tu navegador.</p></div>
Usa la variante print
para agregar estilos condicionalmente que solo se aplican cuando el documento se está imprimiendo:
<div> <article class="print:hidden"> <h1>Mi Receta Secreta de Pizza</h1> <p>Esta receta es secreta y no debe compartirse con nadie</p> <!-- ... --> </article> <div class="hidden print:block">¿En serio estás intentando imprimir esto? ¡Es secreto!</div></div>
Usa la variante supports-[...]
para aplicar estilos a las cosas basados en si una característica específica es compatible con el navegador del usuario:
<div class="flex supports-[display:grid]:grid ..."> <!-- ... --></div>
Bajo el capó, la variante supports-[...]
genera @supports rules
y toma cualquier cosa que usarías con @supports (...)
entre los corchetes, como un par propiedad/valor, e incluso expresiones usando and
y or
.
Para brevedad, si solo necesitas verificar si una propiedad es compatible (y no un valor específico), puedes especificar solo el nombre de la propiedad:
<div class="bg-black/75 supports-backdrop-filter:bg-black/25 supports-backdrop-filter:backdrop-blur ..."> <!-- ... --></div>
Usa la variante not-supports-[...]
para aplicar estilos a las cosas basados en si una característica específica no es compatible con el navegador del usuario:
<div class="not-supports-[display:grid]:flex"> <!-- ... --></div>
Puedes configurar atajos para reglas @supports
comunes que estés usando en tu proyecto creando una nueva variante en el namespace supports-*
:
@custom-variant supports-grid { @supports (display: grid) { @slot; }}
Luego puedes usar estas variantes personalizadas supports-*
en tu proyecto:
<div class="supports-grid:grid"> <!-- ... --></div>
Usa la variante starting
para establecer la apariencia de un elemento cuando se renderiza por primera vez en el DOM, o transiciona de display: none
a visible:
<div> <button popovertarget="my-popover">Buscar actualizaciones</button> <div popover id="my-popover" class="opacity-0 starting:open:opacity-0 ..."> <!-- ... --> </div></div>
Usa la variante aria-*
para aplicar estilos condicionalmente basados en atributos ARIA.
Por ejemplo, para aplicar la clase bg-sky-700
cuando el atributo aria-checked
está establecido en true
, usa la clase aria-checked:bg-sky-700
:
<div aria-checked="true" class="bg-gray-600 aria-checked:bg-sky-700"> <!-- ... --></div>
Por defecto, hemos incluido variantes para los atributos ARIA booleanos más comunes:
Variante | CSS |
---|---|
aria-busy | &[aria-busy="true"] |
aria-checked | &[aria-checked="true"] |
aria-disabled | &[aria-disabled="true"] |
aria-expanded | &[aria-expanded="true"] |
aria-hidden | &[aria-hidden="true"] |
aria-pressed | &[aria-pressed="true"] |
aria-readonly | &[aria-readonly="true"] |
aria-required | &[aria-required="true"] |
aria-selected | &[aria-selected="true"] |
Puedes personalizar qué variantes aria-*
están disponibles creando una nueva variante:
@custom-variant aria-asc (&[aria-sort="ascending"]);@custom-variant aria-desc (&[aria-sort="descending"]);
Si necesitas usar una variante aria
única que no tiene sentido incluir en tu proyecto, o para atributos ARIA más complejos que toman valores específicos, usa corchetes para generar una propiedad sobre la marcha usando cualquier valor arbitrario:
# Factura | Cliente | Monto |
---|---|---|
#100 | Pendant Publishing | $2,000.00 |
#101 | Kruger Industrial Smoothing | $545.00 |
#102 | J. Peterman | $10,000.25 |
<table> <thead> <tr> <th aria-sort="ascending" class="aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg-[url('/img/up-arrow.svg')]" > # Factura </th> <!-- ... --> </tr> </thead> <!-- ... --></table>
Las variantes de estado ARIA también pueden apuntar a elementos padre y hermanos usando las variantes group-aria-*
y peer-aria-*
:
<table> <thead> <tr> <th aria-sort="ascending" class="group"> # Factura <svg class="group-aria-[sort=ascending]:rotate-0 group-aria-[sort=descending]:rotate-180"><!-- ... --></svg> </th> <!-- ... --> </tr> </thead> <!-- ... --></table>
Usa la variante data-*
para aplicar estilos condicionalmente basados en atributos de datos.
Para verificar si existe un atributo de datos (y no un valor específico), puedes especificar solo el nombre del atributo:
<!-- Se aplicará --><div data-active class="border border-gray-300 data-active:border-purple-500"> <!-- ... --></div><!-- No se aplicará --><div class="border border-gray-300 data-active:border-purple-500"> <!-- ... --></div>
Si necesitas verificar un valor específico, puedes usar un valor arbitrario:
<!-- Se aplicará --><div data-size="large" class="data-[size=large]:p-8"> <!-- ... --></div><!-- No se aplicará --><div data-size="medium" class="data-[size=large]:p-8"> <!-- ... --></div>
Alternativamente, puedes configurar atajos para atributos de datos comunes que estés usando en tu proyecto creando una nueva variante en el namespace data-*
:
@import "tailwindcss";@custom-variant data-checked (&[data-ui~="checked"]);
Luego puedes usar estas variantes personalizadas data-*
en tu proyecto:
<div data-ui="checked active" class="data-checked:underline"> <!-- ... --></div>
Usá las variantes rtl
y ltr
para agregar estilos condicionalmente en modos de derecha a izquierda y de izquierda a derecha respectivamente cuando construyas diseños multidireccionales:
De izquierda a derecha
Tom Cook
Director of Operations
De derecha a izquierda
تامر كرم
الرئيس التنفيذي
<div class="group flex items-center"> <img class="h-12 w-12 shrink-0 rounded-full" src="..." alt="" /> <div class="ltr:ml-3 rtl:mr-3"> <p class="text-gray-700 group-hover:text-gray-900 ...">...</p> <p class="text-gray-500 group-hover:text-gray-700 ...">...</p> </div></div>
Recordá, estas variantes solo son útiles si estás construyendo un sitio que necesita soportar ambos diseños, de izquierda a derecha y de derecha a izquierda. Si estás construyendo un sitio que solo necesita soportar una única dirección, no necesitás estas variantes — simplemente aplicá los estilos que tengan sentido para tu contenido.
Usá la variante open
para agregar estilos condicionalmente cuando un elemento <details>
o <dialog>
está en estado abierto:
Probá alternar el despliegue para ver cómo cambian los estilos
La taza es redonda. El frasco es redondo. Deberían llamarlo Roundtine.
<details class="border border-transparent open:border-black/10 open:bg-gray-100 ..." open> <summary class="text-sm leading-6 font-semibold text-gray-900 select-none">¿Por qué lo llaman Ovaltine?</summary> <div class="mt-3 text-sm leading-6 text-gray-600"> <p>La taza es redonda. El frasco es redondo. Deberían llamarlo Roundtine.</p> </div></details>
Esta variante también apunta a la pseudo-clase :popover-open
para popovers:
<div> <button popovertarget="my-popover">Abrir Popover</button> <div popover id="my-popover" class="opacity-0 open:opacity-100 ..."> <!-- ... --> </div></div>
La variante inert
te permite estilizar elementos marcados con el atributo inert
:
<form> <legend>Preferencias de notificación</legend> <fieldset> <input type="radio" /> <label> Personalizado </label> <fieldset inert class="inert:opacity-50"> <!-- ... --> </fieldset> <input type="radio" /> <label> Todo </label> </fieldset></form>
Esto es útil para agregar señales visuales que dejan claro que secciones del contenido no son interactivas.
Aunque generalmente es preferible poner las clases de utilidad directamente en los elementos hijos, podés usar la variante *
en situaciones donde necesitás estilizar hijos directos sobre los que no tenés control:
<div> <h2>Categorías<h2> <ul class="*:rounded-full *:border *:border-sky-100 *:bg-sky-50 *:px-2 *:py-0.5 dark:text-sky-300 dark:*:border-sky-500/15 dark:*:bg-sky-500/10 ..."> <li>Ventas</li> <li>Marketing</li> <li>SEO</li> <!-- ... --> </ul></div>
Es importante notar que sobrescribir un estilo con una utilidad directamente en el propio hijo no funcionará debido a la especificidad del selector hijo generado:
No funcionará, los hijos no pueden sobrescribir su propio estilo.
<ul class="*:bg-sky-50 ..."> <li class="bg-red-50 ...">Ventas</li> <li>Marketing</li> <li>SEO</li> <!-- ... --></ul>
Al igual que *
, la variante **
se puede usar para estilizar los hijos de un elemento. La principal diferencia es que **
aplicará estilos a todos los descendientes, no solo a los hijos directos. Esto es especialmente útil cuando la combinás con otra variante para acotar lo que estás seleccionando:
<ul class="**:data-avatar:size-12 **:data-avatar:rounded-full ..."> {#each items as item} <li> <img src={item.src} data-avatar /> <p>{item.name}</p> </li> {/each}</ul>
Así como los valores arbitrarios te permiten usar valores personalizados con tus clases de utilidad, las variantes arbitrarias te permiten escribir variantes de selector personalizadas directamente en tu HTML.
Las variantes arbitrarias son solo cadenas de formato que representan el selector, envueltas en corchetes. Por ejemplo, esta variante arbitraria cambia el cursor a grabbing
cuando el elemento tiene la clase is-dragging
:
<ul role="list"> {#each items as item} <li class="[&.is-dragging]:cursor-grabbing">{item}</li> {/each}</ul>
Las variantes arbitrarias se pueden apilar con variantes incorporadas o entre sí, al igual que el resto de las variantes en Tailwind:
<ul role="list"> {#each items as item} <li class="[&.is-dragging]:active:cursor-grabbing">{item}</li> {/each}</ul>
Si necesitás espacios en tu selector, podés usar un guion bajo. Por ejemplo, esta variante arbitraria selecciona todos los elementos p
dentro del elemento donde agregaste la clase:
<div class="[&_p]:mt-4"> <p>Lorem ipsum...</p> <ul> <li> <p>Lorem ipsum...</p> </li> <!-- ... --> </ul></div>
También podés usar at-rules como @media
o @supports
en variantes arbitrarias:
<div class="flex [@supports(display:grid)]:grid"> <!-- ... --></div>
Con las variantes personalizadas de at-rule, el marcador de posición &
no es necesario, al igual que cuando se anida con un preprocesador.
Si te encontrás usando la misma variante arbitraria varias veces en tu proyecto, podría valer la pena crear una variante personalizada usando la directiva @custom-variant
:
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
Ahora podés usar la variante theme-midnight:<utility>
en tu HTML:
<html data-theme="midnight"> <button class="theme-midnight:bg-black ..."></button></html>
Aprendé más sobre cómo agregar variantes personalizadas en la documentación de agregar variantes personalizadas.
Una tabla de referencia rápida de cada variante incluida en Tailwind por defecto.
Variant | CSS |
---|---|
hover | @media (hover: hover) { &:hover } |
focus | &:focus |
focus-within | &:focus-within |
focus-visible | &:focus-visible |
active | &:active |
visited | &:visited |
target | &:target |
* | :is(& > *) |
** | :is(& *) |
has-[...] | &:has(...) |
group-[...] | &:is(:where(.group)... *) |
peer-[...] | &:is(:where(.peer)... ~ *) |
in-[...] | :where(...) & |
not-[...] | &:not(...) |
inert | &:is([inert], [inert] *) |
first | &:first-child |
last | &:last-child |
only | &:only-child |
odd | &:nth-child(odd) |
even | &:nth-child(even) |
first-of-type | &:first-of-type |
last-of-type | &:last-of-type |
only-of-type | &:only-of-type |
nth-[...] | &:nth-child(...) |
nth-last-[...] | &:nth-last-child(...) |
nth-of-type-[...] | &:nth-of-type(...) |
nth-last-of-type-[...] | &:nth-last-of-type(...) |
empty | &:empty |
disabled | &:disabled |
enabled | &:enabled |
checked | &:checked |
indeterminate | &:indeterminate |
default | &:default |
optional | &:optional |
required | &:required |
valid | &:valid |
invalid | &:invalid |
user-valid | &:user-valid |
user-invalid | &:user-invalid |
in-range | &:in-range |
out-of-range | &:out-of-range |
placeholder-shown | &:placeholder-shown |
details-content | &:details-content |
autofill | &:autofill |
read-only | &:read-only |
before | &::before |
after | &::after |
first-letter | &::first-letter |
first-line | &::first-line |
marker | &::marker, & *::marker |
selection | &::selection |
file | &::file-selector-button |
backdrop | &::backdrop |
placeholder | &::placeholder |
sm | @media (width >= 40rem) |
md | @media (width >= 48rem) |
lg | @media (width >= 64rem) |
xl | @media (width >= 80rem) |
2xl | @media (width >= 96rem) |
min-[...] | @media (width >= ...) |
max-sm | @media (width < 40rem) |
max-md | @media (width < 48rem) |
max-lg | @media (width < 64rem) |
max-xl | @media (width < 80rem) |
max-2xl | @media (width < 96rem) |
max-[...] | @media (width < ...) |
@3xs | @container (width >= 16rem) |
@2xs | @container (width >= 18rem) |
@xs | @container (width >= 20rem) |
@sm | @container (width >= 24rem) |
@md | @container (width >= 28rem) |
@lg | @container (width >= 32rem) |
@xl | @container (width >= 36rem) |
@2xl | @container (width >= 42rem) |
@3xl | @container (width >= 48rem) |
@4xl | @container (width >= 56rem) |
@5xl | @container (width >= 64rem) |
@6xl | @container (width >= 72rem) |
@7xl | @container (width >= 80rem) |
@min-[...] | @container (width >= ...) |
@max-3xs | @container (width < 16rem) |
@max-2xs | @container (width < 18rem) |
@max-xs | @container (width < 20rem) |
@max-sm | @container (width < 24rem) |
@max-md | @container (width < 28rem) |
@max-lg | @container (width < 32rem) |
@max-xl | @container (width < 36rem) |
@max-2xl | @container (width < 42rem) |
@max-3xl | @container (width < 48rem) |
@max-4xl | @container (width < 56rem) |
@max-5xl | @container (width < 64rem) |
@max-6xl | @container (width < 72rem) |
@max-7xl | @container (width < 80rem) |
@max-[...] | @container (width < ...) |
dark | @media (prefers-color-scheme: dark) |
motion-safe | @media (prefers-reduced-motion: no-preference) |
motion-reduce | @media (prefers-reduced-motion: reduce) |
contrast-more | @media (prefers-contrast: more) |
contrast-less | @media (prefers-contrast: less) |
forced-colors | @media (forced-colors: active) |
inverted-colors | @media (inverted-colors: inverted) |
pointer-fine | @media (pointer: fine) |
pointer-coarse | @media (pointer: coarse) |
pointer-none | @media (pointer: none) |
any-pointer-fine | @media (any-pointer: fine) |
any-pointer-coarse | @media (any-pointer: coarse) |
any-pointer-none | @media (any-pointer: none) |
portrait | @media (orientation: portrait) |
landscape | @media (orientation: landscape) |
noscript | @media (scripting: none) |
@media print | |
supports-[…] | @supports (…) |
aria-busy | &[aria-busy="true"] |
aria-checked | &[aria-checked="true"] |
aria-disabled | &[aria-disabled="true"] |
aria-expanded | &[aria-expanded="true"] |
aria-hidden | &[aria-hidden="true"] |
aria-pressed | &[aria-pressed="true"] |
aria-readonly | &[aria-readonly="true"] |
aria-required | &[aria-required="true"] |
aria-selected | &[aria-selected="true"] |
aria-[…] | &[aria-…] |
data-[…] | &[data-…] |
rtl | &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) |
ltr | &:where(:dir(ltr), [dir="ltr"], [dir="ltr"] *) |
open | &:is([open], :popover-open, :open) |
starting | @starting-style |
Esta es una lista completa de ejemplos para todas las variantes de pseudo-clases incluidas en Tailwind para complementar la documentación de pseudo-clases al inicio de esta guía.
Estiliza un elemento cuando el usuario pasa el cursor sobre él con el mouse usando la variante hover
:
<div class="bg-black hover:bg-white ..."> <!-- ... --></div>
Estiliza un elemento cuando tiene foco usando la variante focus
:
<input class="border-gray-300 focus:border-blue-400 ..." />
Estiliza un elemento cuando él o uno de sus descendientes tiene foco usando la variante focus-within
:
<div class="focus-within:shadow-lg ..."> <input type="text" /></div>
Estiliza un elemento cuando ha sido enfocado usando el teclado con la variante focus-visible
:
<button class="focus-visible:outline-2 ...">Enviar</button>
Estiliza un elemento cuando está siendo presionado usando la variante active
:
<button class="bg-blue-500 active:bg-blue-600 ...">Enviar</button>
Estiliza un enlace cuando ya ha sido visitado usando la variante visited
:
<a href="https://seinfeldquotes.com" class="text-blue-600 visited:text-purple-600 ..."> Inspiración </a>
Estiliza un elemento si su ID coincide con el fragmento de URL actual usando la variante target
:
<div id="about" class="target:shadow-lg ..."> <!-- ... --></div>
Estiliza un elemento si es el primer hijo usando la variante first
:
<ul> {#each people as person} <li class="py-4 first:pt-0 ..."> <!-- ... --> </li> {/each}</ul>
Estiliza un elemento si es el último hijo usando la variante last
:
<ul> {#each people as person} <li class="py-4 last:pb-0 ..."> <!-- ... --> </li> {/each}</ul>
Estiliza un elemento si es el único hijo usando la variante only
:
<ul> {#each people as person} <li class="py-4 only:py-0 ..."> <!-- ... --> </li> {/each}</ul>
Estiliza un elemento si es un hijo con número impar usando la variante odd
:
<table> {#each people as person} <tr class="bg-white odd:bg-gray-100 ..."> <!-- ... --> </tr> {/each}</table>
Estiliza un elemento si es un hijo con número par usando la variante even
:
<table> {#each people as person} <tr class="bg-white even:bg-gray-100 ..."> <!-- ... --> </tr> {/each}</table>
Estiliza un elemento si es el primer hijo de su tipo usando la variante first-of-type
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="ml-2 first-of-type:ml-6 ..."> <!-- ... --> </a> {/each}</nav>
Estiliza un elemento si es el último hijo de su tipo usando la variante last-of-type
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="mr-2 last-of-type:mr-6 ..."> <!-- ... --> </a> {/each} <button>Más</button></nav>
Estiliza un elemento si es el único hijo de su tipo usando la variante only-of-type
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="mx-2 only-of-type:mx-6 ..."> <!-- ... --> </a> {/each} <button>Más</button></nav>
Estiliza un elemento en una posición específica usando la variante nth
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="mx-2 nth-3:mx-6 nth-[3n+1]:mx-7 ..."> <!-- ... --> </a> {/each} <button>Más</button></nav>
Estiliza un elemento en una posición específica desde el final usando la variante nth-last
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="mx-2 nth-last-3:mx-6 nth-last-[3n+1]:mx-7 ..."> <!-- ... --> </a> {/each} <button>Más</button></nav>
Estiliza un elemento en una posición específica, del mismo tipo usando la variante nth-of-type
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="mx-2 nth-of-type-3:mx-6 nth-of-type-[3n+1]:mx-7 ..."> <!-- ... --> </a> {/each} <button>Más</button></nav>
Estiliza un elemento en una posición específica desde el final, del mismo tipo usando la variante nth-last-of-type
:
<nav> <img src="/logo.svg" alt="Vandelay Industries" /> {#each links as link} <a href="#" class="mx-2 nth-last-of-type-3:mx-6 nth-last-of-type-[3n+1]:mx-7 ..."> <!-- ... --> </a> {/each} <button>Más</button></nav>
Estiliza un elemento si no tiene contenido usando la variante empty
:
<ul> {#each people as person} <li class="empty:hidden ...">{person.hobby}</li> {/each}</ul>
Estiliza un input cuando está deshabilitado usando la variante disabled
:
<input class="disabled:opacity-75 ..." />
Estiliza un input cuando está habilitado usando la variante enabled
, muy útil cuando solo quieres aplicar otro estilo cuando un elemento no está deshabilitado:
<input class="enabled:hover:border-gray-400 disabled:opacity-75 ..." />
Estiliza un checkbox o botón de radio cuando está seleccionado usando la variante checked
:
<input type="checkbox" class="appearance-none checked:bg-blue-500 ..." />
Estiliza un checkbox o botón de radio en un estado indeterminado usando la variante indeterminate
:
<input type="checkbox" class="appearance-none indeterminate:bg-gray-300 ..." />
Estiliza una opción, checkbox o botón de radio que era el valor por defecto cuando la página cargó inicialmente usando la variante default
:
<input type="checkbox" class="default:outline-2 ..." />
Estiliza un input cuando es opcional usando la variante optional
:
<input class="border optional:border-red-500 ..." />
Estiliza un input cuando es requerido usando la variante required
:
<input required class="border required:border-red-500 ..." />
Estiliza un input cuando es válido usando la variante valid
:
<input required class="border valid:border-green-500 ..." />
Estiliza un input cuando es inválido usando la variante invalid
:
<input required class="border invalid:border-red-500 ..." />
Estiliza un input cuando es válido y el usuario ha interactuado con él, usando la variante user-valid
:
<input required class="border user-valid:border-green-500" />
Estiliza un input cuando es inválido y el usuario ha interactuado con él, usando la variante user-invalid
:
<input required class="border user-invalid:border-red-500" />
Estiliza un input cuando su valor está dentro de un límite de rango especificado usando la variante in-range
:
<input min="1" max="5" class="in-range:border-green-500 ..." />
Estiliza un input cuando su valor está fuera de un límite de rango especificado usando la variante out-of-range
:
<input min="1" max="5" class="out-of-range:border-red-500 ..." />
Estiliza un input cuando el placeholder está visible usando la variante placeholder-shown
:
<input class="placeholder-shown:border-gray-500 ..." placeholder="tú@ejemplo.com" />
Estiliza el contenido de un elemento <details>
usando la variante details-content
:
<details class="details-content:bg-gray-100 ..."> <summary>Detalles</summary> Esto es un secreto.</details>
Estiliza un input cuando ha sido autocompletado por el navegador usando la variante autofill
:
<input class="autofill:bg-yellow-200 ..." />
Estiliza un input cuando es de solo lectura usando la variante read-only
:
<input class="read-only:bg-gray-100 ..." />