</> HTML5Advent
ENFRESDEITPT

// css · Web Platform Advent #17

El selector CSS :has(): el selector de padre, explicado (2026)

La pseudoclase relacional :has() permite dar estilo a un elemento segun lo que contiene o lo que le sigue: el tan esperado selector de padre. Como funciona, ejemplos reales, compatibilidad con navegadores y los detalles que conviene conocer.

Primer plano de lineas de codigo CSS de colores en la pantalla de un ordenador

La pseudoclase :has() es la funcionalidad que los autores de CSS pidieron durante dos decadas: una forma de dar estilo a un elemento segun lo que hay dentro de el, o lo que viene despues. Se le suele llamar el "selector de padre", aunque en realidad es mas potente que eso: es un selector relacional. Ahora esta disponible en todos los navegadores modernos, asi que puedes usarlo hoy en produccion.

La idea basica

Un selector CSS normal apunta a un elemento por su propio tipo, clase o estado. :has() le da la vuelta a eso: apunta a un elemento por los elementos que contiene. Escribes el padre y luego :has(...) con un selector para lo que debe haber dentro.

/* Style any .card that contains an <img> */
.card:has(img) {
  padding: 0;
}

/* Style a <figure> only when it has a caption */
figure:has(figcaption) {
  border: 1px solid #ddd;
}

El elemento que realmente recibe el estilo es el que va antes de :has(): la .card y el figure, no la imagen ni el pie de foto que hay dentro. Ese es todo el truco: la condicion trata sobre los descendientes, pero el objetivo es el ancestro.

Es relacional, no solo "padre"

Como puedes poner combinadores dentro de :has(), hace mucho mas que mirar a los hijos. Puedes reaccionar a los hermanos, algo que antes era imposible en CSS.

/* A heading that is immediately followed by a paragraph */
h2:has(+ p) {
  margin-bottom: 0.25rem;
}

/* A form that contains an invalid field */
form:has(input:invalid) {
  border-left: 3px solid crimson;
}

/* A label whose related required input is empty and focused */
.field:has(input:required:placeholder-shown) .hint {
  opacity: 1;
}

Ese segundo ejemplo es justo el tipo de cosa para la que la gente solia recurrir a JavaScript: cambiar el aspecto de un formulario entero (o de cualquier contenedor) segun el estado de un control que hay dentro. Con :has() es una sola linea de CSS, y se actualiza en vivo a medida que el usuario escribe.

Vista en primer plano de codigo HTML y CSS mostrado en una pantalla
:has() lee la relacion entre elementos, de modo que un contenedor puede responder al contenido y al estado que hay dentro: sin necesidad de JavaScript.

Patrones practicos

  • Consultas de cantidad y contenido: da estilo a una galeria de forma distinta cuando tiene exactamente una imagen frente a cuando tiene muchas: .gallery:has(img:only-child).
  • Estilo del hermano anterior: CSS solo podia mirar hacia delante con + y ~; :has() permite que un elemento anterior reaccione a otro posterior, por ejemplo label:has(+ input:checked).
  • Maquetacion que se adapta al contenido: da a una tarjeta una maquetacion de dos columnas solo cuando realmente contiene una imagen, y de una sola columna cuando no.
  • Estados de formulario y validacion: resalta un fieldset entero cuando cualquier control de su interior es invalido, sin una sola linea de script.

Compatibilidad con navegadores

La compatibilidad es amplia en 2026. :has() llego a Chrome y Edge 105 (agosto de 2022) y a Safari 15.4, y Firefox se sumo en la version 121 (diciembre de 2023). Eso significa que ahora esta disponible en las versiones actuales de todos los navegadores principales, y es seguro apoyarse en el para la mayoria de las audiencias. Para navegadores mas antiguos puedes tratar los estilos con :has() como una mejora progresiva, ya que un navegador que no entiende el selector simplemente ignora la regla.

Detalles que conviene conocer

  • No puedes coincidir con pseudoelementos dentro de :has(). Selectores como ::before no estan permitidos como argumento.
  • :has() no se permite dentro de :has(). No puedes anidar una comprobacion relacional dentro de otra.
  • Manten un alcance razonable. Selectores muy amplios como body:has(...) que obligan al navegador a volver a comprobar grandes partes de la pagina pueden costar rendimiento en documentos grandes que se actualizan con frecuencia. En la practica, los selectores acotados en componentes van bien; los motores han optimizado los casos comunes.
  • El objetivo es el sujeto, no la coincidencia. Si una regla no se aplica, comprueba que estas dando estilo al elemento que va antes de :has(), no al que esta dentro.

Preguntas frecuentes

¿Es :has() realmente el selector de padre de CSS? En la practica si, y mas. Selecciona un ancestro segun sus descendientes, que es el caso de uso clasico del "selector de padre", pero como acepta combinadores tambien puede seleccionar segun los hermanos siguientes, asi que se describe mejor como un selector relacional.

¿Puedo usar :has() en produccion en 2026? Para la mayoria de los sitios, si: es compatible con las versiones actuales de todos los navegadores principales. Usalo como una mejora progresiva si necesitas dar soporte a navegadores muy antiguos.

¿:has() perjudica el rendimiento? Rara vez, si mantienes los selectores acotados. Comprobaciones :has() extremadamente amplias, a nivel de todo el documento, en paginas grandes que cambian constantemente pueden anadir coste, pero el uso a nivel de componente va bien.

Para mas CSS moderno, consulta nuestras guias sobre anidamiento nativo de CSS y container queries.