Nidificazione dei CSS

Una delle nostre funzionalità preferite del preprocessore CSS è ora integrata nel linguaggio: le regole di stile di annidamento.

Prima dell'annidamento, ogni selettore doveva essere dichiarato esplicitamente, separatamente l'uno dall'altro. Ciò comporta ripetizioni, un foglio di stile voluminoso e un'esperienza di creazione frammentata.

Prima
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Dopo l'annidamento, i selettori possono essere continuati e le regole di stile correlate possono essere raggruppate al loro interno.

Dopo
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Prova questa funzionalità nel browser.

L'annidamento aiuta gli sviluppatori riducendo la necessità di ripetere i selettori e di collocare le regole di stile per gli elementi correlati. Può anche aiutare gli stili a corrispondere all'HTML a cui sono destinati. Se il componente .nesting nell'esempio precedente è stato rimosso dal progetto, puoi eliminare l'intero gruppo anziché cercare i file per le istanze di selettori correlate.

L'annidamento può essere utile per: - Organizzazione - Riduzione delle dimensioni dei file - Refactoring

L'annidamento è disponibile a partire da Chrome 112 e può essere provato anche in Safari Technical Preview 162.

Inizia a utilizzare l'annidamento CSS

Per il resto di questo post,viene utilizzato il seguente sandbox demo per aiutarti a visualizzare le selezioni. In questo stato predefinito, non è selezionato nulla e tutto è visibile. Selezionando le varie forme e dimensioni, puoi esercitarti con la sintassi e vederla in azione.

Una griglia colorata di cerchi, triangoli e quadrati piccoli e grandi.

All'interno del sandbox sono presenti cerchi, triangoli e quadrati. Alcuni sono piccoli, medi o grandi. Altri sono blu, rosa o viola. Sono tutti all'interno dell'elemento contenitore .demo. Di seguito è riportata un'anteprima degli elementi HTML a cui ti rivolgerai.

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

Esempi di annidamento

L'annidamento CSS ti consente di definire gli stili per un elemento nel contesto di un altro selettore.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

In questo esempio, il .child selettore di classe è annidato all'interno del .parent selettore di classe. Ciò significa che il selettore .child annidato verrà applicato solo agli elementi che sono figli di elementi con una classe .parent.

Questo esempio potrebbe essere scritto in alternativa utilizzando il simbolo &, per indicare esplicitamente dove deve essere inserita la classe padre.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Questi due esempi sono funzionalmente equivalenti e il motivo per cui hai delle opzioni diventerà più chiaro man mano che esploreremo esempi più avanzati in questo articolo.

Selezionare i cerchi

Per questo primo esempio, l'attività consiste nell'aggiungere stili per sfumare e sfocare solo i cerchi all'interno della demo.

Senza annidamento, CSS oggi:

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

Con l'annidamento, esistono due modi validi:

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

o

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

Il risultato: tutti gli elementi all'interno di .demo con una classe .circle sono sfocati e quasi invisibili:

La griglia colorata di forme non ha più cerchi,
    sono molto tenui sullo sfondo.
Prova una demo

Selezionare tutti i triangoli e i quadrati

Questa attività richiede la selezione di più elementi annidati, chiamati anche selettore di gruppo.

Senza annidamento, CSS oggi, esistono due modi:

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

oppure, utilizzando :is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

Con l'annidamento, ecco due modi validi:

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

o

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

Il risultato, all'interno di .demo rimangono solo gli elementi .circle:

Nella griglia colorata di forme rimangono solo cerchi,
    tutte le altre forme sono quasi invisibili.
Prova una demo

Selezionare triangoli e cerchi di grandi dimensioni

Questa attività richiede un selettore composto, dove gli elementi devono avere entrambe le classi presenti per essere selezionati.

Senza annidamento, CSS oggi:

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

o

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

Con l'annidamento, ecco due modi validi:

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

o

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

Il risultato: tutti i triangoli e i cerchi di grandi dimensioni sono nascosti all'interno di .demo:

Nella griglia colorata sono visibili solo forme piccole e medie.
Prova una demo
Consiglio da pro con selettori composti e annidamento

Il simbolo & è utile in questo caso perché mostra esplicitamente come unire i selettori annidati. Considera l'esempio seguente:

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

Sebbene sia un modo valido per l'annidamento, i risultati non corrisponderanno agli elementi che potresti aspettarti. Il motivo è che senza & per specificare il risultato desiderato di .lg.triangle, .lg.circle composto, il risultato effettivo sarebbe .lg .triangle, .lg .circle; selettori discendenti.

Selezionare tutte le forme tranne quelle rosa

Questa attività richiede una pseudoclasse funzionale di negazione, in cui gli elementi non devono avere il selettore specificato.

Senza annidamento, CSS oggi:

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

Con l'annidamento, ecco due modi validi:

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

o

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

Il risultato: tutte le forme non rosa sono nascoste all'interno di .demo:

La griglia colorata è ora monocromatica e mostra solo forme rosa.
Prova una demo
Precisione e flessibilità con &

Supponiamo che tu voglia scegliere come target .demo con il selettore :not(). Per farlo, è necessario utilizzare &:

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

In questo modo, .demo e :not() vengono combinati in .demo:not(), a differenza dell'esempio precedente che richiedeva .demo :not(). Questo promemoria è molto importante quando si vuole annidare un'interazione :hover.

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

Altri esempi di annidamento

La specifica CSS per l'annidamento contiene molti altri esempi. Se vuoi saperne di più sulla sintassi tramite esempi, troverai una vasta gamma di esempi validi e non validi.

I prossimi esempi introdurranno brevemente una funzionalità di annidamento CSS per aiutarti a comprendere l'ampiezza delle funzionalità che introduce.

Annidamento @media

Può essere molto distraente spostarsi in un'altra area del foglio di stile per trovare le condizioni delle query multimediali che modificano un selettore e i relativi stili. Questa distrazione non esiste più grazie alla possibilità di annidare le condizioni direttamente all'interno del contesto.

Per comodità della sintassi, se la query multimediale annidata modifica solo gli stili per il contesto del selettore corrente, è possibile utilizzare una sintassi minima.

.card {
  font-size: 1rem;

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

È possibile utilizzare anche & in modo esplicito:

.card {
  font-size: 1rem;

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

Questo esempio mostra la sintassi espansa con &, scegliendo come target anche le schede .large per dimostrare che le funzionalità di annidamento aggiuntive continuano a funzionare.

Scopri di più sull'annidamento delle @rules.

Annidamento ovunque

Tutti gli esempi fino a questo punto hanno continuato o aggiunto un contesto precedente. Se necessario, puoi modificare o riorganizzare completamente il contesto.

.card {
  .featured & {
    /* .featured .card */
  }
}

Il simbolo & rappresenta un riferimento a un oggetto selettore (non a una stringa) e può essere inserito ovunque in un selettore annidato. Può essere inserito anche più volte:

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

Sebbene questo esempio sembri un po' inutile, esistono sicuramente scenari in cui la possibilità di ripetere un contesto di selettore è utile.

Esempi di annidamento non validi

Esistono alcuni scenari di sintassi di annidamento non validi che potrebbero sorprenderti se hai utilizzato l'annidamento nei preprocessori.

Annidamento e concatenazione

Molte convenzioni di denominazione delle classi CSS si basano sulla possibilità di annidare per concatenare o aggiungere selettori come se fossero stringhe. Questo non funziona nell'annidamento CSS perché i selettori non sono stringhe, ma riferimenti a oggetti.

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

Nella specifica è disponibile una spiegazione più approfondita.

Esempio di annidamento complesso

Annidamento all'interno di elenchi di selettori e :is()

Considera il seguente blocco CSS di annidamento:

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

Questo è il primo esempio che inizia con un elenco di selettori e continua con l'annidamento. Gli esempi precedenti terminavano solo con un elenco di selettori. Non c'è nulla di non valido in questo esempio di annidamento, ma esiste un dettaglio di implementazione potenzialmente complesso sull'annidamento all'interno degli elenchi di selettori, in particolare quelli che includono un selettore ID.

Affinché l'intento dell'annidamento funzioni, qualsiasi elenco di selettori che non sia l'annidamento più interno verrà racchiuso tra :is() dal browser. Questo wrapping mantiene il raggruppamento dell'elenco di selettori all'interno di tutti i contesti creati. L'effetto collaterale di questo raggruppamento, :is(.one, #two), è che adotta la specificità del punteggio più alto all'interno dei selettori tra parentesi. Questo è il modo in cui :is() sempre funziona, ma potrebbe essere una sorpresa quando si utilizza la sintassi di annidamento perché non è esattamente ciò che è stato creato. In sintesi, l'annidamento con ID ed elenchi di selettori può portare a selettori con specificità molto elevata.

Per riepilogare chiaramente l'esempio complesso, il blocco di annidamento precedente verrà applicato al documento come segue:

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

Tieni d'occhio o insegna ai tuoi linter a avvisare quando l'annidamento all'interno di un elenco di selettori che utilizza un selettore ID, la specificità di tutto l'annidamento all'interno di quell'elenco di selettori sarà elevata.

Combinazione di annidamento e dichiarazioni

Considera il seguente blocco CSS di annidamento:

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

Il colore degli elementi .card sarà blue.

Tutte le dichiarazioni di stile combinate vengono sollevate in alto, come se fossero state create prima di qualsiasi annidamento. Nella specifica sono disponibili ulteriori dettagli.

Esistono modi per aggirare il problema. Di seguito vengono racchiusi i tre stili di colore in &, che mantiene l'ordine a cascata come previsto dall'autore. Il colore degli elementi .card sarà rosso.

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

In realtà, è una buona pratica racchiudere tutti gli stili che seguono l'annidamento con un &.

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

Rilevamento delle funzionalità

Esistono due ottimi modi per rilevare le funzionalità di annidamento CSS: utilizzare l'annidamento o utilizzare @supports per verificare la funzionalità di analisi dei selettori di annidamento.

Uno screenshot della demo di Codepen di Bramus, che chiede se il browser supporta
  l'annidamento CSS. Sotto la domanda si trova un riquadro verde, che indica il supporto.

Utilizzo dell'annidamento:

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

  .no-nesting {
    display: none;
  }
}

Utilizzo di @supports:

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

Il mio collega Bramus ha un ottimo Codepen che mostra questa strategia.

Debug con Chrome DevTools

Il supporto attuale per l'annidamento negli Strumenti per sviluppatori è minimo. Al momento, gli stili vengono rappresentati nel riquadro Stili come previsto, ma il tracciamento dell'annidamento e del relativo contesto di selettore completo non è ancora supportato. Abbiamo progettato e pianificato di rendere questa funzionalità trasparente e chiara.

Uno screenshot della sintassi di nidificazione di Chrome DevTools.

Chrome 113 prevede un supporto aggiuntivo per l'annidamento CSS. Non perderti gli aggiornamenti.

Futuro =

L'annidamento CSS è solo alla versione 1. La versione 2 introdurrà una sintassi più semplice e potenzialmente meno regole da memorizzare. Esiste una forte richiesta che l'analisi dell'annidamento non sia limitata o presenti momenti difficili.

L'annidamento è un miglioramento significativo del linguaggio CSS. Ha implicazioni di creazione per quasi tutti gli aspetti architettonici di CSS. Questo impatto significativo deve essere esplorato e compreso a fondo prima che la versione 2 possa essere specificata in modo efficace.

Per concludere, ecco una demo che utilizza @scope, l'annidamento e @layer tutti insieme. È tutto molto entusiasmante!

Una scheda chiara su uno sfondo grigio. La scheda ha un titolo e un testo,
  alcuni pulsanti di azione e un'immagine in stile cyberpunk.