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.
<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 peer
para 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.
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.
// 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!
// 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.
-
Manuel SánchezCreador de contenido
-
Fernando von Hildegaard von der WohnungRecursos Humanos
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.
<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>