Imbrication CSS

L'une de nos fonctionnalités préférées de 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 explicitement déclaré séparément les uns des autres. Cela entraîne des répétitions, l'encombrement des 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 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 dans votre 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 é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 tâches suivantes: - 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 de Safari 162.

Premiers pas avec l'imbrication CSS

Dans la suite de cet article,le bac à sable de démonstration suivant est utilisé 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 entraîner et la voir en action.

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

À l'intérieur du bac à sable se trouvent des cercles, des triangles et des carrés. Certains sont petits, moyens ou grands. D'autres sont bleus, roses ou violets. 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 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. Le but de ces options sera plus clair à mesure que nous aborderons des exemples plus avancés dans cet article.

Sélection des cercles

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

Aujourd'hui, sans imbrication, les CSS:

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

Avec l'imbrication, il existe deux façons valides:

/* & 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 dans .demo avec une classe .circle sont floutés et presque invisibles:

La grille colorée des formes 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électeurs de groupe.

Aujourd'hui, sans imbrication, il existe deux façons d'utiliser le 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 : il ne reste que des éléments .circle dans .demo :

La grille de formes colorée ne contient que des cercles. Toutes les autres formes sont presque invisibles.
Essayer une démonstration

Sélectionner de grands triangles et cercles

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

Aujourd'hui, sans imbrication, les CSS:

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

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 de pro avec les sélecteurs composés et l'imbrication

Le symbole & est votre ami 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 la combinaison de .lg.triangle, .lg.circle, le résultat réel serait .lg .triangle, .lg .circle ; sélecteurs descendants.

Sélection de toutes les formes sauf celles roses

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

Aujourd'hui, sans imbrication, les CSS:

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

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 vouliez cibler .demo avec le sélecteur :not(). & est obligatoire pour cela:

.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 contient d'autres exemples. Si vous souhaitez en savoir plus sur la syntaxe à travers des exemples, vous trouverez 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 trouver des conditions de requête média qui modifient un sélecteur et ses styles. Cette distraction disparaît avec 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 les styles que pour le contexte du 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

Nest Protect n'importe où

Tous les exemples jusqu'à présent se sont poursuivis ou sont 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 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 peu utile, il existe certainement des scénarios dans lesquels il est utile de pouvoir répéter un contexte de sélecteur.

Exemples d'imbrication non valides

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

Imbriquer et concaténation

De nombreuses conventions d'attribution de noms de 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 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 les spécifications.

Exemple d'imbrication complexe

Imbriquer des listes de sélecteurs 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, puis qui 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'intent de l'imbrication fonctionne, toute liste de sélecteur qui n'est pas la plus imbriquée interne sera encapsulée avec :is() par le navigateur. Cette encapsulation permet de conserver le regroupement de la liste de sélecteur 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é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 à 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.

Combiner l'imbrication et les déclarations

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

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

Les éléments .card seront de couleur blue.

Toutes les déclarations de style entrelacées sont hissées en haut, comme si elles avaient été créées avant toute imbrication. Pour en savoir plus, consultez les spécifications.

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. Les éléments .card seront de couleur 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 caractéristiques

Il existe deux méthodes efficaces pour détecter l'imbrication CSS: utiliser 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 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 utilise un excellent Codepen pour illustrer cette stratégie.

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

L'imbrication est actuellement très peu prise en charge dans les outils de développement. Actuellement, vous constaterez que 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 une conception et des plans pour le rendre 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

L'imbrication CSS est disponible dans la version 1 uniquement. La version 2 introduira davantage de sucre syntaxique et potentiellement moins de règles à mémoriser. Il existe une forte demande pour que l'analyse de l'imbrication ne soit pas limitée ou ne présente pas de moments délicats.

L'imbrication améliore considérablement le langage CSS. Il a des implications sur la création pour presque tous les aspects de l'architecture CSS. Cet impact majeur doit être exploré et compris en profondeur avant de pouvoir spécifier la version 2.

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

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