Frontend

CSS :where() y :is(): Especificidad, Resets y Mantenimiento

Muchos desarrolladores ignoran las pseudo-clases CSS :where() e :is(), viéndolas como "azúcar sintáctico". Este artículo revela cómo son cruciales para resolver el caos de la especificidad, permitiendo crear CSS escalable y mantenible. Entiende la diferencia y aplícala en resets, componentes y sistemas de diseño.

CSS :where() y :is(): Especificidad, Resets y Mantenimiento

¿Ya has escrito el mismo bloque de CSS tres veces solo cambiando el selector? Ese es exactamente el problema que :is() y :where() resuelven — y la diferencia entre ambos es más importante de lo que parece.

Este CSS te resulta familiar:

TEXT
header a,footer a,nav a {  color: blue;  text-decoration: none;}

Repitiendo la misma regla para tres contextos diferentes. Ahora imagina que tienes diez contextos. Y que cada uno tiene tres variaciones de estado — :hover, :focus, :visited. El archivo CSS se convierte en una guía telefónica.

El CSS moderno tiene una respuesta para esto. Dos, de hecho — y parecen iguales pero se comportan diferente en un detalle que te salvará de errores sutiles.

:is() — agrupa selectores sin repetición

El :is() acepta una lista de selectores y aplica el estilo a cualquiera que coincida. El bloque anterior se convierte en:

TEXT
:is(header, footer, nav) a {  color: blue;  text-decoration: none;}

Una línea, mismo resultado. El navegador lo interpreta exactamente como la versión repetida — semánticamente son equivalentes.

Se vuelve más interesante cuando lo combinas con pseudo-clases:

TEXT
/* Antes — repetindo pra cada estado */button:hover,button:focus,button:active {  background: darkblue;}/* Depois — limpo */button:is(:hover, :focus, :active) {  background: darkblue;}

O para estilizar encabezados de una vez:

TEXT
/* Todos os headings dentro de article */article :is(h1, h2, h3, h4) {  font-family: Georgia, serif;  line-height: 1.3;}

:where() — idéntico, pero sin peso de especificidad

Aquí reside la diferencia importante.

La especificidad en CSS es el sistema de puntuación que decide qué regla prevalece cuando dos se contradicen. Un selector de ID vale más que una clase, que vale más que una etiqueta. Cuando usas :is(), la especificidad del selector más fuerte dentro de los paréntesis se propaga a toda la regla.

TEXT
/* :is() herda a especificidade do #header — alta */:is(#header, .nav, footer) a {  color: blue;}/* ↑ esse seletor tem especificidade de ID   por causa do #header, mesmo que o elemento   seja .nav ou footer */

El :where() funciona igual en comportamiento — pero tiene especificidad cero. Siempre. No importa lo que pongas dentro.

TEXT
/* :where() — especificidade zero */:where(#header, .nav, footer) a {  color: blue;}/* ↑ fácil de sobrescrever — qualquer seletor   de classe ou tag já ganha */

En la práctica, esto significa que :where() es ideal para estilos base y resets — defines sin bloquear al desarrollador que personalizará después.

La diferencia en la vida real

Problema con :is()
TEXT
/* base.css */:is(article, section) p {  color: gray;}/* theme.css — não vai funcionar */p {  color: black;}/* :is(article, section) p tem   especificidade maior que p */
Solución con :where()
TEXT
/* base.css */:where(article, section) p {  color: gray;}/* theme.css — funciona */p {  color: black;}/* :where() tem especificidade zero   qualquer p ganha na sobreposição */

Un ejemplo que une ambos

En un sistema de diseño, usarías ambos en capas diferentes:

TEXT
/* Camada base — :where() pra não travar ninguém */:where(h1, h2, h3, h4, h5, h6) {  margin: 0 0 0.5em;  line-height: 1.2;}/* Camada de componente — :is() pra contextos específicos */.card :is(h2, h3) {  font-size: 1.25rem;  color: var(--color-heading);}/* Estados interativos — :is() porque precisa de peso */.btn:is(:hover, :focus-visible) {  outline: 2px solid currentColor;  outline-offset: 2px;}