Durante mucho tiempo, si querías que una página web reaccionara a lo que estaba haciendo el usuario, la solución habitual era recurrir a JavaScript.
Un cambio de estado.
Una clase que aparece.
Un elemento que cambia cuando otro está seleccionado.
Una tarjeta que modifica su apariencia dependiendo de lo que ocurre dentro.
Muchas de estas situaciones necesitaban JavaScript para controlar qué debía suceder.
Pero CSS ha evolucionado.
Y una de las incorporaciones más interesantes es :has(), un selector que permite seleccionar un elemento en función de lo que contiene o de la relación que mantiene con otros elementos.
No sustituye a JavaScript.
Pero sí permite resolver con CSS muchos estados visuales que antes requerían código adicional.
¿Qué es el selector CSS :has()?
:has() es una pseudo-clase de CSS que permite seleccionar un elemento cuando contiene un elemento que cumple una determinada condición.
Dicho de una forma sencilla:
CSS puede reaccionar a lo que ocurre dentro de otro elemento.
Por ejemplo, podemos seleccionar un contenedor cuando dentro de él existe un campo marcado:
.formulario:has(input:checked) {
border-color: green;
}
En este caso, .formulario recibe el estilo únicamente cuando contiene un input que está seleccionado.
Y ahí empieza lo interesante.
Hasta ahora, CSS podía aplicar estilos principalmente hacia abajo en la estructura del documento. Con :has(), podemos utilizar una condición basada en un elemento descendiente para decidir qué aspecto tendrá su contenedor.
Por eso se suele describir como un selector relacional.
Un ejemplo sencillo
Imagina una tarjeta que contiene una casilla de selección.
Queremos que la tarjeta cambie de apariencia cuando el usuario marque esa casilla.
HTML
<div class="tarjeta">
<label>
<input type="checkbox">
Seleccionar producto
</label>
<h2>Producto destacado</h2>
<p>Descripción del producto.</p>
</div>
CSS
.tarjeta {
padding: 20px;
border: 2px solid #ddd;
transition: .3s;
}
.tarjeta:has(input:checked) {
border-color: #00ccff;
transform: translateY(-4px);
}
No hemos añadido JavaScript.
Cuando el usuario marca la casilla, CSS detecta el estado :checked y aplica los estilos al contenedor .tarjeta.
Esto es precisamente lo interesante de :has().
¿Por qué :has() resulta tan útil?
Porque permite expresar determinadas condiciones directamente en CSS.
En lugar de utilizar JavaScript para detectar un estado y añadir una clase, podemos hacer que CSS compruebe directamente esa condición.
Por ejemplo:
.card:has(.error) {
border-color: red;
}
Aquí estamos diciendo:
“Selecciona .card cuando contenga un elemento con la clase .error”.
Esto puede resultar especialmente útil para interfaces, formularios y componentes cuyo comportamiento es principalmente visual.
:has() no significa que CSS sustituya a JavaScript
Aquí merece la pena hacer una aclaración.
A veces se presenta :has() como si fuera una especie de sustituto de JavaScript.
No lo es.
JavaScript sigue siendo necesario cuando necesitamos lógica, cálculos, acceso a APIs, manipulación compleja del DOM, peticiones de datos, almacenamiento o comportamientos que CSS no puede controlar.
La verdadera ventaja de :has() es otra:
permite evitar JavaScript cuando el problema que queremos solucionar es únicamente de presentación o estado visual.
Y eso puede simplificar bastante determinados componentes.
Algunos usos interesantes de :has()
Las posibilidades son bastante amplias.
Formularios
Podemos modificar visualmente un formulario cuando contiene un campo válido, inválido o seleccionado.
Por ejemplo:
form:has(input:invalid) {
border-color: red;
}
Esto permite reaccionar visualmente ante un estado de validación sin tener que añadir una clase mediante JavaScript.
Tarjetas seleccionadas
.tarjeta:has(input:checked) {
background: #f5f5f5;
}
Ideal para tarjetas de selección, configuradores o determinadas interfaces de producto.
Navegación
También podemos modificar determinados elementos de una navegación dependiendo de lo que ocurra dentro de ella.
nav:has(a:hover) {
background: #f5f5f5;
}
Aquí el contenedor <nav> cambia mientras uno de sus enlaces está bajo el cursor.
Diseño responsive
:has() también puede utilizarse junto con otras características de CSS para construir componentes que respondan a determinadas condiciones de su contenido.
Esto abre posibilidades interesantes para crear componentes más adaptables sin depender de JavaScript para cada pequeño cambio visual.
Una de las grandes ventajas: trabajar con estados
Una buena forma de entender :has() es pensar en estados.
Un elemento puede estar:
- seleccionado
- vacío
- activo
- deshabilitado
- enfocado
- mostrando un error
- conteniendo determinados elementos
Y :has() permite utilizar algunos de esos estados para modificar el aspecto de otro elemento relacionado.
Por ejemplo:
.contenedor:has(input:focus) {
box-shadow: 0 0 0 3px rgba(0, 204, 255, .2);
}
Cuando el usuario entra en el campo, podemos modificar visualmente todo el contenedor.
Son pequeños detalles, pero pueden hacer que una interfaz resulte mucho más clara.
¿Qué ventajas tiene utilizar :has()?
Utilizar CSS cuando CSS es suficiente puede aportar varias ventajas.
Menos JavaScript innecesario
Si solo necesitas cambiar un estilo dependiendo de un estado, no siempre necesitas escribir JavaScript para hacerlo.
Código más sencillo
La condición puede quedar expresada directamente en el selector.
Mejor separación entre comportamiento y presentación
Cuando hablamos de cambios puramente visuales, tiene sentido que esa lógica permanezca en CSS.
Componentes más fáciles de mantener
Un selector como:
.tarjeta:has(input:checked)
puede resultar mucho más fácil de entender que un script que detecta el evento, busca el elemento, añade una clase y después elimina esa clase cuando cambia el estado.
Eso sí: esto depende del caso de uso. :has() no convierte automáticamente cualquier solución en una solución mejor.
Compatibilidad de :has()
La buena noticia es que :has() ya cuenta con soporte en los principales navegadores modernos, incluyendo Chrome, Edge, Safari y Firefox.
Aun así, cuando desarrollamos una web profesional conviene comprobar siempre la compatibilidad concreta de las funciones CSS que vamos a utilizar y decidir qué experiencia tendrán los usuarios en navegadores antiguos.
La referencia más fiable para comprobar el soporte actualizado es la documentación de MDN:
¿Cuándo deberías utilizar :has()?
Una buena regla es bastante sencilla:
utilízalo cuando la condición sea visual y CSS pueda resolverla de forma clara.
Por ejemplo:
- cambiar el aspecto de una tarjeta seleccionada
- modificar un formulario cuando contiene errores
- destacar un contenedor cuando uno de sus elementos tiene foco
- adaptar componentes según determinados estados
- crear pequeñas interacciones
- controlar determinados estados visuales sin JavaScript
Pero si necesitas realizar cálculos, consultar una API, guardar información o ejecutar una lógica compleja, JavaScript seguirá siendo la herramienta adecuada.
No se trata de elegir entre CSS o JavaScript.
Se trata de utilizar cada herramienta donde tiene más sentido.
El diseño web también está en estos pequeños detalles
En Lumazaki nos interesa especialmente esta parte.
Porque una buena interfaz no se construye únicamente con colores, tipografías y fotografías.
También se construye con pequeñas decisiones.
Qué ocurre cuando haces clic.
Qué cambia cuando seleccionas algo.
Cómo se comporta una tarjeta.
Cómo sabes que un formulario tiene un error.
Cómo responde una interfaz a tus acciones.
Muchas veces el usuario ni siquiera sabe que todo eso está ocurriendo gracias a CSS.
Y precisamente ahí está la gracia.
El mejor código es muchas veces el que hace que una interfaz parezca sencilla.
Conclusión
El selector CSS :has() es una de las herramientas más interesantes que ha incorporado CSS moderno.
No porque vaya a hacer desaparecer JavaScript.
Sino porque permite resolver directamente desde CSS determinados problemas que antes necesitaban código adicional.
Y eso abre una forma diferente de plantear el desarrollo de interfaces:
menos código cuando no hace falta más código.
Para diseñadores y desarrolladores web, aprender a utilizar :has() significa tener una herramienta más para crear interfaces dinámicas, claras y eficientes.
Porque a veces no necesitas programar una interacción.
Solo necesitas saber que CSS ya sabe hacerla.