Logo Tailwind Tricks

Cosas que igual no sabías de Tailwind

Novedad: 'has' ha sido aceptado por todos los navegadores principales (>90% compatibilidad)

La propiedad has ha sido aceptada por todos los navegadores principales, por lo que ya podemos utilizarla sin miedo. Esta propiedad nos permite estilar el parent dependiendo de lo que pase en uno de los children. Por ejemplo, podemos estilar el label de un input cuando el input está checked como veremos en uno de los ejemplos más abajo, pero hay más.


<div class="[&:has(h3)]:border [&:has(h3)]:p-4">
	<h3>Esto es un h3</h3>
	<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus quod dignissimos, cupiditate et doloremque quam ipsa similique! Aut aliquam odit suscipit vel quaerat magnam architecto necessitatibus ad nulla! Possimus, error?</p>
</div>
<div class="[&:has(h3)]:border">
	<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus quod dignissimos, cupiditate et doloremque quam ipsa similique! Aut aliquam odit suscipit vel quaerat magnam architecto necessitatibus ad nulla! Possimus, error?</p>
</div>
								

Esto nos dará algo como lo siguiente.

Esto es un h3

Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus quod dignissimos, cupiditate et doloremque quam ipsa similique! Aut aliquam odit suscipit vel quaerat magnam architecto necessitatibus ad nulla! Possimus, error?

Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloribus quod dignissimos, cupiditate et doloremque quam ipsa similique! Aut aliquam odit suscipit vel quaerat magnam architecto necessitatibus ad nulla! Possimus, error?


1. Propiedades 'peer:' y 'accent-color'

Aquí podríamos hablar de la propiedad peer con la que podemos hacer algo dependiendo del estado del elemento hermano (sibling). En este caso fíjate cómo añadimos la negrita al contenido del div si el input está checked. También, es interesante comentar lo que hace la propiedad accent-color para estilar el los input de type="radio" o type="checkbox" por ejemplo.

¿Conocías esto de Tailwind CSS?
Lista de la compra

		<fieldset class="accent-purple-600 my-6">
			<legend class="text-md font-semibold mb-2">¿Conocías esto de Tailwind CSS?</legend>
			<label class="flex gap-2">
				<input checked type="radio" name="option" id="yes" class="peer" />
				<div class="peer-checked:font-bold">Sí</div>
			</label>
			<label class="flex gap-2">
				<input type="radio" name="option" id="no" class="peer" />
				<div class="peer-checked:font-bold">No</div>
			</label>
			<label class="flex gap-2">
				<input type="radio" name="option" id="maybe" class="peer" />
				<div class="peer-checked:font-bold">Me sonaba...</div>
			</label>
		</fieldset>

		<fieldset class="accent-purple-600 my-4">
			<legend class="text-md font-semibold mb-2">Lista de la compra</legend>
			<div class="flex gap-2 items-center">
				<input type="checkbox" name="coke" id="coke" checked />
				<label for="coke">Coca Cola Zero</label>
			</div>
			<div class="flex gap-2 items-center">
				<input type="checkbox" name="bread" id="bread" checked />
				<label for="bread">Pan para el desayuno</label>
			</div>
			<div class="flex gap-2 items-center">
				<input type="checkbox" name="hummus" id="hummus" />
				<label for="hummus">Hummus</label>
			</div>
		</fieldset>
						

También podríamos usar peerpara hacer unas cuantas validaciones de imputs en el Frontend (pero recuerda que esto no debe sustituir sino complementar a la validación desde el servidor)


<div class="p-4 bg-black">
	<form class="shadow-lg rounded-md p-5 md:p-10 flex flex-col w-11/12 max-w-lg group gap-6" novalidate>

		<label for="email">
			<div class="text-white mb-5">Email</div>
			<input
			type="email"
			name="email"
			id="email"
			class="invalid:[&:not(:placeholder-shown):not(:focus)]:outline-red-500 invalid:[&:not(:placeholder-shown):not(:focus)]:outline peer w-full py-2 px-3 rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
			placeholder=" "
			required
			pattern="[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,}$"
			/>
			<span class="mt-2 hidden text-sm text-red-500 peer-[&:not(:placeholder-shown):not(:focus):invalid]:block">
				Please enter a valid email address
			</span>
		</label>
		<label for="password">
			<div class="text-white mb-5">Password</div>
			<input
			type="password"
			name="password"
			id="password"
			class="invalid:[&:not(:placeholder-shown):not(:focus)]:outline-red-500 invalid:[&:not(:placeholder-shown):not(:focus)]:outline peer w-full py-2 px-3 rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
			placeholder=" "
			required
			pattern=".{7,}"
			/>
			<span class="mt-2 hidden text-sm text-red-500 peer-[&:not(:placeholder-shown):not(:focus):invalid]:block">
				Please enter a valid password with a minimum of 7 characters
			</span>
		</label>

		<button type="submit" class="text-white group-invalid:pointer-events-none group-invalid:opacity-30 border border-white py-2 my-3">Submit</button>
	</form>
</div>
								

2. Propiedades 'group:' y 'has:'

Es cierto que por ahora podemos estilar el contenido de ciertos children al interactuar con el parent. A través de group podemos decir cuál va a ser el parent y luego hacer que algo suceda en hover o focus en alguno de los children, como es el caso del texto de la descripción de estas cards.

¿Qué camino quieres emprender?

Lo que va a ser genial y está todavía en fase experimental, supongo porque Firefox todavía no lo ha aceptado del todo a julio de 2023, es el uso de has, ya que vamos a poder hacer justo lo contrario: estilar al parent dependiendo de lo que pase en uno de los children. Aquí te lo dejo en el código, pero teóricamente una vez se aplique, la carta seleccionada podrá tener un color de fondo.


				
		<fieldset class="my-6 accent-lime-600">
			<legend class="text-md font-semibold mb-2">¿Qué camino quieres emprender?</legend>
			<label for="frontend" class="flex items-start gap-x-4 bg-[var(--color-bg)] border border-black p-6 [&:has(:checked)]:bg-indigo-200 group my-3">	
				<div>
					<input class="checked:ring-white" type="radio" name="path" id="frontend" checked />
				</div>
				<div>
					<div class="font-semibold has-[:checked]:font-bold">Frontend</div>
					<div class="group-hover:text-gray-500 group-focus-within:text-gray-500">El camino de aquellos amantes de una buena UI y experiencia de usuario.</div>
				</div>
			</label>
			<label for="backend" class="flex items-start gap-x-4 bg-[var(--color-bg)] border border-black p-6 [&:has(:checked)]:bg-indigo-200 group my-3">	
				<div>
					<input class="checked:ring-white" type="radio" name="path" id="backend" />
				</div>
				<div>
					<div class="font-semibold has-[:checked]:font-bold">Backend</div>
					<div class="group-hover:text-gray-500 group-focus-within:text-gray-500">Tu elección si te gusta la seguridad y robustez de unos datos bien mantenidos.</div>
				</div>
			</label>
			<label for="technical" class="flex items-start gap-x-4 bg-[var(--color-bg)] border border-black p-6 [&:has(:checked)]:bg-indigo-200 group my-3">	
				<div>
					<input class="checked:ring-white" type="radio" name="path" id="technical" />
				</div>
				<div>
					<div class="font-semibold has-[:checked]:font-bold">Técnico</div>
					<div class="group-hover:text-gray-500 group-focus-within:text-gray-500">Quiero hacer de las actualizaciones del AVAST una carrera profesional.</div>
				</div>
			</label>
		</fieldset>
			

3. Tamaño de texto fluido

Queremos intentar no utilizar tantas media queries y utilizar el view port a nuestro favor para poder tener unos títulos la mar de responsive. Puedes comprobar lo que ocurre cuando intentas hacer un resize de esta página con los títulos.

Para ello, hemos podido hacer:


		{variant === 'h1' && <h1 class={`text-[6.5vw] leading-none font-bold mb-4 ${extraClasses}`}>{title}</h1>}
		{variant === 'h2' && <h2 class={`text-[3vw] leading-none font-bold mb-4 ${extraClasses}`}>{title}</h2>}
			

4. Estilar dependiendo de la posición

Esta la sabes, pero muchas veces se nos olvida, y por eso no viene nada mal un buen recordatorio. Tenemos las clases para hacer first: odd: (posición impar), o even: (posición par) y poder crear una UI de la hostia. Incluso podemos hacer movidas para estilar solo los tres primeros elementos. Y todo solo con CSS/Tailwind, nada de JavaScript mirando index.


		// Estila el primer y último elemento	
		<div class="flex flex-wrap gap-2 my-4">
			{[...Array(7)].map((_) => (
				<div class="h-4 w-6 bg-white border border-black first:bg-red-200 last:bg-red-200"></div>
			))}
		</div>

		// Estila los elementos impares
		<div class="flex flex-wrap gap-2 my-4">
			{[...Array(7)].map((_) => (
				<div class="h-4 w-6 bg-white border border-black odd:bg-red-200" ></div>
			))}
		</div>

		// Estila los elementos pares
		<div class="flex flex-wrap gap-2 my-4">
			{[...Array(7)].map((_) => (
				<div class="h-4 w-6 bg-white border border-black even:bg-red-200"></div>
			))}
		</div>

		// Estila los tres primeros elementos
		<div class="flex flex-wrap gap-2 my-4 [&>*:nth-child(-n+3)]:bg-red-200">
			{[...Array(7)].map((_) => (
				<div class="h-4 w-6 bg-white border border-black" ></div>
			))}
		</div>
		

También podemos crear layouts interesantes con grid de esta forma.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!

		// Estila el primer elemento para ocupar todo el espacio
		<div class="grid gap-4 my-4">
			{[...Array(3)].map((_) => (
				<div
				class="bg-white border border-black first:sm:col-span-2 first:sm:row-span-2 first:sm:bg-red-200 p-4"
				>Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!</div>
			))}
		</div>
			

5. Container Queries

Por mucho que intentemos huir de ellas, las container queries ya están aquí, y en verdad, tienen bastante sentido. Echémosle un vistazo al siguiente componente en el que podemos hacer cambios dependiendo del espacio que ocupe. Por ahora hay que instalar el plugin @tailwindcss/container-queries, ¡pero ojalá se incorpore de forma nativa pronto!

Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!

		// El elemento más grande desplegará la imagen y el nombre, además del texto en negrita
		<div class="grid gap-4 my-4 ">
			{[...Array(3)].map((_) => (
				<div class="@container bg-[var(--color-bg)] border border-black first:sm:col-span-2 first:sm:row-span-2 p-4">
					<div class="@lg:font-bold">Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam reiciendis voluptatum voluptate non dolorum odit excepturi vero. Ut, dolores perspiciatis? Culpa ad aspernatur accusantium quis autem reprehenderit voluptatem, odit et!</div>
					<figure class="hidden @xl:flex @xl:gap-2 @xl:items-center my-4">
						<img class="w-12 h-12 rounded-full object-cover" src="https://images.unsplash.com/photo-1568602471122-7832951cc4c5?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80" alt="guy">
						<figcaption class="flex flex-col">
							<span class="text-xs">Paco Sánchez</span>
							<span class="text-xs">Entusiasta web</span>
						</figcaption>
					</figure>
				</div>
			))}
		</div>

		

6. Grid Masonry

Esto no necesita mucha descripción. Pronto vamos a poder ser capaces de utilizar la propiedad grid-rows-[masonry] para poder crear este tipo de layout de una forma sencilla. Fuera quebraderos de cabeza y librerías externas. ¡Vendrá más pronto que tarde!

7. Text Balance

Esto va a estar genial el día que esté aceptado en todos los navegadores, ya que por el momento no es así ni en Safari ni en Chrome a julio de 2023. Se trata de una forma de distribuir el espacio del texto y hacer los saltos de línea de forma que el texto quede visualmente bien. Por el momento no funciona, pero llegará...

Este texto debería verse bien en las dos resoluciones


		<h3 class="text-balance text-4xl font-bold my-6">Este texto debería verse bien en las dos resoluciones</h3>
			

8. Máximo número de líneas con line-clamp

La propiedad line-clamp en CSS es superútil cuando no queremos que el contenido exceda un cierto número de líneas para así mantener una estética coherente y ordenada. Por ejemplo, supongamos que tenemos los siguientes elementos con nombres y direcciones de correo de las personas.

9. Crear gradients con from, via y to

Como todos sabemos, los gradientes son una forma muy buena para darle vida y profundidad a los diseños. Son muy versátiles: pueden ser sutiles o atrevidos, lineales o radiales, y pueden tener tantos colores como desees. Con Tailwind podemos controlar la dirección, los colores y las posiciones de los puntos de parada de los gradientes directamente desde las clases en el HTML.


	<button class="mb-4 w-64 h-10 text-white bg-gradient-to-r from-blue-500 to-purple-500 hover:from-purple-500 hover:to-blue-500 transition duration-500 ease-in-out">
		Botón con gradiente
	</button>
					
			

Gradient lineal con 3 puntos


	<div class="w-full h-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 min-h-[300px] my-6"></div>
		
			

Si quieres profundizar en el tema, échale un vistazo a esa parte de la documentación en la página de TailwindCSS , ya que se pueden hacer un montón de movidas como usar porcentajes para tener un control total de los intervalos de los gradientes.

10. Atajo para font-size y line-height

Desde la versión 3.3 de TailwindCSS podemos utilizar un atajo para gestionar ambas propiedades al mismo tiempo. De forma que los dos siguientes pedazos de código serían equivalentes.

Heading con las dos propiedades (version 1)
Heading con las dos propiedades (version 2)

	<div class="text-6xl leading-normal mb-4">Heading con las dos propiedades (version 1)</div>
	<div class="text-6xl/normal" >Heading con las dos propiedades (version 2)</div>