Skip to content
article

:has() en CSS: el selector padre que pedimos durante 20 años (y 6 cosas que ya hago con él)

sin JavaScript, sin clases extra, sin trucos raros

Sofía Ramírez@sofi_dev_mx

September 26, 2026 · 2 min read

Durante años la respuesta a "¿se puede seleccionar el padre en CSS?" fue "no, usa JavaScript". Ya no. :has() funciona en todos los navegadores modernos desde finales de 2023 (Firefox fue el último, en la versión 121) y yo ya lo uso en producción sin miedo.

La idea: A:has(B) selecciona A si adentro tiene algo que cumpla B. Van mis usos favoritos:

1. Tarjetas con o sin imagen

.card:has(img) {
  grid-template-rows: 180px 1fr;
}
.card:not(:has(img)) {
  padding-top: 2rem;
}

2. Estilos de error en formularios, sin JS

.campo:has(input:user-invalid) label {
  color: #b91c1c;
}
.campo:has(input:user-invalid) .ayuda {
  display: block;
}

Ojo: :user-invalid y no :invalid. La diferencia es que :user-invalid solo aplica después de que el usuario interactuó con el campo. Con :invalid el formulario aparece en rojo antes de que escribas nada, y eso es horrible.

3. Bloquear el scroll cuando hay un modal abierto

body:has(dialog[open]) {
  overflow: hidden;
}

Adiós al useEffect que agregaba y quitaba una clase al body.

4. Modo oscuro con un checkbox, cero JS

:root:has(#modo-oscuro:checked) {
  color-scheme: dark;
  --fondo: #111;
  --texto: #eee;
}

(Para guardar la preferencia sí necesitas JS, pero el cambio visual no.)

5. Cambiar el layout según cuántos hijos hay

.galeria {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}
.galeria:has(> :nth-child(5)) {
  grid-template-columns: repeat(3, 1fr);
}

Si existe un quinto hijo, es que hay 5 o más, y pasamos a 3 columnas.

6. El hermano anterior

li:has(+ li:hover) {
  transform: scale(1.05);
}

Esto selecciona el li que está justo ANTES del que tiene hover. Durante años fue imposible. Lo uso para un efecto tipo dock de macOS en un menú.

Cuidado con

  • No lo pongas en selectores súper amplios tipo *:has(...) en un DOM gigante. Los navegadores lo optimizan bien, pero no abuses
  • Si necesitas soportar navegadores viejos, envuélvelo en @supports selector(:has(*))

¿Qué otros usos le han encontrado? Quiero robarles ideas jaja 💜

Written by

Sofía Ramírez

Frontend dev 💜 React, TypeScript y mucho café de olla. Trabajo remoto para una empresa gringa desde Guadalajara. Fundé codigo_en_espanol porque todo está en inglés y ya me cansé jaja

Get the best of #codigo_en_espanol weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

9

Sign in to join the conversation.

Sign in to comment
Luis Treviño@luis_qa_mty#8 in Código en Español
el del modal con body:has(dialog[open]) me acaba de ahorrar un useEffect horrible que tenía un dev de mi equipo. Ya le mandé el link 🤣
2 net votes
Camilo Restrepo@camilo_parce#3 in Código en Español
El #6 del hermano anterior me voló la cabeza parce. Toda la vida pensé que era imposible sin JS
4 net votes
Valentina Sosa@valen_ux1 rep in Código en Español
re lindo 🎨 el de :user-invalid se lo paso a mi equipo ya mismo, siempre nos peleamos con los devs porque el error aparece antes de que el usuario escriba
0 net votes
Sofía Ramírez@sofi_dev_mx#1 in Código en Español
exacto Valen! esa es justo la diferencia con :invalid 💜
0 net votes
Rosa Quispe@rosa_aprende#7 in Código en Español
Disculpen, ¿esto lo puedo usar si recién estoy aprendiendo CSS? Probé el ejemplo del modo oscuro con un checkbox y funcionó!! 🥺 me emocioné
0 net votes
Sofía Ramírez@sofi_dev_mx#1 in Código en Español
Rosa claro que sí! si te funcionó ya lo estás usando jaja. qué bonito que lo probaste 💜
0 net votes
Lucía Ferrer@lucia_ferrer_dev#6 in Código en Español
Guardado!! El de la galería con nth-child no lo pillo del todo, ¿alguien me lo explica? xD
1 net votes
Sofía Ramírez@sofi_dev_mx#1 in Código en Español
Claro Lucía: .galeria:has(> :nth-child(5)) significa 'la galería que tiene un hijo directo que es el quinto'. Si existe un quinto hijo, es que hay 5 o más, y entonces cambias a 3 columnas. Es una forma de contar hijos con CSS 💜
0 net votes
Lucía Ferrer@lucia_ferrer_dev#6 in Código en Español
AAAH vale ya lo pillo!! qué truco más bueno, gracias Sofía!!
1 net votes