Imbrication CSS

L'une de nos fonctionnalités préférées du préprocesseur CSS est désormais intégrée au langage: l'imbrication des règles de style.

Adam Argyle
Adam Argyle

Avant l'imbrication, chaque sélecteur devait être déclaré explicitement, séparément les uns des autres. Cela entraîne des répétitions, un volume important de feuilles de style et une expérience d'écriture dispersée.

Avant
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Après l'imbrication, vous pouvez continuer les sélecteurs et regrouper les règles de style qui y sont associées.

Après
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Essayez-le dans un navigateur.

L'imbrication aide les développeurs en réduisant le besoin de répéter des sélecteurs, tout en colocalisant les règles de style pour les éléments associés. Il peut également aider les styles à correspondre au code HTML qu'ils ciblent. Si le composant .nesting de l'exemple précédent a été supprimé du projet, vous pouvez supprimer l'ensemble du groupe au lieu de rechercher des instances de sélecteur associées dans les fichiers.

L'imbrication peut vous aider dans les domaines suivants: - Organisation - Réduire la taille des fichiers - Refactorisation

L'imbrication est disponible à partir de Chrome 112 et peut également être essayée dans la version Preview technique 162 de Safari.

Premiers pas avec l'imbrication CSS

Dans la suite de cet article,l'environnement de test de démonstration suivant vous aidera à visualiser les sélections. Dans cet état par défaut, rien n'est sélectionné et tout est visible. En sélectionnant les différentes formes et tailles, vous pouvez vous entraîner à utiliser la syntaxe et la voir en action.

Grille colorée composée de petits et grands cercles, triangles et carrés.

À l'intérieur du bac à sable se trouvent des cercles, des triangles et des carrés. Certaines sont petites, moyennes ou grandes. D'autres sont bleus, roses ou violets. Ils se trouvent tous dans l'élément contenant .demo. Vous trouverez ci-dessous un aperçu des éléments HTML que vous allez cibler.

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

Exemples d'imbrication

L'imbrication CSS vous permet de définir des styles pour un élément dans le contexte d'un autre sélecteur.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

Dans cet exemple, le sélecteur de classe .child est imbriqué dans le sélecteur de classe .parent. Cela signifie que le sélecteur .child imbriqué ne s'applique qu'aux éléments qui sont enfants d'éléments avec une classe .parent.

Cet exemple peut également être écrit à l'aide du symbole & pour indiquer explicitement où la classe parente doit être placée.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Ces deux exemples sont fonctionnellement équivalents. La raison pour laquelle vous avez des options deviendra plus claire à mesure que vous explorerez des exemples plus avancés dans cet article.

Sélection des cercles

Pour ce premier exemple, la tâche consiste à ajouter des styles pour n'effectuer un fondu et flouter que les cercles dans la démonstration.

Sans imbrication, le CSS d'aujourd'hui:

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

Avec l'imbrication, vous avez deux possibilités:

/* & 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);
  }
}

Résultat : tous les éléments de .demo avec une classe .circle sont floutés et presque invisibles :

La grille de formes colorée n&#39;a plus de cercles. Elles sont très pâles en arrière-plan.
Essayer une démonstration

Sélectionner des triangles et des carrés

Cette tâche nécessite de sélectionner plusieurs éléments imbriqués, également appelés sélecteur de groupe.

Sans imbrication, il existe deux façons de procéder avec CSS:

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

ou à l'aide de :is() :

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

Avec l'imbrication, voici deux méthodes valables:

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

ou

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

Résultat : seuls les éléments .circle restent dans .demo :

La grille colorée de formes ne contient plus que des cercles, toutes les autres formes étant presque invisibles.
Essayer une démonstration

Sélectionner de grands triangles et cercles

Cette tâche nécessite un sélecteur composé, où les éléments doivent présenter les deux classes pour être sélectionnés.

Sans imbrication, le CSS actuel:

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

ou

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

Avec l'imbrication, voici deux méthodes valides:

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

ou

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

Résultat : tous les grands triangles et cercles sont masqués dans .demo :

Seules les formes petites et moyennes sont visibles dans la grille colorée.
Essayer une démonstration
Conseil d'expert concernant les sélecteurs composés et l'imbrication

Le symbole & est votre allié ici, car il montre explicitement comment joindre des sélecteurs imbriqués. Prenons l'exemple suivant :

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

Bien qu'il s'agisse d'une méthode d'imbrication valide, les résultats ne correspondront pas aux éléments attendus. En effet, sans & pour spécifier le résultat souhaité de .lg.triangle, .lg.circle combinés, le résultat réel serait .lg .triangle, .lg .circle, c'est-à-dire des sélecteurs descendants.

Sélectionner toutes les formes, sauf les roses

Cette tâche nécessite une pseudo-classe fonctionnelle de négation, où les éléments ne doivent pas avoir le sélecteur spécifié.

Sans imbrication, le CSS d'aujourd'hui:

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

Avec l'imbrication, voici deux méthodes valides:

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

ou

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

Résultat : toutes les formes qui ne sont pas roses sont masquées dans .demo :

La grille colorée est maintenant monochrome et ne montre que les formes roses.
Essayer une démonstration
Précision et flexibilité avec &

Imaginons que vous souhaitiez cibler .demo avec le sélecteur :not(). & est obligatoire pour:

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

Cela combine .demo et :not() à .demo:not(), contrairement à l'exemple précédent qui nécessitait .demo :not(). Ce rappel est très important lorsque vous souhaitez imbriquer une interaction :hover.

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

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

Autres exemples d'imbrication

La spécification CSS pour l'imbrication contient de nombreux autres exemples. Si vous souhaitez en savoir plus sur la syntaxe à l'aide d'exemples, consultez la page qui couvre un large éventail d'exemples valides et non valides.

Les exemples suivants présentent brièvement une fonctionnalité d'imbrication CSS pour vous aider à comprendre l'étendue des fonctionnalités qu'elle introduit.

Imbrication @media

Il peut être très gênant de passer à une autre zone de la feuille de style pour trouver les conditions de requête multimédia qui modifient un sélecteur et ses styles. Cette distraction disparaît grâce à la possibilité d'imbriquer les conditions directement dans le contexte.

Pour des raisons de syntaxe, si la requête multimédia imbriquée ne modifie que les styles pour le contexte de sélecteur actuel, une syntaxe minimale peut être utilisée.

.card {
  font-size: 1rem;

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

Vous pouvez également utiliser & explicitement:

.card {
  font-size: 1rem;

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

Cet exemple montre la syntaxe développée avec &, tout en ciblant les cartes .large pour montrer que les fonctionnalités d'imbrication supplémentaires continuent de fonctionner.

En savoir plus sur l'imbrication de @rules

Nidification n'importe où

Tous les exemples jusqu'à présent se sont poursuivis ou sont ajoutés à un contexte précédent. Vous pouvez modifier ou réorganiser complètement le contexte si nécessaire.

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

Le symbole & représente une référence à un objet sélecteur (et non une chaîne) et peut être placé n'importe où dans un sélecteur imbriqué. Il peut même être placé plusieurs fois:

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

Bien que cet exemple semble un peu inutile, il existe certainement des scénarios où il est pratique de pouvoir répéter un contexte de sélecteur.

Exemples d'imbrication non valides

Certains scénarios de syntaxe d'imbrication ne sont pas valides et peuvent vous surprendre si vous avez imbriqué des éléments dans des préprocesseurs.

Imbriquer et concaténation

De nombreuses conventions de nommage de classe CSS reposent sur l'imbrication pour pouvoir concaténer ou ajouter des sélecteurs comme s'ils étaient des chaînes. Cela ne fonctionne pas dans l'imbrication CSS, car les sélecteurs ne sont pas des chaînes, mais des références d'objets.

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

Pour en savoir plus, consultez la spécification.

Exemple d'imbrication complexe

Imbriquer des listes de sélecteurs et des :is()

Prenons l'exemple suivant d'imbrication de blocs CSS:

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

Il s'agit du premier exemple qui commence par une liste de sélecteurs, puis continue à s'imbriquer. Les exemples précédents se terminaient seulement par une liste de sélecteurs. Cet exemple d'imbrication n'a rien de non valide, mais il existe un détail d'implémentation potentiellement délicat concernant l'imbrication dans des listes de sélecteurs, en particulier celles qui incluent un sélecteur d'ID.

Pour que l'intention de l'imbrication fonctionne, toute liste de sélecteurs qui n'est pas l'imbrication la plus interne sera encapsulée par le navigateur avec :is(). Ce forçage maintient le regroupement de la liste de sélecteurs dans tous les contextes créés. L'effet secondaire de ce regroupement, :is(.one, #two), est qu'il adopte la spécificité du score le plus élevé dans les sélecteurs entre parenthèses. C'est toujours ainsi que :is() fonctionne, mais l'utilisation de la syntaxe d'imbrication peut être surprenante, car ce n'est pas exactement ce qui a été créé. Résumé de l'astuce : l'imbrication avec des identifiants et des listes de sélecteurs peut générer des sélecteurs de spécificité très élevés.

Pour résumer clairement l'exemple délicat, le bloc d'imbrication précédent sera appliqué au document comme suit:

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

Restez à l'affût ou apprenez à vos lints à vous avertir lorsque l'imbrication dans une liste de sélecteur utilise un sélecteur d'ID, la spécificité de toutes les imbrications dans cette liste de sélecteur sera élevée.

Mélanger l'imbrication et les déclarations

Prenons l'exemple suivant d'imbrication de blocs CSS:

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

La couleur des éléments .card sera blue.

Toutes les déclarations de style entremêlées sont hissées en haut, comme si elles avaient été créées avant tout imbrication. Pour en savoir plus, consultez la spécification.

Il existe des solutions. Le code suivant encapsule les trois styles de couleur dans &, ce qui maintient l'ordre en cascade prévu par l'auteur. La couleur des éléments .card sera rouge.

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

En fait, il est recommandé d'encapsuler tous les styles qui suivent l'imbrication avec un &.

.card {
  color: green;

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

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

Détection de fonctionnalités

Il existe deux excellentes façons de détecter l'imbrication CSS: utiliser l'imbrication ou @supports pour vérifier la capacité d'analyse des sélecteurs d'imbrication.

Capture d&#39;écran de la démonstration Codepen de Bramus, qui demande si votre navigateur est compatible avec l&#39;imbrication CSS. Sous cette question se trouve un cadre vert signalant votre soutien.

En utilisant l'imbrication:

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

  .no-nesting {
    display: none;
  }
}

En utilisant @supports :

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

Mon collègue Bramus a créé un excellent Codepen qui illustre cette stratégie.

Déboguer avec les Outils pour les développeurs Chrome

La prise en charge actuelle de l'imbrication dans DevTools est minime. Actuellement, les styles sont représentés dans le volet "Styles" comme prévu, mais le traçage de l'imbrication et de son contexte de sélecteur complet n'est pas encore pris en charge. Nous avons conçu et planifié ce processus pour qu'il soit transparent et clair.

Capture d&#39;écran de la syntaxe d&#39;imbrication des outils pour les développeurs Chrome.

Chrome 113 prévoit une prise en charge supplémentaire de l'imbrication CSS. N'hésitez pas à vous tenir informé.

L'avenir

La imbrication CSS n'est disponible qu'en version 1. La version 2 introduira plus de sucre syntaxique et potentiellement moins de règles à mémoriser. De nombreuses demandes exigent que l'analyse de l'imbrication ne soit pas limitée ni ne présente de difficultés.

L'imbrication est une amélioration majeure du langage CSS. Il a des implications sur la création pour presque tous les aspects de l'architecture CSS. Cet impact important doit être exploré et compris en profondeur avant que la version 2 puisse être spécifiée de manière efficace.

Pour finir, voici une démonstration qui utilise @scope, l'imbrication et @layer. C'est très excitant !

Fiche claire sur fond gris. La carte comporte un titre, du texte, quelques boutons d&#39;action et une image de style cyberpunk.