Escolher uma sintaxe para aninhamento de CSS

Duas sintaxes conflitantes precisam de sua ajuda para determinar qual deve ser defendida por um candidato à especificação.

Adam argyle
Adam Argyle
Miriam suzanne
Miriam Suzanne

O aninhamento de CSS é uma adição de sintaxe de conveniência que permite a inclusão de CSS dentro de um conjunto de regras. Se você já usou SCSS, Menos ou Stylus, provavelmente já conheceu algumas variações:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Depois de ser compilado para o CSS normal pelo pré-processador, ele se transforma em CSS normal da seguinte forma:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Uma versão CSS oficial dessa sintaxe está sendo muito considerada, e queremos usar a ajuda da comunidade para quebrar o empate. No restante desta postagem, apresentaremos as opções de sintaxe para que você se sinta confiante para responder a 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 poder ser usada no estado em que se encontra:

  1. Análise ambígua
    Alguns seletores aninhados podem se parecer exatamente com propriedades e pré-processadores que podem ser resolvidos e gerenciados no tempo de build. Os mecanismos de navegador não terão as mesmas affordances e os seletores nunca devem ser interpretados com flexibilidade.

  2. Conflitos de análise de pré-processador
    A maneira do CSS de aninhamento não pode interromper pré-processadores nem fluxos de trabalho de aninhamento do desenvolvedor. Isso seria prejudicial e desrespeitoso para esses ecossistemas e comunidades.

  3. Aguardando :is()
    O aninhamento básico não precisa de :is(), mas o aninhamento mais complexo precisa. Consulte o Exemplo 3 para ver uma breve introdução às listas de seletores e ao aninhamento. Imagine que a lista de seletores estava no meio de um seletor, e não 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 do CSS corretamente e, nesse espírito, estamos incluindo a comunidade. As seções a seguir descrevem as três versões possíveis que estamos avaliando. Depois, examinaremos alguns exemplos de uso para comparação e, no final, haverá uma pequena pesquisa perguntando qual você prefere no geral.

Opção 1: @nest

Essa é a sintaxe atual especificada em CSS Nesting 1. Ele oferece uma maneira conveniente de aninhar estilos de anexação iniciando novos seletores aninhados com &. Ela 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 precisa lembrar de @nest ou &, dependendo do caso de uso.

Opção 2: restrição com @nest

Essa é uma alternativa mais rigorosa, em uma tentativa de reduzir a despesa mencionada de lembrar dois métodos de aninhamento. Essa sintaxe restrita permite apenas o aninhamento após @nest. Portanto, não há padrão de conveniência apenas para anexação. remover a ambiguidade da escolha, criar uma maneira fácil de lembrar de aninhar, mas sacrificar a falta de tempo em favor da convenção.

Opção 3: colchetes

Para evitar a sintaxe dupla ou a confusão envolvida nas propostas @nest, Miriam Suzanne e Elika Etemad propuseram uma sintaxe alternativa que usa chaves extras. Isso proporciona clareza de sintaxe, com apenas dois caracteres extras e nenhuma nova regra a. Ele também permite que as regras aninhadas sejam agrupadas pelo tipo de aninhamento necessário, como uma forma de simplificar vários seletores aninhados de maneira semelhante.

Exemplo 1: aninhamento direto

@nest

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest sempre

.foo {
  color: #111;

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

colchetes

.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;
  }
}

colchetes

.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;
  }
}

colchetes

.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;
    }
  }
}

colchetes

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 dos pais ou mudança de assunto

@nest

.foo {
  color: red;

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

@nest sempre

.foo {
  color: red;

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

colchetes

.foo {
  color: red;

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

CSS equivalente

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Exemplo 6: mistura de aninhamento direto e de familiar responsável

@nest

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest sempre

.foo {
  color: blue;

  @nest .bar & {
    color: red;

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

colchetes

.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 consulta de mídia

@nest

.foo {
  display: grid;

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

ou explicitamente / estendida

.foo {
  display: grid;

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

@nest sempre (é sempre explícito)

.foo {
  display: grid;

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

colchetes

.foo {
  display: grid;

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

ou explicitamente / estendida

.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 de aninhamento

@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;
    }
  }
}

colchetes

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 "pia de cozinha"

@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;
  }
}

colchetes

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

Espero que você considere uma comparação justa e um exemplo das opções de sintaxe que estamos avaliando. Analise-os com atenção e informe sua preferência abaixo. Agradecemos sua ajuda para aprimorar o aninhamento de CSS para uma sintaxe que todos conheceremos e gostaremos.

Participe da pesquisa