Transição de CSS

Um dos nossos recursos favoritos do pré-processador CSS agora está integrado à linguagem: regras de estilo aninhadas.

Antes do aninhamento, todos os seletores precisavam ser declarados explicitamente, separadamente uns dos outros. Isso leva à repetição, ao volume de folhas de estilo e a uma experiência de criação dispersa.

Antes
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

Depois do anilhamento, os seletores podem ser continuados, e as regras de estilo relacionadas podem ser agrupadas.

Depois
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Teste no navegador.

O aninhamento ajuda os desenvolvedores a reduzir a necessidade de repetir os seletores, além de co-localizar regras de estilo para elementos relacionados. Ele também pode ajudar os estilos a corresponder ao HTML de destino. Se o componente .nesting no exemplo anterior foi removido do projeto, você pode excluir o grupo inteiro em vez de pesquisar arquivos para instâncias de seletor relacionadas.

O aninhamento pode ajudar com: - Organização - Redução do tamanho do arquivo - Refactoring

O aninhamento está disponível no Chrome 112 e também pode ser testado na prévia técnica 162 do Safari.

Introdução ao aninhamento de CSS

No restante desta postagem,o sandbox de demonstração a seguir é usado para ajudar você a visualizar as seleções. Nesse estado padrão, nada é selecionado e tudo fica visível. Ao selecionar as várias formas e tamanhos, você pode praticar a sintaxe e conferir como ela funciona.

Uma grade colorida de círculos, triângulos e quadrados grandes e pequenos.

Dentro do sandbox, há círculos, triângulos e quadrados. Alguns são pequenos, médios ou grandes. Outras são azuis, rosas ou roxas. Eles estão todos dentro do elemento .demo. Confira abaixo uma prévia dos elementos HTML que você vai segmentar.

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

Exemplos de aninhamento

O aninhamento do CSS permite definir estilos para um elemento no contexto de outro seletor.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

Neste exemplo, o seletor de classe .child está aninhado no seletor de classe .parent. Isso significa que o seletor .child aninhado só será aplicado a elementos que são filhos de elementos com uma classe .parent.

Esse exemplo pode ser escrito usando o símbolo & para indicar explicitamente onde a classe mãe precisa ser colocada.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Esses dois exemplos são funcionalmente equivalentes, e o motivo de você ter opções ficará mais claro à medida que exemplos mais avançados forem explorados neste artigo.

Como selecionar os círculos

Neste primeiro exemplo, a tarefa é adicionar estilos para desbotar e desfocar apenas os círculos dentro da demonstração.

Sem aninhamento, o CSS de hoje:

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

Com aninhamento, há duas maneiras válidas:

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

ou

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

O resultado: todos os elementos dentro de .demo com uma classe .circle ficam desfocados e quase invisíveis:

A grade colorida de formas não tem mais círculos,
    eles estão muito fracos no plano de fundo.
Teste uma demonstração

Selecionando qualquer triângulo e quadrado

Essa tarefa exige a seleção de vários elementos aninhados, também chamados de seletor de grupo.

Sem aninhamento, o CSS tem duas maneiras:

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

ou usando :is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

Com aninhamento, há duas maneiras válidas:

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

ou

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

O resultado: apenas os elementos .circle permanecem dentro de .demo:

A grade colorida de formas é deixada com apenas círculos,
    todas as outras formas são quase invisíveis.
Teste uma demonstração

Como selecionar triângulos e círculos grandes

Essa tarefa exige um seletor composto, em que os elementos precisam ter as duas classes presentes para serem selecionados.

Sem aninhamento, o CSS de hoje:

.demo .lg.triangle,
.demo .lg.square {
  opacity: .25;
  filter: blur(25px);
}

ou

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

Com aninhamento, há duas maneiras válidas:

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

ou

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

O resultado: todos os triângulos e círculos grandes estão ocultos dentro de .demo:

A grade colorida só tem formas pequenas e médias visíveis.
Teste uma demonstração
Dica profissional com seletores compostos e aninhamento

O símbolo & é seu amigo aqui, porque mostra explicitamente como anexar seletores anexados. Veja o exemplo a seguir:

.demo {
  .lg {
    .triangle,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

Embora seja uma maneira válida de aninhar, os resultados não correspondem aos elementos esperados. O motivo é que, sem & para especificar o resultado desejado de .lg.triangle, .lg.circle compostos, o resultado real seria .lg .triangle, .lg .circle; seletores descendentes.

Selecionando todas as formas, exceto as rosas

Essa tarefa requer uma pseudoclasse funcional de negação, em que os elementos não precisam ter o seletor especificado.

Sem aninhamento, o CSS de hoje:

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

Com aninhamento, há duas maneiras válidas:

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

ou

.demo {
  & :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

O resultado: todas as formas que não são rosa são ocultas dentro de .demo:

A grade colorida agora é monocromática, mostrando apenas formas rosa.
Teste uma demonstração
Precisão e flexibilidade com &

Digamos que você queira segmentar .demo com o seletor :not(). & é obrigatório para isso:

.demo {
  &:not() {
    ...
  }
}

Esse composto une .demo e :not() a .demo:not(), ao contrário do exemplo anterior, que precisava de .demo :not(). Esse lembrete é muito importante quando você quer aninhar uma interação :hover.

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

Mais exemplos de aninhamento

A especificação do CSS para aninhamento tem mais exemplos. Se você quiser saber mais sobre a sintaxe com exemplos, ele abrange uma ampla gama de exemplos válidos e inválidos.

Os próximos exemplos vão apresentar brevemente um recurso de aninhamento do CSS para ajudar você a entender a amplitude dos recursos que ele apresenta.

Aninhamento de @media

Pode ser muito cansativo mudar para uma área diferente da folha de estilo para encontrar condições de consulta de mídia que modificam um seletor e os estilos dele. Essa distração foi eliminada com a capacidade de aninhar as condições diretamente no contexto.

Para conveniência da sintaxe, se a consulta de mídia aninhada estiver modificando apenas os estilos para o contexto de seletor atual, uma sintaxe mínima poderá ser usada.

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

O uso explícito de & também pode ser usado:

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

Este exemplo mostra a sintaxe expandida com & e também segmenta cards .large para demonstrar que outros recursos de aninhamento continuam funcionando.

Saiba mais sobre anilhamento de @rules.

Aninhamento em qualquer lugar

Todos os exemplos até este ponto continuaram ou foram anexados a um contexto anterior. Você pode mudar ou reorganizar completamente o contexto, se necessário.

.card {
  .featured & {
    /* .featured .card */
  }
}

O símbolo & representa uma referência a um objeto de seletor (não uma string) e pode ser colocado em qualquer lugar em um seletor aninhado. Ele pode até ser colocado várias vezes:

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

Embora esse exemplo pareça inútil, há cenários em que repetir um contexto de seletor é útil.

Exemplos de aninhamento inválido

Há alguns cenários de sintaxe de aninhamento que são inválidos e podem surpreender você se você estiver aninhando em pré-processadores.

Anilhamento e concatenação

Muitas convenções de nomenclatura de classe CSS contam com a capacidade de aninhamento de concatenar ou anexar seletores como se fossem strings. Isso não funciona no aninhamento de CSS, porque os seletores não são strings, são referências de objetos.

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

Uma explicação mais detalhada pode ser encontrada na especificação.

Exemplo de aninhamento complicado

Anilhamento em listas de seletores e :is()

Considere o seguinte bloco de CSS aninhado:

.one, #two {
  .three {
    /* some styles */
  }
}

Este é o primeiro exemplo que começa com uma lista de seletores e continua aninhando. Os exemplos anteriores terminavam com uma lista de seletores. Não há nada inválido neste exemplo de aninhamento, mas há um detalhe de implementação potencialmente complicado sobre aninhamento em listas de seletores, especialmente aqueles que incluem um seletor de ID.

Para que a intenção do aninhamento funcione, qualquer lista de seletor que não seja o aninhamento mais interno será embrulhada com :is() pelo navegador. Esse agrupamento mantém o agrupamento da lista de seletores em todos os contextos criados. O efeito colateral desse agrupamento, :is(.one, #two), é que ele adota a especificidade da maior pontuação entre os seletores entre parênteses. É assim que o :is() sempre funciona, mas pode ser uma surpresa quando se usa a sintaxe de aninhamento porque não é exatamente o que foi criado. Resumo do truque: aninhar com IDs e listas de seletores pode levar a seletores de especificidade muito altos.

Para recapitular claramente o exemplo complicado, o bloco de aninhamento anterior será aplicado ao documento desta forma:

:is(.one, #two) .three {
  /* some styles */
}

Fique de olho ou ensine seus linters a alertar quando houver aninhamento dentro de uma lista de seletores que usa um seletor de ID. A especificidade de todo o aninhamento dentro dessa lista de seletores será alta.

Como misturar aninhamento e declarações

Considere o seguinte bloco de CSS aninhado:

.card {
  color: green;
  & { color: blue; }
  color: red;
}

A cor dos elementos .card será blue.

Todas as declarações de estilo misturadas são elevadas para o topo, como se tivessem sido criadas antes de qualquer aninhamento. Confira mais detalhes na especificação.

Há maneiras de contornar isso. O código a seguir agrupa os três estilos de cor em &, que mantém a ordem em cascata conforme o autor pretendia. A cor dos elementos .card será vermelha.

.card {
  color: green;
  & { color: blue; }
  & { color: red; }
}

Na verdade, é recomendável agrupar todos os estilos que seguem o aninhamento com um &.

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

Detecção de recursos

Há duas maneiras de detectar o aninhamento de CSS: use o aninhamento ou use @supports para verificar o recurso de análise de seletor de aninhamento.

Uma captura de tela da demonstração do Codepen da Bramus, perguntando se o navegador oferece suporte
  ao aninhamento de CSS. Abaixo dessa pergunta, há uma caixa verde, indicando suporte.

Como usar a anidação:

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

Utilizando @supports:

@supports (selector(&)) {
  /* nesting parsing available */
}

Meu colega Bramus tem um ótimo Codepen mostrando essa estratégia.

Depurar com o Chrome DevTools

O suporte atual nas DevTools para aninhamento é mínimo. Atualmente, os estilos são representados no painel de estilos como esperado, mas o rastreamento do aninhamento e o contexto completo do seletor ainda não são compatíveis. Temos um design e planos para tornar isso transparente e claro.

Captura de tela da sintaxe de aninhamento do Chrome DevTools.

O Chrome 113 planeja ter mais suporte para aninhamento de CSS. Fique por dentro das novidades.

O futuro

O aninhamento de CSS está disponível apenas na versão 1. A versão 2 vai introduzir mais açúcar sintático e, possivelmente, menos regras para memorizar. Há muita demanda para que a análise de aninhamento não seja limitada ou tenha momentos complicados.

O aninhamento é uma grande melhoria na linguagem CSS. Ele tem implicações de autoria em quase todos os aspectos de arquitetura do CSS. Esse grande impacto precisa ser profundamente analisado e compreendido antes que a versão 2 possa ser especificada.

Por fim, confira uma demonstração que usa @scope, aninhamento e @layer juntos. É tudo muito empolgante!

Um cartão claro em um fundo cinza. O card tem um título e texto,
  alguns botões de ação e uma imagem no estilo punk cibernético.