Jérémy Le Piolet - Blog

Pour arrêter de galérer avec CSS - La cascade

Cet article a été écrit il y a plus d'un an : son contenu peut être dépassé.

CSS, ce n’est pas simple. Un code CSS ça se pense, ça s’architecture et ça se construit. La simplicité du langage n’est qu’apparente et bon nombre de développeurs front mal préparés s’y cassent les dents.

Résultat : du CSS souvent difficilement maintenable qui conduit à une mauvaise expérience, aussi bien pour l’initateur du code que pour les repreneurs.

Pourtant, ce n’est pas une fatalité : en se donnant les moyens et en y passant un peu de temps, il est possible de rendre CSS beaucoup plus agréable (si, si). CSS aura toujours des défauts (sa difficulté de maintenance et sa faible scalabilité seront toujours présents) mais une majorité des problèmes rencontrés par la plupart des développeurs peuvent être résolus simplement.

Premier article d’une série pour arrêter de galérer avec CSS : la Cascade !

Le C de CSS

S’il vous est déjà arrivé de vous demander “mais pourquoi ce n’est pas pris en compte ce que je viens d’ajouter ?” et que vous vous êtes résolus à utiliser un !important pour faire passer votre règle, c’est sûrement un problème avec la cascade.

La cascade, c’est le C de CSS (Cascading Style Sheets). Et si beaucoup voient la feuille de style (basiquement le fichier où sera décrit le rendu et les balises CSS), peu savent vraiment ce qu’est la cascade. C’est pourtant le coeur de CSS, ce qui détermine l’ordre d’application des règles de style.

Pour déterminer le rendu d’un élément HTML, le navigateur récupère l’ensemble des styles qui peuvent s’y appliquer, soit directement via un sélecteur (sélecteur de classe, ids, style inline, etc.), soit indirectement (hérité du parent comme la couleur de texte). Chaque élément se voit ensuite attribué un score de spécificité et le navigateur applique tout simplement la règle avec le plus gros score.

La spécificité se calcule en fonction du sélecteur choisi : plus il est spécifique à un élément, plus la spécificité est haute. Ainsi, une règle CSS derrière un sélecteur d’id sera plus élevée qu’un sélecteur de classe (l’id est sensé être unique, la classe multiple) qui sera plus élevé qu’un sélecteur de balise (une classe ne s’applique qu’à un nombre restreint d’éléments HTML).

L’ordre des classes CSS dans le fichier HTML n’a donc aucune importance : on peut déclarer dans n’importe quel ordre, ça n’influencera pas le score. L’ordre ne commencera à entrer en compte qu’à score de spécificité égal : dans le cas là, les styles déclarés en dernier gagnent (mais encore une fois, changer l’ordre de vos classes dans le fichier HTML ne changera rien).

Connaître et tenir compte de cette contrainte est une clé de la maîtrise de CSS.

Calculer la spécificité

Alors ok, les règles avec le score le plus élevé gagnent. Mais concrètement, comment on calcule ce score, comment sait-on quelle règle va s’appliquer ?

Prenons le code HTML suivant :

<article id="monArticle">
  <ul>
    <li class="listItem">
        Je suis un texte en couleur
    </li>
  </ul>
</article>

Code HTML pour test

Et le style suivant :

li {
  color : red;
}

ul li {
  color : blue;
}

ul > li {
  color : black;
}

#monArticle .listItem {
  color: green;
}

#monArticle {
  color: orange;
}

li.listItem {
  color : yellow;
}

Code CSS pour test

De quelle couleur sera le texte “Je suis un texte en couleur” ?

Si on considère une version simplifiée dans laquelle nous n’aurions que des sélecteurs de tag, de classe et d’ids, on peut calculer un score comme suit :

  • chaque id dans un sélecteur ajoute une centaine
  • chaque classe dans un sélecteur ajoute une dizaine
  • chaque tag (ou pseudo-élément comme :hover) dans un sélecteur ajoute une unité.
  • l’héritage s’applique mais n’a pas d’incidence sur le score (donc comme si c’était 0)

Si l’on applique ces règles, on obtient donc :

Sélecteur ID Class Tag Score total
li 0 0 1     1
ul li 0 0 2     2
ul > li 0 0 2     2
#monArticle .listItem 1 1 0 110
#monArticle 0 0 0     0
li.listItem 0 1 1    11

C’est donc la couleur verte (green) qui sera appliquée car c’est elle qui a le plus gros score (vous pouvez essayer pour vérifier). Quelques précisions sur ce calcul :

  • On voit bien que l’ordre des propriétés n’a eu aucune importance car celle qui gagne est au milieu des autres, pas à la fin. Vous pouvez vous amusez à changer l’ordre, ce sera toujours vert.
  • Le sélecteur #article a un score de 0 car il ne s’applique pas directement sur le texte, seulement par héritage. S’il n’y avait aucun autre sélecteur, il se serait appliqué mais comme ce n’est pas le cas, il compte pour nul.
  • Les cibles > ne rentrent pas en compte dans le calcul du score.

Bien évidemment, on est ici sur une version simplifiée. Dans un cadre réel, il faut aussi prendre en compte le style inline (plus prioritaire que les feuilles de style), le !important (score maximal) et la résolution des conflits en cas d’égalité. Mais cet exemple permet de se faire une idée facilement de comment sont appliquées les règles css.

Pour ceux qui utilisent VsCode, sachez que l’éditeur propose une fonction d’affichage de la spécificité de votre sélecteur. Il suffit de le survoler et VsCode vous propose deux choses :

  • Un exemple de HTML qui déclenche le sélecteur
  • Son score de spécificité, tel que présenté plus haut.
Screenshot de VsCode avec l'affichage, au survol d'un sélecteur, du rendu HTML et de la spécificité

Capture d'écran de la fonctionnalité sous VsCode

Et c’est tout pour aujourd’hui.

Si vous voulez en savoir plus avec des exemples plus détaillés, je vous invite à lire les articles suivants :

  • The C in CSS: The Cascade, un très bon article en anglais qui reprend les questions d’ordre de CSS, de priorité, de sépcificité avec des exemples simples et un joli diagramme à la fin.
  • Cascade and inheritance, un autre très bon article de MDN qui reprend le concept de la cascade d’un point de vue différent du premier.

(image de couverture : Cascade Langevin, domaine public)