Maîtriser la Cascade CSS et les @layer : Au-delà de l'!important

7
minutes
Mis à jour le
12/1/2024

Share this post

Zoom sur les @layer de CSS 5 comme alternative puissante à !important, qui permettent une gestion structurée, améliorent les performances et la personnalisation des styles, avec une intégration Angular.

#
CSS
#
Angular
#
Front-end
Laura Pedenaud
Software Engineer

Introduction

Lorsqu'il s'agit de styliser nos pages web, nous avons tous été confrontés à des situations où les propriétés CSS ne semblaient pas se comporter comme prévu. Dans de telles situations, il est tentant de recourir à la déclaration !important pour forcer notre style à primer sur les autres.

Cependant, cette solution peut s'avérer être une épée à double tranchant, car elle contourne toutes les règles de priorité du CSS, créant un code difficile à maintenir. Un collègue m’a raconté qu’il a même connu des moments frustrants où même avec un !important cela ne fonctionnait pas car il y avait déjà un autre !important ailleurs. Il aurait eu envie de mettre un !super-important !

L'approche que je présente est une alternative à l'utilisation de !important qui favorise une gestion plus contrôlée de la hiérarchie des styles CSS grâce aux règles @layer. Vous découvrirez comment tirer le meilleur parti de la cascade CSS pour rendre votre code plus efficace, plus lisible et plus maintenable.

C’est quoi la Cascade CSS

La Cascade CSS (Cascading Style Sheets) est un concept fondamental lorsqu’on écrit du CSS. Elle définit l'ordre dans lequel les styles sont appliqués à un élément HTML lorsque plusieurs règles CSS entrent en conflit, ce qui signifie que ces règles définissent des styles contradictoires pour le même élément. Comprendre comment la cascade CSS fonctionne est essentiel pour créer des designs web cohérents et flexibles, tout en évitant le recours excessif à des règles !important qui peuvent compliquer inutilement votre code.

Hiérarchie des Styles

La cascade CSS repose sur un principe de hiérarchie. Pareille à une cascade en français (waterfall en anglais), elle définit un ordre de priorité. Lorsque vous appliquez des styles à un élément HTML, plusieurs sources de styles peuvent entrer en jeu, et la cascade détermine l'ordre de priorité de ces styles.

Voici les principales sources de styles, classées de la plus haute à la plus basse priorité :

Origin et Importance

Si deux styles sont en conflits, la première règle de priorité qui va s’appliquer est celle de l’origine et de l’importance.

Cette règle fonctionne en classant les différentes sources de styles en fonction de leur origine et de leur importance, de la plus haute à la plus basse priorité. Voici comment cela fonctionne :

  1. Origine : Les styles peuvent provenir de différentes sources, et leur origine est l'un des facteurs clés pour déterminer leur priorité. Voici les principales origines, classées de la plus haute à la plus basse priorité :
  2. Author Styles : Les styles de l'auteur proviennent des feuilles de style CSS définies par le développeur ou l'auteur de la page web.
  3. Local User Styles : Les styles de l'utilisateur local sont définis par l'utilisateur final pour personnaliser l'apparence des pages web sur sa propre machine ou navigateur. Ils sont généralement stockés localement sur l'ordinateur de l'utilisateur ou appliqués via des extensions de navigateur.
  4. User Agent Styles : Il s'agit des styles par défaut appliqués par le navigateur lui-même.
  5. Importance : En plus de l'origine, l'importance des règles CSS peut également être spécifiée à l'aide de mots-clés. Les deux niveaux d'importance, du plus élevé au plus bas, sont les suivants :
  6. !important : Lorsqu'un style est marqué avec le mot-clé !important, il obtient la plus haute priorité et l'emporte sur tous les autres styles, quelle que soit leur origine. Et donc l’ordre de priorité entre les origines est inversé quand ils sont importants.
  7. Styles normaux : Les styles sans le mot-clé !important ont une priorité standard et sont appliqués en fonction de la hiérarchie d'origine.
  8. Ainsi, après application de cette règle de priorité sur ce code, on n’aura de conflit de style que entre les couleurs bleu, orange et rose :

Donc si un style provient d'une origine plus prioritaire ou s'il est marqué avec !important, il sera appliqué à l'élément, même s'il entre en conflit avec d'autres styles. Cela garantit que les styles définis par l'utilisateur ou les styles en ligne ont généralement la priorité sur les styles par défaut du navigateur.

On peut résumer cette priorité ainsi :

Style Attribute

Les déclarations telles que le contenu d'un attribut de style ont la priorité sur les déclarations mappées via une règle de style. Cela signifie que si vous avez défini un style directement dans l'attribut de style d'un élément HTML, il prévaudra sur les règles de style définies dans une feuille de style externe ou interne.

Cette fonctionnalité est utile pour apporter des styles spécifiques à un élément sans avoir à modifier la feuille de style principale. Par exemple :

Specificity

Lorsque plusieurs règles CSS s'appliquent à un même élément, et que les autres règles de priorité n’ont pas permis de résoudre le conflit, la spécificité peut déterminer laquelle de ces règles l'emportera. La spécificité est calculée en fonction de la complexité et de la profondeur des sélecteurs CSS.

Il existe des sites qui permettent de calculer la spécificité d’un style, comme par exemple celui-ci, toutefois, voici comment on peut la calculer :

Calcul de la spécificité

La spécificité est généralement mesurée sous la forme d'un triplet de valeurs numériques. Ce triplet est calculé comme suit :

  1. Nombre de sélecteurs d'éléments : Chaque élément HTML dans le sélecteur (par exemple, div, p, span) ajoute 1 à la spécificité.
  2. Nombre de classes et d'attributs : Chaque classe CSS (par exemple, .ma-classe) ou chaque attribut dans le sélecteur (par exemple, [type="text"]) ajoute 10 à la spécificité.
  3. Nombre d'identifiants : Chaque identifiant (par exemple, #mon-id) dans le sélecteur ajoute 100 à la spécificité.
  4. Pondération des pseudo-classes et pseudo-éléments : Les pseudo-classes (par exemple, :hover, :active) et pseudo-éléments (par exemple, ::before, ::after) peuvent également affecter la spécificité, mais leur poids varie en fonction du navigateur et des normes CSS.

Par exemple, le sélecteur universel * a une spécificité de 0, car il ne cible pas spécifiquement un élément. Cependant, certains navigateurs peuvent lui attribuer une spécificité plus élevée que 0 dans certaines situations, ce qui peut entraîner des conflits inattendus. Les anciennes versions d'Internet Explorer, en particulier IE 6, 7, et 8, avaient des interprétations de spécificité qui différaient des normes CSS et des autres navigateurs modernes. Cela a souvent causé des problèmes de compatibilité CSS.

Lorsqu'il y a un conflit entre les règles CSS, la règle avec la spécificité la plus élevée l'emporte. Par exemple, une règle avec un sélecteur plus spécifique, comme div#mon-id.ma-classe, aura une spécificité plus élevée que div.ma-classe. De même, une règle avec une classe et un attribut, tels que .ma-classe[type="text"], aura une spécificité plus élevée que .ma-classe.

Par exemple, si on applique la règle de spécificité sur l’exemple plus haut :

Ordre d’apparition

Si en fin de compte, lorsque toutes les autres règles de priorité, telles que l'origine, l'importance, et la spécificité, n'ont pas permis de résoudre un conflit de style, il reste un dernier élément qui permet de le faire : l'ordre d'apparition.

Les règles qui apparaissent en dernier dans la feuille de style l'emportent sur les règles précédentes. Cela signifie que si vous avez deux règles CSS identiques, mais que l'une est déclarée plus tard dans le fichier de style que l'autre, la dernière règle déclarée sera celle qui sera appliquée.

Pour reprendre notre dernier exemple, en appliquant cette dernière règle de priorité pour régler nos conflits de styles, on a :

Les préprocesseurs CSS (comme SASS, LESS, etc.), en général, conservent l'ordre d'apparition des règles CSS dans les fichiers source. Si vous avez des règles CSS dans un fichier source préprocesseur, elles seront généralement compilées dans le même ordre dans le fichier de sortie. Vous avez un bon contrôle sur l'ordre en organisant vos fichiers source de manière appropriée.En revanche l’utilisation d’un bundler peut influencer l'ordre d'apparition des styles dans le fichier de sortie. La garantie d'un ordre précis dépendra des règles que vous définissez dans votre configuration de bundler.

La cascade simplifiée et résumée

On peut finalement résumer et simplifier la cascade css dans ce schéma :

La cascade CSS détermine l'ordre dans lequel ces styles sont évalués et appliqués. Pour éviter les situations de confusion, il est essentiel de comprendre comment ces priorités fonctionnent ensemble. Cela vous permettra de développer des sites web plus robustes et de résoudre les problèmes de style de manière plus efficace, sans recourir à !important à tout bout de champ.

Dans la suite de cet article, nous explorerons une nouvelle fonctionnalité qui s’ajoute à cet ordre de priorité, les @layer, pour mieux organiser et contrôler vos styles CSS, tout en respectant la cascade CSS.

Comment la fonctionnalité @Layer s’ajoute à la cascade

Les @layer sont une fonctionnalité relativement nouvelle dans CSS, introduite dans les spécifications CSS 5. Elles permettent aux développeurs de catégoriser et d'organiser leurs règles CSS en fonction de leur importance, sans pour autant perturber la cascade CSS traditionnelle. Cette approche offre une meilleure lisibilité du code, une maintenance simplifiée et une solution plus élégante aux conflits de styles.

Vous pouvez regrouper vos règles CSS en différentes couches (layers) selon leur fonctionnalité ou leur priorité. Par exemple, vous pourriez avoir des layers pour les styles de base, les styles de mise en page, les styles spécifiques à un composant, etc.

Fonctionnement des @layer

Le principe général est simple : l'ordre de déclaration des layers (Layers Order) prime sur la spécificité des sélecteurs. Cela signifie que, même si deux règles CSS ont des sélecteurs de spécificité égale, la règle déclarée dans un layer supérieur l'emportera sur celle déclarée dans un layer inférieur. Par exemple, ici le paragraphe sera rouge car layer2 est déclaré dernière :

Déclarer et remplir des @layer

Il existe plusieurs manières de déclarer une couche :

  • On peut déclarer une couche d’abord vide :
  • On peut mettre des styles dans la couche directement et ajouter des styles au layer dans tous le fichier :

  • On peut importer des styles dans la couche :

Par ailleurs on peut déclarer les @layer au début du fichier .css et les remplir ensuite, alors seul l’ordre déclaré au début du fichier comptera (ici, on les styles de la couche reset auront la priorité) :

Résolution des Conflits avec les @layer

La spécificité et l'ordre d'apparition demeurent importants, mais seulement à l'intérieur d'une même couche (Layer).Ainsi si on avait un conflit de priorité lié à la spécificité, on peut s’en débarasser en englobant nos styles dans des layers. Ainsi on a la main sur les priorités de style en choisissant l’ordre de déclaration de nos layers.

Ici, sans les layers, la taille de la police aurait été small car la première propriété a une spécificité plus importante. Avec les layers, on sait parfaitement ce qui se passe, le layer2 étant déclaré en dernier, la taille de police sera large.

Unlayered Styles

Les styles en dehors des couches l'emportent toujours sur les styles imbriqués dans les couches, quelle que soit leur spécificité ou leur ordre d'apparition. Cela est dû au fait que chaque style en dehors des couches est automatiquement ajouté à une couche finale implicite, ce qui lui confère la priorité la plus élevée.

Ici bien que layer1 soit déclarée en dernier et sa propriété ayant le plus grand score de spécificité, le style qui est ‘unlayered’ aura la priorité, la couleur sera rose.

Ainsi lorsqu'un conflit de styles survient, les @layer offrent une solution élégante pour résoudre le problème. Au lieu d'utiliser !important pour forcer un style à prévaloir, vous pouvez simplement déplacer la règle dans le layer approprié pour ajuster sa priorité. Cette approche encourage une meilleure organisation de votre code CSS, car elle vous oblige à réfléchir à la sémantique et à la logique de vos règles.

Avantage des @Layer

L'utilisation des @layer en CSS offre plusieurs avantages significatifs pour le développement web. Citons par exemple quelques avantages que cette utilisation permet :

  • Priorités de style claires
  • Organisation et cohérence des styles (notamment pour la collaboration entre équipes)
  • Facilité de maintenance
  • Amélioration légère des performances (moins de calculs de priorité (notamment sur les spécificités))
  • Personnalisation du thème facile

Pour donner un exemple qui montre comme l’utilisation de layers peut rendre la personnalisation de thème facile, imaginons un site dont les propriétés de style qui définissent le thème ont été définies dans des layers, alors il suffit d’inverser l’ordre de déclaration des layers au début du fichier pour changer le thème (le dernier déclaré sera appliqué):

Comment intégrer @layer à mon projet Angular

Lorsqu'il s'agit de développer des applications web complexes en utilisant Angular, la gestion des styles CSS peut rapidement devenir un défi. Les conflits de style entre les composants, la difficulté à maintenir un code CSS clair et la gestion des priorités de style sont autant de problèmes qui se posent. En Angular, la première règle de priorité sera déterminée par l'ordre d'importation des stylesheets dans le DOM.

Imaginons une application Angular similaire à Tour of Heroes qui présente une liste de livres. Pour améliorer l'attrait visuel, vous souhaitez incorporer une étagère virtuelle interactive qui permet aux utilisateurs de voir les détails de chaque livre lorsqu'ils cliquent dessus. On envisage d'utiliser une bibliothèque JavaScript comme Slick Carousel, qui est couramment utilisée pour créer des carrousels interactifs. Cela peut être pertinent pour afficher les couvertures de livres de manière attrayante et interactive.

On aimerait s’assurer que la mise en forme pour l'étagère de livres prévaut sur tout style externe ou réinitialisation globale. Pour cela, on peut créer plusieurs layer dans le fichier principal styles.scss :

Ainsi on encapsule les fichiers CSS de la bibliothèque Slick Carousel dans un @layer. En organisant nos styles de cette manière, on est certain que les styles de notre composant de bibliothèque de livres prévaudront.

Et l'encapsulation de la vue d'Angular reste valable et assure que les styles de mon composant ne sont pas appliqués à tous les éléments de même type de mon application.

Conclusion

La maîtrise de la cascade CSS est essentielle pour garantir des sites web élégants et cohérents et éviter les pièges de l'attribut !important . C’est à dire, une difficulté de maintenance lorsqu’on a du mal à comprendre pourquoi un style particulier est appliqué car il annule tous les autres styles et des surdéfinitions de styles. Les @layer sont un atout précieux pour organiser et contrôler les règles CSS de manière efficace, améliorant la lisibilité et la maintenance du code en facilitant le suivi de la manière dont les styles sont appliqués. Dans le contexte d'Angular, les @layer sont particulièrement utiles pour gérer les styles, en veillant à ce que les styles personnalisés prévalent tout en préservant l'encapsulation de la vue.

Sources :