Un projet qui combine un reset, la bibliothèque de composants d'un design system, puis des ajustements propres à une page finit presque toujours par la même bataille : un ajustement écrit en dernier, censé gagner, perd contre un sélecteur du design system un peu trop spécifique. La correction habituelle empile les classes ou ajoute un !important, ce qui règle le cas présent en fragilisant le suivant.
Les calques de cascade s'attaquent à la cause plutôt qu'au symptôme : ils laissent décider l'ordre déclaré des blocs de style, indépendamment de leur spécificité interne.
Définition
Un calque de cascade, déclaré avec @layer, regroupe des règles CSS sous un nom. L'ordre dans lequel les calques sont déclarés la première fois fixe leur priorité : un calque déclaré après l'emporte toujours sur un calque déclaré avant, quelle que soit la Spécificité des sélecteurs à l'intérieur de chacun.
@layer reset, composants, utilitaires;
@layer reset {
a {
color: blue;
}
}
@layer composants {
.lien-carte {
color: green;
}
}La première ligne déclare l'ordre sans encore écrire de règles : c'est elle qui fixe la priorité finale, avant même que le contenu des calques n'apparaisse dans le fichier.
Comment l'ordre des calques prime sur la spécificité
À l'intérieur d'un même calque, les règles normales de la Cascade et de la spécificité s'appliquent comme d'habitude. Entre deux calques différents, en revanche, la spécificité ne compte plus du tout : un sélecteur très spécifique dans un calque antérieur perd toujours contre un sélecteur simple dans un calque postérieur.
@layer reset, composants;
@layer reset {
button.btn.btn-primary {
background: grey; /* très spécifique, mais dans le premier calque */
}
}
@layer composants {
.btn {
background: var(
--couleur-marque
); /* peu spécifique, mais dans le second calque : il gagne */
}
}Cas d'usage : faire cohabiter reset, design system et overrides
Trois calques, déclarés dans cet ordre, résolvent la bataille de départ sans qu'aucun sélecteur n'ait besoin d'être renforcé : reset pour la remise à zéro des styles du navigateur, systeme pour les composants du design system, overrides pour les ajustements propres à une page. N'importe quelle règle du dernier calque gagne automatiquement contre les deux précédents, même une simple classe unique face à un sélecteur du design system chargé de trois classes.
Associer un calque à un import
La règle @import accepte elle aussi un calque, ce qui permet de ranger une feuille de style tierce entière dans un calque précis sans ouvrir le fichier pour l'envelopper manuellement dans un bloc @layer.
@layer reset, composants;
@import "reset.css" layer(reset);
@import "bibliotheque-boutons.css" layer(composants);Cette écriture fonctionne aussi bien pour une feuille de style hébergée localement que pour celle d'une bibliothèque tierce chargée depuis un CDN, ce qui règle d'un coup le cas d'un design system externe dont le CSS ne peut pas être réécrit à la main.
@layer l'emporte toujours sur le CSS calé, quel que soit l'ordre des calques. En migrant progressivement un projet existant vers les calques, l'ancien code non calé continue donc de gagner contre les nouveaux calques soigneusement ordonnés, ce qui surprend souvent à la première tentative.Questions fréquentes
Non, jamais, tant que les deux calques restent sans !important : l'ordre des calques est vérifié avant la spécificité, qui ne sert plus qu'à départager deux règles à l'intérieur du même calque.
Plus fort, systématiquement. Tout le CSS écrit hors de @layer se comporte comme s'il appartenait à un calque final implicite, placé après tous les calques nommés, ce qui explique qu'il gagne toujours contre eux.
Oui, un même nom de calque peut être rouvert autant de fois que nécessaire, y compris dans des fichiers différents : les règles s'accumulent dans le calque, et seule sa position dans l'ordre déclaré au départ compte pour la priorité.