
la possibilité d'imbriquer des sélecteurs CSS les uns dans les autres se dirige vers une normalisation
Le 31 août, le CSS Nesting Module a été publié avec le statut « First Public Working Draft ». Il a ainsi définitivement quitté le statut de « Editor's Draft », qu'il détenait depuis mars 2019, et peut être considéré comme un consensus reconnu par le CSS Working Group (CSSWG). Cela ouvre la voie à la norme.
Les préprocesseurs connaissent les CSS imbriqués depuis les années 2000
La fonctionnalité, qui a été proposée pour la première fois en 2011 dans une liste de diffusion W3C par l'éditeur en charge, Tab Atkins, un développeur de Google, sous le nom de « Selector Nesting », apporte pour la première fois la possibilité d'imbriquer des sélecteurs CSS en natif, c'est-à-dire sans avoir besoin d'autres outils.
Les développeurs qui travaillent déjà avec des préprocesseurs tels que Sass, Less, SCSS ou, plus récemment, PostCSS utilisent l'imbrication de sélecteurs dans la syntaxe interne des outils de préprocesseur depuis 2007. Il est considéré comme l'un des principaux arguments en faveur de l'utilisation de Sass and Co. Pour une utilisation sur le Web, les préprocesseurs génèrent un CSS conforme à la norme. À l'avenir, les règles d'imbrication devraient appartenir à ce CSS conforme à la norme.
Le CSSWG décrit la fonctionnalité comme suit : « Ce module décrit la prise en charge de l'imbrication d'une règle de style dans une autre règle de style afin que le sélecteur de la règle interne puisse référencer les éléments de la règle externe. Avec cette fonction, les styles associés peuvent être résumés dans une structure unique au sein du document CSS, ce qui améliore la lisibilité grâce à une hiérarchie visuelle et la maintenabilité en évitant les répétitions. »
Motivation
Le CSS pour les pages Web, même modérément compliquées, comprend souvent de nombreuses duplications dans le but de styliser le contenu associé. Par exemple, voici une partie du balisage CSS pour une version du module [CSS-COLOR-3]*:
Code CSS : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | table.colortable td { text-align:center; } table.colortable td.c { text-transform:uppercase; } table.colortable td:first-child, table.colortable td:first-child+td { border:1px solid black; } table.colortable th { text-align:center; background:black; color:white; } |
L'imbrication permet de regrouper des règles de style associées, comme ceci*:
Code CSS : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | table.colortable { & td { text-align:center; &.c { text-transform:uppercase } &:first-child, &:first-child + td { border:1px solid black } } & th { text-align:center; background:black; color:white; } } |
Outre la suppression des doublons, le regroupement des règles associées améliore la lisibilité et la maintenabilité du CSS résultant.
Imbrication directe et indirecte
Les règles de style peuvent être imbriquées dans d'autres règles de style. Ces règles de style imbriquées agissent exactement comme des règles de style ordinaires (associant des propriétés à des éléments via des sélecteurs) mais elles « héritent » du contexte de sélecteur de leur règle parent, leur permettant de s'appuyer davantage sur le sélecteur du parent sans avoir à le répéter, éventuellement plusieurs fois.
Il existe deux syntaxes étroitement liées pour créer des règles de style imbriquées*:
- Imbrication directe, où la règle de style imbriquée est écrite normalement à l'intérieur de la règle parent, mais avec l'exigence que le sélecteur de la règle de style imbriqué soit préfixé par imbrication.
- La règle @nest, qui impose moins de contraintes sur le sélecteur de la règle de style imbriqué.
Mis à part la légère différence dans la façon dont elles sont écrites, les deux méthodes sont exactement équivalentes en termes de fonctionnalités.
Imbrication directe
Une règle de style peut être directement imbriquée dans une autre règle de style si son sélecteur est préfixé par imbrication.
Pour être préfixé par imbrication, un sélecteur d'imbrication doit être le premier sélecteur simple dans le premier sélecteur composé du sélecteur. Si le sélecteur est une liste de sélecteurs, chaque sélecteur complexe de la liste doit être préfixé par imbrication pour que le sélecteur dans son ensemble soit préfixé par imbrication.
Par exemple, les imbrications suivantes sont valides*:
Code CSS : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /* & can be used on its own */ .foo { color: blue; & > .bar { color: red; } } /* equivalent to .foo { color: blue; } .foo > .bar { color: red; } */ /* or in a compound selector, refining the parents selector */ .foo { color: blue; &.bar { color: red; } } /* equivalent to .foo { color: blue; } .foo.bar { color: red; } */ /* multiple selectors in the list must all start with & */ .foo, .bar { color: blue; & + .baz, &.qux { color: red; } } /* equivalent to .foo, .bar { color: blue; } :is(.foo, .bar) + .baz, :is(.foo, .bar).qux { color: red; } */ /* & can be used multiple times in a single selector */ .foo { color: blue; & .bar & .baz & .qux { color: red; } } /* equivalent to .foo { color: blue; } .foo .bar .foo .baz .foo .qux { color: red; } */ /* Somewhat silly, but can be used all on its own, as well. */ .foo { color: blue; & { padding: 2ch; } } /* equivalent to .foo { color: blue; } .foo { padding: 2ch; } // or .foo { color: blue; padding: 2ch; } */ /* Again, silly, but can even be doubled up. */ .foo { color: blue; && { padding: 2ch; } } /* equivalent to .foo { color: blue; } .foo.foo { padding: 2ch; } */ /* The parent selector can be arbitrarily complicated */ .error, #404 { &:hover > .baz { color: red; } } /* equivalent to :is(.error, #404):hover > .baz { color: red; } */ /* As can the nested selector */ .foo { &:is(.bar, &.baz) { color: red; } } /* equivalent to .foo:is(.bar, .foo.baz) { color: red; } */ /* Multiple levels of nesting "stack up" the selectors */ figure { margin: 0; & > figcaption { background: hsl(0 0% 0% / 50%); & > p { font-size: .9rem; } } } /* equivalent to figure { margin: 0; } figure > figcaption { background: hsl(0 0% 0% / 50%); } figure > figcaption > p { font-size: .9rem; } */ |
Mais ces imbrications ne sont pas valides :
Code CSS : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* No & at all */ .foo { color: blue; .bar { color: red; } } /* & isnt the first simple selector */ .foo { color: blue; .bar& { color: red; } } /* & isnt the first selector of every one in the list */ .foo, .bar { color: blue; & + .baz, .qux { color: red; } } |
Le dernier exemple ici n'est pas techniquement ambigu, car le sélecteur dans son ensemble commence par un &, mais c'est un risque d'édition - si la règle est remaniée pour supprimer le premier sélecteur ou réorganiser les sélecteurs dans la liste, ce qui normalement serait toujours valide, il en résulterait un sélecteur invalide désormais ambigu.
Certains outils de génération CSS concaténeront des sélecteurs tels que des chaînes, permettant aux auteurs de créer un seul sélecteur simple à travers les niveaux d'imbrication. Ceci est parfois utilisé par des méthodes d'organisation de sélecteurs comme BEM pour réduire la répétition dans un fichier, lorsque les sélecteurs eux-mêmes ont une répétition importante en interne.
Par exemple, si un composant utilise la classe .foo et qu'un composant imbriqué utilise .foo__bar, vous pouvez l'écrire dans Sass comme suit*:
Code CSS : | Sélectionner tout |
1 2 3 4 5 6 7 8 | .foo { color: blue; &__bar { color: red; } } /* In Sass, this is equivalent to .foo { color: blue; } .foo__bar { color: red; } */ |
Malheureusement, cette méthode est incompatible avec la syntaxe du sélecteur en général et nécessite au mieux des heuristiques adaptées aux pratiques d'écriture de sélecteur pour reconnaître quand l'auteur le souhaite, ou quand l'auteur essaye d'ajouter un sélecteur de type dans la règle imbriquée. __bar, par exemple, est un nom d'élément personnalisé valide en HTML.
En tant que tel, CSS ne peut pas faire cela ; les composants de sélecteur imbriqués sont interprétés seuls et non "concaténés":
Code CSS : | Sélectionner tout |
1 2 3 4 5 6 7 8 | .foo { color: blue; &__bar { color: red; } } /* In CSS, this is instead equivalent to .foo { color: blue; } __bar.foo { color: red; } */ |
La règle d'imbrication*: @nest
Bien que l'imbrication directe ait l'air agréable, elle est quelque peu fragile. Certains sélecteurs d'imbrication valides, comme .foo &, ne sont pas autorisés, et la modification du sélecteur de certaines manières peut rendre la règle invalide de manière inattendue. De plus, certains auteurs trouvent que l'imbrication est difficile à distinguer visuellement des déclarations environnantes.
Pour aider à résoudre tous ces problèmes, cette spécification définit la règle @nest,...
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.