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.
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.
.nesting { color: hotpink; } .nesting > .is { color: rebeccapurple; } .nesting > .is > .awesome { color: deeppink; }
Après l'imbrication, les sélecteurs peuvent être continués et les règles de style associées peuvent être regroupées.
.nesting { color: hotpink; > .is { color: rebeccapurple; > .awesome { color: deeppink; } } }
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 à: - organiser vos fichiers ; - réduire la taille de vos fichiers ; - refactorer vos fichiers.
L'imbrication est disponible à partir de Chrome 112 et peut également être testé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.
À 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 bleues, roses ou violettes. 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 l'emplacement de la classe parente.
.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électionner les cercles
Pour ce premier exemple, la tâche consiste à ajouter des styles pour flouter et fondre uniquement les cercles de la démonstration.
Sans imbrication, le CSS d'aujourd'hui:
.demo .circle {
opacity: .25;
filter: blur(25px);
}
Avec l'imbrication, vous avez deux options:
/* & 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 :
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 options valides:
.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
:
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 d'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 options 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
:
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 que cette méthode d'imbrication soit valide, les résultats ne correspondront pas aux éléments que vous attendez.
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
:
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 offre.
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 règles @
Nidification n'importe où
Tous les exemples présentés jusqu'à présent ont continué ou ajouté 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.
Nidification 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 délicate
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 uniquement par une liste de sélecteurs. Cet exemple d'imbrication n'est pas invalide, mais l'imbrication dans des listes de sélecteurs, en particulier celles qui incluent un sélecteur d'ID, présente un détail d'implémentation potentiellement délicat.
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 ainsi que :is()
fonctionne, mais cela peut être surprenant lorsque vous utilisez la syntaxe d'imbrication, car ce n'est pas exactement ce qui a été créé. En résumé, l'imbrication avec des ID et des listes de sélecteurs peut entraîner des sélecteurs très spécifiques.
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 */
}
Surveillez ou apprenez à vos outils de linting à vous avertir lorsque vous imbriquez des éléments dans une liste de sélecteurs qui utilise un sélecteur d'ID. La spécificité de tous les éléments imbriqués dans cette liste de sélecteurs 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 tel que l'auteur l'a peut-être prévu. La couleur des éléments .card
est 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.
Utiliser 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.
Chrome 113 prévoit d'améliorer la compatibilité avec 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 pour l'écriture dans presque tous les aspects architecturaux du 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 !