CSS :where() e :is(): Especificidade, Resets e Manutenção
Muitos desenvolvedores ignoram as pseudo-classes CSS :where() e :is(), vendo-as como "syntax sugar". Este artigo revela como elas são cruciais para resolver o caos da especificidade, permitindo criar CSS escalável e manutenível. Entenda a diferença e aplique em resets, componentes e design systems.

Você já escreveu o mesmo bloco de CSS três vezes só mudando o seletor? Essa é exatamente a dor que
:is()e:where()resolvem — e a diferença entre os dois é mais importante do que parece.
Esse CSS te parece familiar:
header a,footer a,nav a { color: blue; text-decoration: none;}Repetindo a mesma regra pra três contextos diferentes. Agora imagina que você tem dez contextos. E que cada um tem três variações de estado — :hover, :focus, :visited. O arquivo CSS vira uma lista telefônica.
O CSS moderno tem uma resposta pra isso. Duas, na verdade — e elas parecem iguais mas se comportam diferente num detalhe que vai te salvar de bugs sutis.
:is() — agrupe seletores sem repetição
O :is() aceita uma lista de seletores e aplica o estilo pra qualquer um que bater. O bloco acima vira:
:is(header, footer, nav) a { color: blue; text-decoration: none;}Uma linha, mesmo resultado. O navegador interpreta isso exatamente como a versão repetida — semanticamente são equivalentes.
Fica mais interessante quando você combina com pseudo-classes:
/* Antes — repetindo pra cada estado */button:hover,button:focus,button:active { background: darkblue;}/* Depois — limpo */button:is(:hover, :focus, :active) { background: darkblue;}Ou pra estilizar headings de uma vez:
/* Todos os headings dentro de article */article :is(h1, h2, h3, h4) { font-family: Georgia, serif; line-height: 1.3;}:where() — igualzinho, mas sem peso de especificidade
Aqui mora a diferença que importa.
Especificidade em CSS é o sistema de pontuação que decide qual regra vence quando duas se contradizem. Um seletor de ID vale mais que uma classe, que vale mais que uma tag. Quando você usa :is(), a especificidade do seletor mais forte dentro dos parênteses se propaga pra regra inteira.
/* :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 */O :where() funciona igual em comportamento — mas tem especificidade zero. Sempre. Não importa o que você colocar dentro.
/* :where() — especificidade zero */:where(#header, .nav, footer) a { color: blue;}/* ↑ fácil de sobrescrever — qualquer seletor de classe ou tag já ganha */Na prática isso significa que :where() é ideal pra estilos base e resets — você define sem travar o desenvolvedor que vai customizar depois.
A diferença na vida real
/* 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 *//* base.css */:where(article, section) p { color: gray;}/* theme.css — funciona */p { color: black;}/* :where() tem especificidade zero qualquer p ganha na sobreposição */Um exemplo que junta os dois
Num design system, você usaria os dois em camadas diferentes:
/* 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;}

