Pular para o conteúdo
~/.primo-academy.sh
☰ Aulas · CSS · 0/36
Recomendado: essencial

:has(): o Seletor que 'Sobe' pro Pai

2 min de leitura

fonte

Por anos, CSS foi estritamente unidirecional: o seletor olha pra filho ou irmão, nunca pro pai. Você sempre precisou de JavaScript pra "se o pai tem X, faça Y" - tipo "se a <form> tem campo inválido, mude a cor do botão de submit".

:has() mudou isso. É o seletor pai: :has(X) significa "elemento que tem X como descendente". Suporte amplo em navegadores modernos (Chrome 105+, Safari 15.4+, Firefox 121+).

Exemplo clássico: form com erro

<form class="formulario">
  <input type="email" required value="invalido" />
  <input type="text" />
  <button>Enviar</button>
</form>
/* Se o form tem input inválido, o botão fica desabilitado visualmente */
.formulario:has(input:invalid) button {
  background: #ccc;
  cursor: not-allowed;
}

Sem JavaScript, sem classe extra, sem mexer no HTML. O CSS "percebe" que existe um input inválido dentro do form e estiliza o botão de acordo.

Card com imagem vs sem

<div class="card">
  <img src="foto.jpg" alt="" />
  <h3>Título</h3>
  <p>Descrição</p>
</div>

<div class="card">
  <h3>Só texto</h3>
  <p>Esse card não tem imagem</p>
</div>
/* Cards com imagem ocupam mais espaço vertical */
.card:has(img) {
  grid-template-rows: 200px auto auto;
}

/* Cards sem imagem são mais compactos */
.card:not(:has(img)) {
  grid-template-rows: auto auto;
}

Layout que muda baseado em irmãos

<article>
  <h1>Título</h1>
  <img src="capa.jpg" alt="" />
  <p>Texto do artigo...</p>
</article>
/* Se tem imagem, o h1 vai em cima e a imagem em baixo com tamanho cheio */
article:has(img) h1 {
  margin-bottom: 16px;
}

Estilizar inputs baseado no estado deles

/* Label de campo obrigatório fica vermelha se o campo está vazio e
   teve foco e perdeu o foco sem preencher (touched) */
.campo:has(input:user-invalid) .label {
  color: #d93025;
}

:user-invalid é uma pseudo-classe moderna (ainda em adoção) que diferencia "inválido por causa de validação nativa" de "inválido porque o usuário digitou errado e perdeu o foco". Combinar com :has() cobre o caso sem JavaScript.

Layout condicional a partir do conteúdo

<section class="lista-de-posts">
  <article>...</article>
  <article>...</article>
  <article>...</article>
</section>

<section class="lista-de-posts">
  <!-- vazia -->
</section>
/* Se a lista tem posts, layout em grid */
.lista-de-posts:has(article) {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
}

/* Se está vazia, mostra mensagem centralizada */
.lista-de-posts:not(:has(article))::before {
  content: "Nenhum post ainda";
  text-align: center;
  color: #999;
  display: block;
  padding: 48px;
}

O ::before aparece quando a lista está vazia. Puro CSS.

:has() dentro de :has() - aninhamento

/* Card que tem um botão desabilitado dentro */
.card:has(button:disabled) {
  opacity: 0.6;
}

Você pode aninhar quantos precisar. Cada nível é uma seleção adicional.

:has(*:not(...)) - "tem algo que não é X"

/* Header que tem APENAS logo (sem menu) */
.header:not(:has(nav)) {
  justify-content: center;  /* centraliza o logo */
}

Limitações e cuidado

Performance: :has() é mais "caro" que seletores simples, porque o navegador precisa olhar os descendentes de cada candidato. Em listas enormes (milhares de itens) com :has() em cada um, pode haver lentidão. Na prática, em 99% dos casos, é imperceptível.

Suporte: navegadores antigos (pré-2021) não entendem. Para projetos que precisam suportar IE ou Safari muito velho, ter fallback é importante.

  • :has(X) seleciona o elemento que tem X como descendente.
  • Combina com tudo: :has(), :not(:has()), :has(:has()).
  • Substitui muito JavaScript: validação de form, layout condicional, estados de UI.
  • Funciona com pseudo-classes (:invalid, :checked, :user-invalid).
  • Performance boa em geral; aninhar muito é o único cuidado.

Dica: pense em :has() como "if em CSS". É a primeira vez que o CSS tem condicional real ("se este elemento tem X, então Y"). É tão transformador quanto Flexbox foi em 2010.

No próximo nó, vamos falar de logical properties - a forma moderna de CSS que respeita a direção de escrita e deixa seu código pronto para internacionalização.

// recursos

// avaliação da trilha

ainda sem avaliações