Escolher uma sintaxe para aninhamento de CSS

Duas sintaxes concorrentes precisam da sua ajuda para determinar qual delas deve ser promovida a um candidato de especificação.

Adam Argyle
Adam Argyle
Miriam Suzanne
Miriam Suzanne

O aninhamento de CSS é uma adição de sintaxe conveniente que permite que o CSS seja adicionado dentro de um conjunto de regras. Se você já usou SCSS, Less ou Stylus, provavelmente já viu algumas variações disso:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Que, depois de ser compilado em CSS normal pelo pré-processador, se transforma em CSS normal assim:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Uma versão oficial do CSS dessa sintaxe está sendo considerada, e temos uma divisão de preferências que gostaríamos de usar a ajuda da comunidade para quebrar o empate. O restante desta postagem vai apresentar as opções de sintaxe para que você possa se sentir informado para fazer uma pesquisa no final.

Por que o exemplo de aninhamento exato mostrado acima não pode ser a sintaxe para aninhamento de CSS?

Há alguns motivos para a sintaxe de aninhamento mais conhecida não ser usada como está:

  1. Análise ambígua
    Alguns seletores aninhados podem parecer exatamente como propriedades e pré-processadores e podem resolvê-los e gerenciá-los no momento da criação. Os mecanismos do navegador não terão as mesmas características, e os seletores nunca poderão ser interpretados de forma imprecisa.

  2. Conflitos de análise de pré-processadores
    A maneira CSS de aninhar não pode quebrar os pré-processadores ou os fluxos de trabalho de aninhamento de desenvolvedores. Isso seria perturbador e desconsiderado para esses ecossistemas e comunidades.

  3. Aguardando :is()
    A aninhação básica não precisa de :is(), mas a aninhação mais complexa precisa. Consulte o Exemplo 3 para uma introdução simples às listas de seletores e ao aninhamento. Imagine que a lista de seletores estivesse no meio de um seletor em vez de no início. Nesses casos, :is() é necessário para agrupar os seletores no meio de outro seletor.

Visão geral do que estamos comparando

Queremos fazer o aninhamento de CSS corretamente, e com esse espírito, estamos incluindo a comunidade. As seções a seguir ajudarão a descrever as três versões possíveis que estamos avaliando. Em seguida, vamos analisar alguns exemplos de uso para comparação e, no final, haverá uma pesquisa rápida perguntando qual você preferiu.

Opção 1: @nest

Essa é a sintaxe especificada atualmente em Aninhamento de CSS 1. Ele oferece uma maneira conveniente de aninhar estilos de adição iniciando novos seletores aninhados com &. Ele também oferece @nest como uma maneira de colocar o contexto & em qualquer lugar dentro de um novo seletor, como quando você não está apenas anexando assuntos. Ele é flexível e mínimo, mas, em troca, você precisa lembrar de @nest ou &, dependendo do caso de uso.

Opção 2: @nest restrito

Essa é uma alternativa mais rígida, em uma tentativa de reduzir a despesa mencionada de lembrar dois métodos de aninhamento. Essa sintaxe restrita permite apenas que o aninhamento ocorra após @nest. Portanto, não há um padrão de conveniência de adição somente. Removendo a ambiguidade de escolha, criando uma maneira fácil de lembrar de aninhar, mas sacrificando a rigidez em favor da convenção.

Opção 3: colchetes

Para evitar a sintaxe dupla ou a desordem extra envolvida nas propostas de @nest, Miriam Suzanne e Elika Etemad propuseram uma sintaxe alternativa que depende de outros colchetes. Isso proporciona clareza de sintaxe, com apenas dois caracteres extras e sem novas regras de at. Ele também permite que regras aninhadas sejam agrupadas pelo tipo de anilhamento necessário, como uma forma de simplificar vários seletores aninhados de maneira semelhante.

Exemplo 1: anilhamento direto

@nest

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest sempre

.foo {
  color: #111;

  @nest & .bar {
    color: #eee;
  }
}

chaves

.foo {
  color: #111;

  {
    & .bar {
      color: #eee;
    }
  }
}

CSS equivalente

.foo {
  color: #111;
}

.foo .bar {
  color: #eee;
}

Exemplo 2: aninhamento composto

@nest

.foo {
  color: blue;

  &.bar {
    color: red;
  }
}

@nest sempre

.foo {
  color: blue;

  @nest &.bar {
    color: red;
  }
}

chaves

.foo {
  color: blue;

  {
    &.bar {
      color: red;
    }
  }
}

CSS equivalente

.foo {
  color: blue;
}

.foo.bar {
  color: red;
}

Exemplo 3: listas de seletores e aninhamento

@nest

.foo, .bar {
  color: blue;

  & + .baz,
  &.qux {
    color: red;
  }
}

@nest sempre

.foo, .bar {
  color: blue;

  @nest & + .baz,
  &.qux {
    color: red;
  }
}

chaves

.foo, .bar {
  color: blue;

  {
    & + .baz,
    &.qux {
      color: red;
    }
  }
}

CSS equivalente

.foo, .bar {
  color: blue;
}

:is(.foo, .bar) + .baz,
:is(.foo, .bar).qux {
  color: red;
}

Exemplo 4: vários níveis

@nest

figure {
  margin: 0;

  & > figcaption {
    background: lightgray;

    & > p {
      font-size: .9rem;
    }
  }
}

@nest sempre

figure {
  margin: 0;

  @nest & > figcaption {
    background: lightgray;

    @nest & > p {
      font-size: .9rem;
    }
  }
}

chaves

figure {
  margin: 0;

  {
    & > figcaption {
      background: lightgray;

      {
        & > p {
          font-size: .9rem;
        }
      }
    }
  }
}

CSS equivalente

figure {
  margin: 0;
}

figure > figcaption {
  background: hsl(0 0% 0% / 50%);
}

figure > figcaption > p {
  font-size: .9rem;
}

Exemplo 5: aninhamento de pai ou mudança de assunto

@nest

.foo {
  color: red;

  @nest .parent & {
    color: blue;
  }
}

@nest sempre

.foo {
  color: red;

  @nest .parent & {
    color: blue;
  }
}

chaves

.foo {
  color: red;

  {
    .parent & {
      color: blue;
    }
  }
}

CSS equivalente

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Exemplo 6: misturar anilhamento direto e pai

@nest

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest sempre

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    @nest &.baz {
      color: green;
    }
  }
}

chaves

.foo {
  color: blue;

  {
    .bar & {
      color: red;

      {
        &.baz {
          color: green;
        }
      }
    }
  }
}

CSS equivalente

.foo {
  color: blue;
}

.bar .foo {
  color: red;
}

.bar .foo.baz {
  color: green;
}

Exemplo 7: aninhamento de consultas de mídia

@nest

.foo {
  display: grid;

  @media (width => 30em) {
    grid-auto-flow: column;
  }
}

ou explicitamente / estendido

.foo {
  display: grid;

  @media (width => 30em) {
    & {
      grid-auto-flow: column;
    }
  }
}

@nest always (é sempre explícito)

.foo {
  display: grid;

  @media (width => 30em) {
    @nest & {
      grid-auto-flow: column;
    }
  }
}

chaves

.foo {
  display: grid;

  @media (width => 30em) {
    grid-auto-flow: column;
  }
}

ou explicitamente / estendido

.foo {
  display: grid;

  @media (width => 30em) {
    & {
      grid-auto-flow: column;
    }
  }
}

CSS equivalente

.foo {
  display: grid;
}

@media (width => 30em) {
  .foo {
    grid-auto-flow: column;
  }
}

Exemplo 8: grupos aninhados

@nest

fieldset {
  border-radius: 10px;

  &:focus-within {
    border-color: hotpink;
  }

  & > legend {
    font-size: .9em;
  }

  & > div {
    & + div {
      margin-block-start: 2ch;
    }

    & > label {
      line-height: 1.5;
    }
  }
}

@nest sempre

fieldset {
  border-radius: 10px;

  @nest &:focus-within {
    border-color: hotpink;
  }

  @nest & > legend {
    font-size: .9em;
  }

  @nest & > div {
    @nest & + div {
      margin-block-start: 2ch;
    }

    @nest & > label {
      line-height: 1.5;
    }
  }
}

chaves

fieldset {
  border-radius: 10px;

  {
    &:focus-within {
      border-color: hotpink;
    }
  }

  > {
    legend {
      font-size: .9em;
    }

    div {
      + div {
        margin-block-start: 2ch;
      }

      > label {
        line-height: 1.5;
      }
    }}
  }
}

CSS equivalente

fieldset {
  border-radius: 10px;
}

fieldset:focus-within {
  border-color: hotpink;
}

fieldset > legend {
  font-size: .9em;
}

fieldset > div + div {
  margin-block-start: 2ch;
}

fieldset > div > label {
  line-height: 1.5;
}

Exemplo 9: grupo de aninhamento complexo "Kitchen Sink"

@nest

dialog {
  border: none;

  &::backdrop {
    backdrop-filter: blur(25px);
  }

  & > form {
    display: grid;

    & > :is(header, footer) {
      align-items: flex-start;
    }
  }

  @nest html:has(&[open]) {
    overflow: hidden;
  }
}

@nest sempre

dialog {
  border: none;

  @nest &::backdrop {
    backdrop-filter: blur(25px);
  }

  @nest & > form {
    display: grid;

    @nest & > :is(header, footer) {
      align-items: flex-start;
    }
  }

  @nest html:has(&[open]) {
    overflow: hidden;
  }
}

chaves

dialog {
  border: none;

  {
    &::backdrop {
      backdrop-filter: blur(25px);
    }

    & > form {
      display: grid;

      {
        & > :is(header, footer) {
          align-items: flex-start;
        }
      }
    }
  }

  {
    html:has(&[open]) {
      overflow: hidden;
    }
  }
}

CSS equivalente

dialog {
  border: none;
}

dialog::backdrop {
  backdrop-filter: blur(25px);
}

dialog > form {
  display: grid;
}

dialog > form > :is(header, footer) {
  align-items: flex-start;
}

html:has(dialog[open]) {
  overflow: hidden;
}

Hora de votar

Esperamos que você tenha considerado essa uma comparação justa e um exemplo das opções de sintaxe que estamos avaliando. Analise com atenção e informe sua preferência abaixo. Agradecemos por nos ajudar a avançar o aninhamento de CSS para uma sintaxe que todos vamos conhecer e amar.

Responda à pesquisa!