Selectores avanzados: :has(), :is(), :where() y :not()

Domina los selectores funcionales modernos para reducir duplicación, controlar especificidad y crear lógica visual sin JavaScript.

<code>:has()</code> permite seleccionar un elemento según su contenido o estado interno.

<code>:is()</code> y <code>:where()</code> agrupan selectores; <code>:where()</code> lo hace con especificidad 0.

<code>:not()</code> excluye casos concretos y evita reglas duplicadas.

Combinando estos selectores con estados como <code>:valid</code> y <code>:invalid</code> puedes resolver mucha lógica visual sin JavaScript.

  • Selecciona el elemento actual cuando se cumple una condición interna.
  • Durante años CSS no podía seleccionar un padre según su contenido. <code>:has()</code> resuelve eso y habilita patrones que antes requerían JavaScript.
  • La forma mental correcta es: <code>elemento:has(condición)</code>. Si la condición se cumple, se estiliza el elemento principal.
  • En <code>.card:has(img)</code> no seleccionas la imagen, seleccionas la tarjeta por su contenido. En formularios, <code>form:has(:invalid)</code> permite feedback global de estado.
  • Sintaxis: <code>elemento:has(selector)</code>.

:has() para decisiones de layout y estado

Selecciona el elemento actual cuando se cumple una condición interna.

Durante años CSS no podía seleccionar un padre según su contenido. <code>:has()</code> resuelve eso y habilita patrones que antes requerían JavaScript.

La forma mental correcta es: <code>elemento:has(condición)</code>. Si la condición se cumple, se estiliza el elemento principal.

En <code>.card:has(img)</code> no seleccionas la imagen, seleccionas la tarjeta por su contenido. En formularios, <code>form:has(:invalid)</code> permite feedback global de estado.

  • Sintaxis: <code>elemento:has(selector)</code>.
  • Casos típicos: tarjetas con/sin media, menús con submenú, formularios con errores.
  • Puedes combinar <code>:has()</code> con <code>:not()</code>: <code>article:not(:has(img))</code>.
  • Acota el alcance por componente: <code>.checkout-form:has(:invalid)</code> suele ser mejor que reglas globales.
  • Compatibilidad: soporte sólido en navegadores modernos; revisa <a href="https://caniuse.com/css-has" target="_blank" rel="noreferrer">Can I Use</a>.

:is(), :where() y :not(): agrupar y excluir con estrategia

Mismo objetivo de mantenimiento, distinta estrategia de especificidad.

Usa <code>:is()</code> para agrupar selectores equivalentes sin repetir prefijos largos.

Usa <code>:where()</code> para estilos base que quieras sobrescribir con facilidad, porque su especificidad es 0.

Usa <code>:not()</code> para excluir variantes concretas sin duplicar reglas generales.

  • <code>:is(a, b, ...)</code>: agrupa y toma la especificidad del argumento más fuerte.
  • <code>:where(a, b, ...)</code>: agrupa con especificidad 0.
  • <code>:not(...)</code>: exclusión explícita y mantenible.
  • Regla práctica: base con <code>:where()</code>, variaciones con clases de componente.

Estrategia de especificidad y buenas prácticas

Evita guerras de especificidad desde el diseño del selector.

Si una regla es fundacional (tipografía base, espaciados por defecto), prioriza <code>:where()</code> para permitir overrides claros.

Reserva <code>:is()</code> para agrupaciones dentro de componentes o layout, y combina con clases para estados de interfaz.

Evita cadenas muy complejas como <code>:has(:not(...):is(...))</code> cuando no aportan valor real: la legibilidad también es mantenibilidad.

Pseudo-clases de validación en formularios

Estiliza inputs según required, pattern y validación nativa.

La validación nativa de HTML5 expone estados útiles para CSS: <code>:valid</code>, <code>:invalid</code>, <code>:required</code> y <code>:optional</code>.

Combinando esos estados con <code>:has()</code> puedes adaptar el botón de envío sin depender de JavaScript.

  • <code>:valid</code> / <code>:invalid</code>: según si el valor cumple las restricciones.
  • <code>:required</code> / <code>:optional</code>: según la presencia del atributo <code>required</code>.
  • Evita estilizar <code>:invalid</code> antes de interacción real; combina con <code>:focus</code> o <code>:not(:placeholder-shown)</code>.
CSS
53

Selectores avanzados: :has(), :is(), :where() y :not()

Domina los selectores funcionales modernos para reducir duplicación, controlar especificidad y crear lógica visual sin JavaScript.

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

📘 Teoría

:has() para decisiones de layout y estado

Selecciona el elemento actual cuando se cumple una condición interna.

Durante años CSS no podía seleccionar un padre según su contenido. :has() resuelve eso y habilita patrones que antes requerían JavaScript.

La forma mental correcta es: elemento:has(condición). Si la condición se cumple, se estiliza el elemento principal.

En .card:has(img) no seleccionas la imagen, seleccionas la tarjeta por su contenido. En formularios, form:has(:invalid) permite feedback global de estado.

  • Sintaxis: elemento:has(selector).
  • Casos típicos: tarjetas con/sin media, menús con submenú, formularios con errores.
  • Puedes combinar :has() con :not(): article:not(:has(img)).
  • Acota el alcance por componente: .checkout-form:has(:invalid) suele ser mejor que reglas globales.
  • Compatibilidad: soporte sólido en navegadores modernos; revisa Can I Use.
Patrones concretos con :has()
.card:has(img) {
  grid-template-columns: 160px 1fr;
  border-color: #f59e0b;
}

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

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

nav li:has(ul) > a::after {
  content: "▾";
  margin-left: 0.35rem;
}

:is(), :where() y :not(): agrupar y excluir con estrategia

Mismo objetivo de mantenimiento, distinta estrategia de especificidad.

Usa :is() para agrupar selectores equivalentes sin repetir prefijos largos.

Usa :where() para estilos base que quieras sobrescribir con facilidad, porque su especificidad es 0.

Usa :not() para excluir variantes concretas sin duplicar reglas generales.

  • :is(a, b, ...): agrupa y toma la especificidad del argumento más fuerte.
  • :where(a, b, ...): agrupa con especificidad 0.
  • :not(...): exclusión explícita y mantenible.
  • Regla práctica: base con :where(), variaciones con clases de componente.
Agrupación mantenible
:is(header, main, footer) a {
  color: #1d4ed8;
}

:where(article, section) h2 {
  margin-top: 0;
}

:is(.btn, .chip, .tag):focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

.button:not(.button--primary, .button--danger) {
  background: #e2e8f0;
  color: #0f172a;
}

Estrategia de especificidad y buenas prácticas

Evita guerras de especificidad desde el diseño del selector.

1

Si una regla es fundacional (tipografía base, espaciados por defecto), prioriza :where() para permitir overrides claros.

2

Reserva :is() para agrupaciones dentro de componentes o layout, y combina con clases para estados de interfaz.

3

Evita cadenas muy complejas como :has(:not(...):is(...)) cuando no aportan valor real: la legibilidad también es mantenibilidad.

Base baja + override claro
:where(.card, .panel, .sheet) h3 {
  font-size: 1.125rem;
  line-height: 1.35;
}

.card.featured h3 {
  font-size: 1.25rem;
}

Pseudo-clases de validación en formularios

Estiliza inputs según required, pattern y validación nativa.

La validación nativa de HTML5 expone estados útiles para CSS: :valid, :invalid, :required y :optional.

Combinando esos estados con :has() puedes adaptar el botón de envío sin depender de JavaScript.

  • :valid / :invalid: según si el valor cumple las restricciones.
  • :required / :optional: según la presencia del atributo required.
  • Evita estilizar :invalid antes de interacción real; combina con :focus o :not(:placeholder-shown).
Estilos por estado de validación
input:focus:invalid {
  border-color: #dc2626;
  outline-color: #dc2626;
}

input:valid:not(:placeholder-shown) {
  border-color: #16a34a;
}

form:has(:invalid) .btn-submit {
  opacity: 0.6;
  cursor: not-allowed;
}

🧭 Visuales clave

Selectores avanzados y prioridad

Ayuda a decidir cuando conviene simplificar selectores para mantener override limpio.

Comparativa de selectores compuestos y su impacto en especificidad.

Mapa de selectores avanzados

Sirve como chuleta rápida para reconocer combinadores, funcionales y contextos donde aportan precisión real.

Resumen visual de selectores avanzados en CSS y sus casos de uso

Pseudo-clases y pseudo-elementos

Aclara una de las confusiones más frecuentes cuando ya empiezas a usar selectores más expresivos.

Comparativa entre pseudo-clases y pseudo-elementos en CSS

🧪 Aprende probando

Ejemplo Demo: decisiones con :has() + agrupación con :is() Activa un modo visual y comprueba estilos compartidos sin duplicar selectores.
Ejemplo Demo interactiva: formulario contextual El contenedor cambia cuando hay foco o errores.

🏁 Retos

Reto Reto: form con :has(:invalid) Deshabilita visualmente el botón cuando haya campos inválidos usando form:has(:invalid) .btn-submit.
Reto Reto: exclusión con :not() Aplica un estilo neutro a todos los botones salvo primary y danger.
Reto Reto: tarjeta condicional Resalta .card solo si contiene una imagen.

🧰 Recursos

¿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 .