Guida alla scelta di una sintassi per la nidificazione CSS

Due sintassi in competizione hanno bisogno del tuo aiuto per determinare quale deve essere promossa come candidato alla specifica.

L'annidamento CSS è un'aggiunta di sintassi conveniente che consente di aggiungere CSS all'interno di un insieme di regole. Se hai utilizzato SCSS, Less o Stylus, allora avrai sicuramente visto alcune varianti di questo:

.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

che, dopo essere stato compilato in CSS normale dal preprocessor, si trasforma in CSS normale come questo:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Stiamo prendendo in seria considerazione una versione CSS ufficiale di questa sintassi e abbiamo una preferenza divisa che vorremmo risolvere con l'aiuto della community. Il resto di questo post introdurrà le opzioni di sintassi, così potrai rispondere a un sondaggio alla fine.

Perché l'esempio di nidificazione esatto mostrato sopra non può essere la sintassi per la nidificazione CSS?

Esistono alcuni motivi per cui la sintassi di nidificazione più comune non può essere utilizzata così com'è:

  1. Analisi ambigua
    Alcuni selettori nidificati possono avere l'aspetto esatto di proprietà e preprocessor sono in grado di risolverli e gestirli in fase di compilazione. I motori del browser non avranno le stesse funzionalità, i selettori non devono mai essere interpretati in modo approssimativo.

  2. Conflitti di analisi del preprocessor
    Il modo di nidificare il CSS non deve interrompere i preprocessor o i flussi di lavoro di nidificazione esistenti degli sviluppatori. Questo comportamento sarebbe distruttivo e scortese nei confronti di questi ecosistemi e community.

  3. In attesa di :is()
    L'annidamento di base non richiede :is(), ma quello più complesso sì. Consulta l'esempio n. 3 per una breve introduzione agli elenchi di selettori e all'annidamento. Immagina che l'elenco dei selettori si trovi al centro di un selettore anziché all'inizio. In questi casi, :is() è obbligatorio per raggruppare i selettori al centro di un altro selettore.

Panoramica di ciò che stiamo confrontando

Vogliamo implementare correttamente l'annidamento CSS e, in questo spirito, includiamo la community. Le sezioni seguenti descrivono le tre possibili versioni che stiamo valutando. Poi esamineremo alcuni esempi di utilizzo per il confronto e alla fine ci sarà un breve sondaggio in cui ti verrà chiesto quale preferisci in generale.

Opzione 1: @nest

Questa è la sintassi specificata attualmente in CSS Nesting 1. Offre un modo pratico per nidificare gli stili di aggiunta iniziando nuovi selettori nidificati con &. Offre anche @nest come modo per inserire il contesto & ovunque all'interno di un nuovo selettore, ad esempio quando non stai solo aggiungendo soggetti. È flessibile e minimale, ma a scapito della necessità di ricordare @nest o & a seconda del caso d'uso.

Opzione 2: @nest restricted

Si tratta di un'alternativa più rigorosa, nel tentativo di ridurre la spesa menzionata per ricordare due metodi di nidificazione. Questa sintassi limitata consente l'annidamento solo dopo @nest, quindi non esiste un pattern di convenienza di sola aggiunta. Rimozione dell'ambiguità della scelta, creazione di un modo facile da ricordare per nidificare, ma sacrifici la concisione a favore della convenzione.

Opzione 3: parentesi

Per evitare la doppia sintassi o l'ingombro aggiuntivo delle proposte @nest, Miriam Suzanne ed Elika Etemad hanno proposto una sintassi alternativa che si basa invece su parentesi graffe aggiuntive. In questo modo la sintassi è più chiara, con solo due caratteri aggiuntivi e nessuna nuova regola @. Consente inoltre di raggruppare le regole nidificate in base al tipo di nidificazione richiesto, in modo da semplificare più selettori nidificati in modo simile.

Esempio 1 - Nidificazione diretta

@nest

.foo {
  color: #111;

  & .bar {
    color: #eee;
  }
}

@nest always

.foo {
  color: #111;

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

parentesi

.foo {
  color: #111;

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

CSS equivalente

.foo {
  color: #111;
}

.foo .bar {
  color: #eee;
}

Esempio 2 - Nidificazione composta

@nest

.foo {
  color: blue;

  &.bar {
    color: red;
  }
}

@nest always

.foo {
  color: blue;

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

parentesi

.foo {
  color: blue;

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

CSS equivalente

.foo {
  color: blue;
}

.foo.bar {
  color: red;
}

Esempio 3: elenchi di selettori e nidificazione

@nest

.foo, .bar {
  color: blue;

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

@nest always

.foo, .bar {
  color: blue;

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

parentesi

.foo, .bar {
  color: blue;

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

CSS equivalente

.foo, .bar {
  color: blue;
}

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

Esempio 4: più livelli

@nest

figure {
  margin: 0;

  & > figcaption {
    background: lightgray;

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

@nest always

figure {
  margin: 0;

  @nest & > figcaption {
    background: lightgray;

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

parentesi

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

Esempio 5: nidificazione dei genitori o modifica dell'oggetto

@nest

.foo {
  color: red;

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

@nest always

.foo {
  color: red;

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

parentesi

.foo {
  color: red;

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

CSS equivalente

.foo {
  color: red;
}

.parent .foo {
  color: blue;
}

Esempio 6: combinazione di nidificazione diretta e nidificazione principale

@nest

.foo {
  color: blue;

  @nest .bar & {
    color: red;

    &.baz {
      color: green;
    }
  }
}

@nest always

.foo {
  color: blue;

  @nest .bar & {
    color: red;

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

parentesi

.foo {
  color: blue;

  {
    .bar & {
      color: red;

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

CSS equivalente

.foo {
  color: blue;
}

.bar .foo {
  color: red;
}

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

Esempio 7: nidificazione delle media query

@nest

.foo {
  display: grid;

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

o esplicito / esteso

.foo {
  display: grid;

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

@nest always (is always explicit)

.foo {
  display: grid;

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

parentesi

.foo {
  display: grid;

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

o esplicito / esteso

.foo {
  display: grid;

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

CSS equivalente

.foo {
  display: grid;
}

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

Esempio 8: gruppi nidificati

@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 always

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

parentesi

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

Esempio 9: gruppo di nidificazione complesso "Lavello"

@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 always

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

parentesi

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

È ora di votare

Ci auguriamo che tu ritenga che questo sia un confronto equo e un assaggio delle opzioni di sintassi che stiamo valutando. Esaminali attentamente e facci sapere quale preferisci qui sotto. Ti ringraziamo per averci aiutato a far progredire il nesting CSS verso una sintassi che tutti impareremo a conoscere e ad amare.

Partecipa al sondaggio