Transição de CSS

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

Antes do aninhamento, cada seletor precisava ser declarado explicitamente, separadamente. Isso leva à repetição, ao aumento do tamanho da folha 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 aninhamento, os seletores podem ser continuados e as regras de estilo relacionadas a eles podem ser agrupadas.

Depois
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Teste isso no navegador.

O aninhamento ajuda os desenvolvedores a reduzir a necessidade de repetir seletores e também a 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 fosse removido do projeto, você poderia excluir todo o grupo em vez de pesquisar arquivos para instâncias de seletor relacionadas.

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

O aninhamento está disponível no Chrome 112 e também para teste no Safari Technical Preview 162.

Como começar a usar o aninhamento de CSS

No restante desta postagem,a seguinte caixa de areia de demonstração é usada 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 vê-la em ação.

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

Dentro da caixa de areia, há círculos, triângulos e quadrados. Alguns são pequenos, médios ou grandes. Outros são azuis, rosa ou roxos. Todos estão dentro do elemento contido .demo. A seguir, há 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 de CSS permite definir estilos para um elemento no contexto de outro seletor.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

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

Esse exemplo também pode ser escrito usando o símbolo &, para indicar explicitamente onde a classe pai 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.

Selecionar os círculos

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

Sem aninhamento, CSS 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 estão 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.
Testar uma demonstração

Selecionar triângulos e quadrados

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

Sem aninhamento, CSS hoje, há 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 fica apenas com círculos, e todas as outras formas ficam quase invisíveis.
Testar uma demonstração

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, CSS 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.
Testar uma demonstração
Dica profissional com seletores compostos e aninhamento

O símbolo & é seu amigo aqui, porque mostra explicitamente como unir seletores aninhados. Confira o exemplo abaixo:

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

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

Selecionar todas as formas, exceto as rosas

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

Sem aninhamento, CSS 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 rosas estão ocultas dentro de .demo:

A grade colorida agora é monocromática e mostra apenas formas rosas.
Testar uma demonstração
Precisão e flexibilidade com &

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

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

Isso combina .demo e :not() para .demo:not(), em vez 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 de CSS para aninhamento está repleta de mais exemplos. Se você quiser saber mais sobre a sintaxe usando exemplos, ela abrange uma ampla variedade de exemplos válidos e inválidos.

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

Aninhamento @media

Pode ser muito confuso mudar para uma área diferente da folha de estilo para encontrar condições de consulta de mídia que modificam um seletor e seus estilos. Essa distração desaparece 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 do contexto do seletor atual, uma sintaxe mínima poderá ser usada.

.card {
  font-size: 1rem;

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

O uso de & explicitamente também pode ser usado:

.card {
  font-size: 1rem;

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

Este exemplo mostra a sintaxe expandida com &, ao mesmo tempo em que segmenta cartões .large para demonstrar que outros recursos de aninhamento continuam funcionando.

Saiba mais sobre as @regras de aninhamento.

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 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 seja um pouco inútil, certamente há cenários em que é útil repetir um contexto de seletor.

Exemplos de aninhamento inválidos

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

Aninhamento e concatenação

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

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

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

Exemplo de aninhamento complicado

Aninhamento em listas de seletores e :is()

Considere o seguinte bloco de CSS de aninhamento:

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

Este é o primeiro exemplo que começa com uma lista de seletores e continua a aninhar ainda mais. Os exemplos anteriores só terminaram 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 o aninhamento em listas de seletores, especialmente aquelas que incluem um seletor de ID.

Para que a intent do aninhamento funcione, qualquer lista de seletores que não seja o aninhamento mais interno será encapsulada com :is() pelo navegador. Esse encapsulamento mantém o agrupamento da lista de seletores em qualquer contexto criado. O efeito colateral desse agrupamento, :is(.one, #two), é que ele adota a especificidade da pontuação mais alta nos seletores entre parênteses. É assim que :is() sempre funciona, mas pode ser uma surpresa ao usar a sintaxe de aninhamento, porque não é exatamente o que foi criado. O truque resumido: o aninhamento com IDs e listas de seletores pode levar a seletores de especificidade muito alta.

Para recapitular claramente o exemplo complicado, o bloco de aninhamento anterior será aplicado ao documento da seguinte maneira:

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

Fique atento ou ensine seus linters a avisar quando o aninhamento estiver dentro de uma lista de seletores que usa um seletor de ID. A especificidade de todo o aninhamento nessa lista de seletores será alta.

Como misturar aninhamento e declarações

Considere o seguinte bloco de CSS de aninhamento:

.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 a parte de cima, como se tivessem sido criadas antes de qualquer aninhamento. Mais detalhes podem ser encontrados em a especificação.

Há maneiras de contornar isso. O seguinte encapsula 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, é uma boa prática encapsular 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 ótimas maneiras de detectar o aninhamento de CSS: usar o aninhamento ou usar @supports para verificar a capacidade de análise do seletor de aninhamento.

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

Usando o aninhamento:

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

  .no-nesting {
    display: none;
  }
}

Usando @supports:

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

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

Depuração com o Chrome DevTools

O suporte atual no DevTools para aninhamento é mínimo. No momento, você vai encontrar estilos representados no painel "Estilos" conforme esperado, mas o rastreamento do aninhamento e do contexto completo do seletor ainda não é compatível. 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 suporte adicional para aninhamento de CSS. Fique por dentro das novidades.

O futuro

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

O aninhamento é uma grande melhoria na linguagem CSS. Ele tem implicações de criação para quase todos os aspectos arquitetônicos do CSS. Esse grande impacto precisa ser profundamente explorado e compreendido antes que a versão 2 possa ser especificada de maneira eficaz.

Como um pensamento final, aqui está uma demonstração que usa @scope, aninhamento e @layer juntos. É tudo muito emocionante!

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 cyberpunk.