Selectores :is() y :where()

Básico
CSS
CSS
Actualizado: 19/04/2026

El problema de los selectores repetitivos

En hojas de estilo de un cierto tamaño es habitual encontrarse con reglas que aplican los mismos estilos a varios selectores. Un ejemplo clásico son los encabezados: queremos que h1, h2, h3, h4, h5 y h6 compartan familia tipográfica, peso y color, pero tengan tamaños distintos.

Sin ayudas, tendríamos que escribirlos separados por comas cada vez que aparecen:

header h1, header h2, header h3, header h4, header h5, header h6 {
    font-family: "Inter", sans-serif;
    color: #1e293b;
}

article h1, article h2, article h3, article h4, article h5, article h6 {
    font-family: "Inter", sans-serif;
    color: #1e293b;
}

Esa repetición crece con cada contexto añadido y es fácil equivocarse al copiar la lista. Los selectores :is() y :where() simplifican estas listas permitiendo agrupar cualquier conjunto de selectores dentro de paréntesis.

La regla anterior se reescribe con :is() de forma mucho más compacta:

:is(header, article) :is(h1, h2, h3, h4, h5, h6) {
    font-family: "Inter", sans-serif;
    color: #1e293b;
}

Ambos selectores dentro de los paréntesis se combinan: el navegador aplicará los estilos a cualquier encabezado que viva dentro de un header o un article. El CSS queda más limpio y eliminamos la duplicación.

Cómo funciona :is()

El selector :is() acepta una lista de selectores separados por comas y coincide con cualquier elemento que cumpla al menos uno de ellos. Es equivalente a escribir esos selectores por separado, pero en una única regla.

.boton:is(.primario, .secundario, .peligro) {
    padding: 0.5rem 1rem;
    border-radius: 4px;
    font-weight: 600;
}

Esta regla aplica a cualquier elemento con clase .boton y que tenga además cualquiera de las tres variantes. Evita escribir tres reglas separadas con las mismas declaraciones.

El selector :is() se puede combinar con descendencia, hermanos o cualquier otro combinador estándar. Esto lo hace especialmente útil para describir jerarquías complejas:

nav :is(ul, ol) li {
    list-style: none;
    padding: 0.25rem 0;
}

main > :is(section, article) h2 {
    margin-top: 2rem;
}

Especificidad en :is()

Un detalle importante es cómo :is() calcula su especificidad. Toma la mayor especificidad de los selectores que contiene. Por ejemplo, :is(.boton, #principal) pesa como un id porque contiene #principal, aunque el elemento coincidente solo sea un botón sin id.

/* Especificidad de id (0,1,0) por culpa de #menu */
:is(.enlace, #menu) {
    color: #3b82f6;
}

Esta característica puede causar conflictos inesperados en la cascada. Si mezclamos clases con ids dentro de :is(), la regla puede sobrescribir otras reglas que no esperábamos porque su peso es mayor al que sugiere su apariencia.

La especificidad de :is() es la del selector con mayor especificidad de su lista. Evita mezclar selectores de especificidades muy distintas si no quieres sorpresas.

Para evitar esa elevación de peso existe el selector :where(), que se describe a continuación.

El selector :where()

El selector :where() se comporta exactamente igual que :is() en cuanto a la coincidencia de elementos, pero tiene una diferencia crucial: su especificidad siempre es cero. No importa qué selectores contengas dentro, el selector completo pesa como si no estuviera.

Esta propiedad lo hace ideal para crear estilos base fácilmente sobreescribibles desde otros sitios. Los frameworks y reset CSS modernos la usan para definir valores por defecto sin interferir con los estilos de quien los consume.

:where(h1, h2, h3, h4, h5, h6) {
    line-height: 1.2;
    margin-block: 0.5em;
}

.titulo-especial {
    line-height: 1.5;
}

Aunque :where(h1, ...) apunta a un h1, no gana peso por su presencia. La clase .titulo-especial tiene mayor peso (0,1,0) y sobrescribe sin problemas el line-height base. Si hubiéramos usado :is(), el comportamiento sería el mismo porque también es clase cero en esta lista; la diferencia se nota cuando la lista tiene selectores de más peso.

Comparemos los dos casos:

/* Especificidad: (0,1,0) por el .nav */
:is(.nav, .menu) a {
    color: #3b82f6;
}

/* Especificidad: (0,0,1) solo por el "a" final */
:where(.nav, .menu) a {
    color: #3b82f6;
}

El primer selector es más específico y gana sobre reglas simples. El segundo queda al nivel de un selector de etiqueta y se deja sobrescribir fácilmente.

Aplicaciones de :where() en resets y temas base

La principal utilidad de :where() aparece en librerías de componentes, resets CSS y design systems. Un reset moderno típico utiliza :where() para garantizar que sus estilos nunca estorben a quien los incluya:

:where(*, *::before, *::after) {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

:where(button, input, select, textarea) {
    font: inherit;
    color: inherit;
}

:where(a) {
    color: inherit;
    text-decoration: none;
}

Quien importe este reset podrá sobrescribir cualquier regla con un simple selector de clase, sin tener que pelear con !important ni añadir especificidad artificial. Este patrón es el que usan proyectos como Open Props, Pico CSS y muchos otros.

flowchart LR
    A[Tu estilo con clase] --> B{Cascada CSS}
    C[Reset con :where] --> B
    B --> D[Tu estilo gana siempre]

:is() anidado y lista indulgente

Los selectores :is() y :where() implementan una característica llamada lista indulgente o forgiving selector list. Si uno de los selectores dentro de la lista es inválido o no lo entiende el navegador, el resto sigue funcionando.

En una lista con comas tradicional, si un selector falla, la regla entera se descarta:

/* Si un navegador no soporta :has, toda la regla falla */
.card, :has(img) {
    border: 1px solid;
}

/* Si :has no se soporta, la clase .card sigue funcionando */
:is(.card, :has(img)) {
    border: 1px solid;
}

Esta resiliencia facilita adoptar features experimentales junto a selectores clásicos, sin miedo a romper toda la regla por un selector no soportado en algún navegador antiguo.

Patrones comunes y buenas prácticas

En proyectos reales, :is() ayuda a describir jerarquías complejas con menos líneas, y :where() se reserva para estilos base que no deben competir con los del consumidor.

Un patrón habitual es combinar ambos en una hoja de estilos:

/* Reset base con :where para no interferir */
:where(article, section) {
    padding-block: 2rem;
}

/* Regla específica con :is para agrupar sin perder peso */
article :is(h1, h2, h3) + p {
    margin-top: 0.5rem;
}

Otra práctica útil es usar :is() junto con el anidamiento nativo para expresar variantes en componentes:

.tarjeta {
    padding: 1rem;

    &:is(.destacada, .premium) {
        border: 2px solid gold;
    }

    & :is(h2, h3) {
        margin-top: 0;
    }
}

Como regla práctica: usa :is() cuando quieras mantener o aprovechar la especificidad; usa :where() cuando escribas estilos base o utilidades que deban sobrescribirse con facilidad.

La combinación de :is(), :where(), :has() y el anidamiento nativo forma el vocabulario moderno de CSS. Con estas herramientas, las hojas de estilo son más concisas, más legibles y más resistentes a cambios futuros. Los proyectos grandes se benefician especialmente porque permiten describir patrones complejos sin recurrir a preprocesadores ni repetir reglas una y otra vez.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en CSS

Documentación oficial de CSS
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, CSS es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de CSS

Explora más contenido relacionado con CSS y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Utilizar :is() para agrupar selectores comunes, aplicar :where() para estilos base sin impactar la cascada y combinar ambos con otros selectores modernos.