Transição de CSS

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

Adam argyle
Adam Argyle

Antes do aninhamento, cada seletor precisava ser declarado explicitamente, separadamente um do outro. Isso leva à repetição, a uma folha de estilo em massa e a uma experiência de criação esparsa.

Antes
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Após o aninhamento, os seletores podem ser mantidos, e as regras de estilo relacionadas a eles podem ser agrupadas.

Depois
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Tente fazer isso no navegador.

O aninhamento ajuda os desenvolvedores ao reduzir a necessidade de repetir seletores e colocalizar regras de estilo para elementos relacionados. Isso também ajuda os estilos a corresponder ao HTML segmentado. Se o componente .nesting no exemplo anterior tiver sido removido do projeto, exclua todo o grupo em vez de pesquisar instâncias de seletor relacionadas nos arquivos.

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

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

Começar a transição do 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, é possível praticar a sintaxe e vê-la em ação.

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

Dentro da sandbox existem círculos, triângulos e quadrados. Alguns são pequenos, médios ou grandes. Outras são azul, rosa ou roxo. Todos eles estão dentro do elemento que contém .demo. Esta é uma visualização dos elementos HTML que você 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 seletor de classe .child está aninhado no seletor de classe .parent. Isso significa que o seletor aninhado .child será aplicado apenas a elementos filhos de elementos com uma classe .parent.

Esse exemplo poderia 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 das opções ficará mais claro à medida que exemplos mais avançados forem explorados neste artigo.

Como selecionar os círculos

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

Sem aninhamento, o CSS hoje:

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

Com o 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);
  }
}

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 são muito fracos em segundo plano.
Teste uma demonstração

Seleção de triângulos e quadrados

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

Sem aninhamento, atualmente, há duas maneiras de usar o CSS:

.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 o aninhamento, estas são duas maneiras válidas:

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

ou

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

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

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

Como selecionar triângulos e círculos grandes

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

Sem aninhamento, o 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 o aninhamento, estas são 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 agrupar seletores aninhados. Confira 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 corresponderão aos elementos esperados. Isso acontece porque, 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 de negação funcional, em que os elementos não podem ter o seletor especificado.

Sem aninhamento, o CSS hoje:

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

Com o aninhamento, estas são 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 ficam ocultas dentro de .demo:

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

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

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

Isso composto .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 CSS para aninhamento está repleta de mais exemplos. Para saber mais sobre a sintaxe usando exemplos, confira uma ampla variedade de exemplos válidos e inválidos.

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

Aninhamento de @media

Pode ser uma distração ir para uma área diferente da folha de estilo para encontrar condições de consulta de mídia que modifiquem um seletor e os estilos dele. Essa distração é perdida com a capacidade de aninhar as condições dentro do contexto.

Para facilitar a sintaxe, se a consulta de mídia aninhada estiver apenas modificando 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 cards .large para demonstrar que outros recursos de aninhamento continuam funcionando.

Saiba mais sobre aninhar @rules.

Aninhamento em qualquer lugar

Todos os exemplos até este ponto continuaram ou anexados a um contexto anterior. É possível 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 pareça inútil, certamente há cenários em que poder repetir um contexto de seletor é útil.

Exemplos de aninhamento inválidos

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

Aninhamento e concatenação

Muitas convenções de nomenclatura de classes CSS contam com a capacidade do 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, mas são referências de objeto.

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

Confira uma explicação mais detalhada na especificação.

Exemplo de aninhamento complicado

Aninhamento em listas de seletores e :is()

Considere este bloco de CSS de aninhamento:

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

Este é o primeiro exemplo que começa com uma lista de seletores e continua aninhando. Os exemplos anteriores só terminavam com uma lista de seletores. Não há nada inválido nesse exemplo de aninhamento, mas há um detalhe de implementação potencialmente complicado sobre aninhar 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á unida com :is() pelo navegador. Esse encapsulamento mantém o agrupamento da lista de seletores em qualquer contexto de criação. O efeito colateral desse agrupamento, :is(.one, #two), é que ele adota a especificidade da pontuação mais alta dentro dos seletores dentro dos 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. Resumindo: o aninhamento com IDs e listas de seletores pode levar a seletores com uma especificidade muito alta.

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

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

Fique de olho ou ensine os lints a avisar ao aninhar em uma lista de seletores que usa um seletor de ID. A especificidade de todo o aninhamento nessa lista vai ser alta.

Como misturar aninhamento e declarações

Considere este bloco de CSS de aninhamento:

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

A cor dos elementos .card vai 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. Confira mais detalhes na especificação.

Há formas de evitar isso. O exemplo a seguir encapsula os três estilos de cores em &, o que mantém a ordem em cascata da forma que o autor pretendeu. A cor dos elementos .card será vermelha.

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

Na verdade, é recomendável unir 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: use o aninhamento ou @supports para conferir a capacidade de análise do seletor de aninhamento.

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

Como usar o aninhamento:

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

  .no-nesting {
    display: none;
  }
}

Utilizando @supports:

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

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

Como depurar com o Chrome DevTools

O suporte ao aninhamento no DevTools é mínimo. Atualmente, os estilos são representados no painel "Estilos" conforme esperado, mas ainda não há suporte para rastrear o aninhamento e o contexto completo do seletor. Temos 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 o aninhamento de CSS. Fique ligado.

O futuro

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

O aninhamento é uma grande melhoria da linguagem CSS. Isso tem implicações na criação em quase todos os aspectos arquitetônicos do CSS. Esse grande impacto precisa ser profundamente explorado e entendido antes que a versão 2 possa ser efetivamente especificada.

Para refletir, veja uma demonstração que usa @scope, aninhamento e @layer juntos. É 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 cyber punk.