Imbrication CSS

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

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, une accumulation de feuilles de style et une expérience de création dispersée.

Avant
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

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

Après
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

À essayer dans un navigateur

L'imbrication aide les développeurs en réduisant le besoin de répéter les sélecteurs tout en colocalisant les règles de style pour les éléments associés. Cela peut aussi 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 dans les fichiers les instances de sélecteur associées.

Avantages de l'imbrication: - 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,nous utiliserons le bac à sable de démonstration suivant pour vous aider à 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 exercer à 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, il y a des cercles, des triangles et des carrés. Certaines sont petites, moyennes ou grandes. D'autres sont en bleu, rose ou violet. Ils se trouvent tous dans l'élément contenant .demo. Voici 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 enfants d'éléments ayant une classe .parent.

Vous pouvez également écrire cet exemple à l'aide du symbole & pour indiquer explicitement l'emplacement de la classe parente.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Ces deux exemples sont équivalents d'un point de vue fonctionnel. La raison pour laquelle vous disposez de ces options deviendront plus claires à mesure que des exemples plus avancés seront abordés dans cet article.

Sélection des cercles

Pour ce premier exemple, la tâche consiste à ajouter des styles pour fondre et flouter uniquement les cercles dans la démo.

Sans imbrication, les CSS aujourd'hui:

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

Avec l'imbrication, deux options s'offrent à vous:

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

Par conséquent, tous les éléments d'une classe .demo associés à une classe .circle sont floutés et presque invisibles:

La grille de formes colorée n&#39;a plus de cercles, ils sont très tamisés à l&#39;arrière-plan.
Essayer une version de démonstration

Sélectionner des triangles et des carrés

Cette tâche nécessite la sélection de plusieurs éléments imbriqués, également appelés sélecteurs de groupe.

Aujourd'hui, sans imbrication, les CSS peuvent proposer deux solutions:

.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 de formes colorée ne contient que des cercles, toutes les autres formes sont presque invisibles.
Essayer une version de démonstration

Sélectionner de grands triangles et des cercles

Cette tâche nécessite un sélecteur composé, dans lequel les deux classes doivent être présentes pour que les éléments soient sélectionnés.

Sans imbrication, les CSS aujourd'hui:

.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 valables:

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

ou

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

Par conséquent, tous les grands triangles et cercles sont masqués dans .demo:

La grille colorée ne présente que des formes petites et moyennes visibles.
Essayer une version de démonstration
Conseil de pro concernant les sélecteurs composés et l'imbrication

Le symbole & représente votre ami ici, car il indique 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é des .lg.triangle, .lg.circle combinés, le résultat réel serait .lg .triangle, .lg .circle ; sélecteurs descendants.

Sélection de toutes les formes sauf les roses

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

Sans imbrication, les CSS aujourd'hui:

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

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

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

ou

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

Par conséquent, toutes les formes qui ne sont pas roses sont cachées dans .demo:

La grille colorée est désormais monochrome et ne montre que des formes roses.
Essayer une version de 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() en .demo:not(), par opposition à 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 regorge d'exemples. Si vous souhaitez en savoir plus sur la syntaxe à l'aide d'exemples, elle 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.

Imbriquer @media

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

Pour des raisons de syntaxe, si la requête mé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 étendue avec &, tout en ciblant les fiches .large pour montrer que d'autres fonctionnalités d'imbrication continuent de fonctionner.

En savoir plus sur l'imbrication de @rules

Imbrication partout

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

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

Le symbole & représente une référence à un objet de 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 soit un peu inutile, il existe certainement des scénarios dans lesquels pouvoir répéter un contexte de sélecteur est pratique.

Exemples d'imbrication non valides

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

Imbrication et concaténation

De nombreuses conventions de dénomination des classes CSS comptent sur la capacité de l'imbrication à concaténer ou à ajouter des sélecteurs comme s'il s'agissait de chaînes. Cela ne fonctionne pas avec 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 délicate

Imbrication dans des listes de sélecteur et :is()

Prenons l'exemple du bloc CSS d'imbrication suivant:

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

Il s'agit du premier exemple qui commence par une liste de sélecteurs et qui continue l'imbrication. Les exemples précédents se terminaient uniquement par une liste de sélecteurs. Cet exemple d'imbrication n'est pas non plus valide, mais il peut s'avérer difficile d'effectuer des opérations détaillées sur l'imbrication dans des listes de sélecteurs, en particulier celles qui incluent un sélecteur d'ID.

Pour que l'intent de l'imbrication fonctionne, toute liste de sélecteur qui n'est pas la plus imbriquée est encapsulée avec :is() par le navigateur. Cet encapsulation conserve le regroupement de la liste de sélecteur dans tous les contextes créés. Ce regroupement, :is(.one, #two), adopte la spécificité du score le plus élevé parmi les sélecteurs entre parenthèses. C'est ainsi que :is() fonctionne toujours. Toutefois, l'utilisation de la syntaxe d'imbrication peut être surprenante, car elle ne correspond pas exactement à celle qui a été créée. Voici l'astuce : l'imbrication avec des ID et des listes de sélecteurs peut conduire à des sélecteurs de spécificité très élevés.

Pour récapituler 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 attentif ou apprenez à vos linters à vous avertir lorsque vous imbriquez un élément dans une liste de sélecteurs qui utilise un sélecteur d'ID, car la spécificité de toutes les imbrications dans cette liste de sélecteurs sera élevée.

Combiner l'imbrication et les déclarations

Prenons l'exemple du bloc CSS d'imbrication suivant:

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

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

Toutes les déclarations de style mélangées sont hissées en haut de la liste, comme si elles avaient été créées avant l'imbrication. Pour en savoir plus, consultez les spécifications.

Il y a des moyens de contourner ce problème. Le code suivant encapsule les trois styles de couleur dans &, qui conserve l'ordre en cascade tel que l'auteur l'a peut-être prévu. Les éléments .card seront de couleur rouge.

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

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 façons de détecter l'imbrication CSS: l'imbrication ou @supports pour vérifier la capacité d'analyse du sélecteur d'imbrication.

Capture d&#39;écran de la démonstration de Codepen de Bramus, demandant si votre navigateur prend en charge l&#39;imbrication CSS. Sous cette question se trouve une case verte indiquant que l&#39;on vous aide.

Avec l'imbrication:

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

  .no-nesting {
    display: none;
  }
}

En utilisant @supports :

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

Mon collègue Bramus a développé un excellent Codepen illustrant cette stratégie.

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

La prise en charge actuelle de l'imbrication dans les outils de développement est minimale. 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 mis au point une conception et prévoyons de rendre ces informations transparentes et claires.

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

Chrome 113 prévoit de prendre en charge davantage l'imbrication CSS. N'hésitez pas à suivre notre actualité.

L'avenir

L'imbrication CSS n'est disponible qu'en version 1. La version 2 introduira davantage de sucre syntaxique et potentiellement moins de règles à mémoriser. Il existe de nombreuses demandes pour que l'analyse de l'imbrication ne soit pas limitée ou qu'elle ne présente pas de moments délicats.

L'imbrication constitue une amélioration majeure du langage CSS. Elle a des implications sur la création pour presque tous les aspects architecturaux des CSS. Cet impact important doit être exploré en profondeur et compris avant de pouvoir spécifier efficacement la version 2.

Pour terminer, voici une démonstration qui utilise à la fois @scope, l'imbrication et @layer. C'est passionnant !

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