:has() en profundidad: lógica visual sin JavaScript

Domina :has() para estilizar padres y relaciones entre elementos, con casos reales de formularios, tarjetas y estados de interfaz.

:has() permite seleccionar un elemento en función de su contenido, su estructura interna o el estado de sus descendientes.

Es clave para crear UI más declarativas: formularios que reaccionan al estado, tarjetas que cambian según su contenido y layouts con lógica contextual.

Úsalo con intención: selectores cortos, específicos por componente y fáciles de mantener.

  • Piensa en :has() como una condición booleana sobre el elemento actual.
  • En .card:has(img), no seleccionas la imagen: seleccionas la tarjeta si contiene una imagen. Esto cambia la forma de pensar CSS, porque puedes estilizar el contenedor según lo que ocurre dentro.
  • En formularios, :has() permite reglas globales de estado: por ejemplo, form:has(:invalid) o .field:has(input:focus). El resultado es menos JavaScript y menos clases temporales.
  • Seleccionar padres por contenido o estado interno.
  • Construir feedback visual sin depender de eventos JS.

Modelo mental correcto

Piensa en :has() como una condición booleana sobre el elemento actual.

En .card:has(img), no seleccionas la imagen: seleccionas la tarjeta si contiene una imagen. Esto cambia la forma de pensar CSS, porque puedes estilizar el contenedor según lo que ocurre dentro.

En formularios, :has() permite reglas globales de estado: por ejemplo, form:has(:invalid) o .field:has(input:focus). El resultado es menos JavaScript y menos clases temporales.

  • Seleccionar padres por contenido o estado interno.
  • Construir feedback visual sin depender de eventos JS.
  • Reducir clases auxiliares y mantener HTML más limpio.

Patrones reales con :has()

Tres patrones que usarás de forma recurrente en producto.

Patrón 1: tarjeta con contenido enriquecido. Si una card contiene imagen, aumenta contraste visual para priorizarla.

Patrón 2: campo activo. El wrapper del input responde al foco y mejora la percepción de interacción.

Patrón 3: formulario inválido. El botón de envío refleja estado global y reduce errores de envío prematuro.

Buenas prácticas

Evita cadenas difíciles de leer como :has(:not(...):is(...)) cuando no aporten valor claro. Prioriza reglas legibles por componente.

Acota el alcance del selector: .checkout-form:has(:invalid) suele ser mejor que un form:has(:invalid) global.

Cuando la condición sea crítica para negocio, documenta la intención con comentarios breves para facilitar mantenimiento.

CSS
55

:has() en profundidad: lógica visual sin JavaScript

Domina :has() para estilizar padres y relaciones entre elementos, con casos reales de formularios, tarjetas y estados de interfaz.

Código del tema: display: grid; gap: 1rem;

📘 Teoría

Modelo mental correcto

Piensa en :has() como una condición booleana sobre el elemento actual.

En .card:has(img), no seleccionas la imagen: seleccionas la tarjeta si contiene una imagen. Esto cambia la forma de pensar CSS, porque puedes estilizar el contenedor según lo que ocurre dentro.

En formularios, :has() permite reglas globales de estado: por ejemplo, form:has(:invalid) o .field:has(input:focus). El resultado es menos JavaScript y menos clases temporales.

  • Seleccionar padres por contenido o estado interno.
  • Construir feedback visual sin depender de eventos JS.
  • Reducir clases auxiliares y mantener HTML más limpio.

Patrones reales con :has()

Tres patrones que usarás de forma recurrente en producto.

1

Patrón 1: tarjeta con contenido enriquecido. Si una card contiene imagen, aumenta contraste visual para priorizarla.

2

Patrón 2: campo activo. El wrapper del input responde al foco y mejora la percepción de interacción.

3

Patrón 3: formulario inválido. El botón de envío refleja estado global y reduce errores de envío prematuro.

Ejemplos prácticos
.card:has(img) {
  border-color: #f59e0b;
}

.form-row:has(input:focus) {
  outline: 2px solid #2563eb;
}

form:has(:invalid) .submit {
  opacity: 0.5;
  pointer-events: none;
}

Buenas prácticas

1

Evita cadenas difíciles de leer como :has(:not(...):is(...)) cuando no aporten valor claro. Prioriza reglas legibles por componente.

2

Acota el alcance del selector: .checkout-form:has(:invalid) suele ser mejor que un form:has(:invalid) global.

3

Cuando la condición sea crítica para negocio, documenta la intención con comentarios breves para facilitar mantenimiento.

🧪 Aprende probando

Ejemplo Demo interactiva: formulario contextual El contenedor cambia cuando hay foco o errores.

🏁 Retos

Reto Reto: tarjeta condicional Resalta .card solo si contiene una imagen.

🧰 Recursos

Enlaces útiles

Test

Comprueba tus conocimientos con un test sobre CSS.

Test de CSS

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros 😉.

Esta la hice en febrero de 2026 para facilitar el aprendizaje de mis alumnxs. Aprender desarrollo web practicando. La idea es que crezca semanalmente con nuevos temas, tests y retos.

Inspirado en MDN, en W3Schools, en Codepen, en el crack de Manz y en mil sitios de documentación sobre desarrollo web. Quería aportar además de bloques teóricos con ejemplos, la gamificación de los retos y el sistema de test que ya tenía en culTest .

Si te gustó, si no te gustó, si quieres saludarme, o invitarme a 🍻 no dudes en escribirme en cristianeslava@gmail.com .