Deux règles CSS ciblent parfois le même élément avec des instructions contradictoires. Avant même de regarder l'ordre du code, le navigateur compare le poids de chaque sélecteur : c'est la spécificité, un calcul précis qui décide laquelle des deux règles l'emporte.
Ne pas comprendre ce calcul pousse souvent à empiler les id ou les !important pour forcer un style à s'appliquer, une habitude qui rend une feuille de style de plus en plus difficile à maintenir au fil du temps.
Définition
La spécificité est un score attribué à chaque sélecteur CSS selon les composants qu'il contient. Plus ce score est élevé, plus la règle a de poids face à une autre règle concurrente qui cible le même élément et la même propriété.
Le calcul, en trois catégories
Le score se calcule en comptant trois catégories de composants dans le sélecteur : le nombre d'id, le nombre de classes, d'attributs et de pseudo-classes, puis le nombre de types d'éléments et de pseudo-éléments. On compare ensuite ces trois nombres de gauche à droite, comme des chiffres dans un système à trois colonnes.
/* 0 id, 1 classe, 0 élément : spécificité (0, 1, 0) */
.bouton {
color: blue;
}
/* 0 id, 2 classes, 0 élément : spécificité (0, 2, 0) */
.bouton.actif {
color: green;
}
/* 1 id, 0 classe, 0 élément : spécificité (1, 0, 0) */
#action-principale {
color: red;
}
/* résultat : #action-principale l'emporte sur les deux autres règles, quel que soit leur ordre */Dans cet exemple, #action-principale gagne systématiquement, même si les deux autres règles apparaissent plus tard dans le fichier, parce qu'un seul id pèse plus lourd que n'importe quel nombre de classes empilées.
Exemples chiffrés supplémentaires
| Sélecteur | id | classes/attributs | éléments |
|---|---|---|---|
p | 0 | 0 | 1 |
.carte | 0 | 1 | 0 |
.carte.mise-en-avant | 0 | 2 | 0 |
nav ul li a | 0 | 0 | 4 |
#en-tete .logo | 1 | 1 | 0 |
Le Sélecteur universel et les combinateurs comme l'espace ou le chevron n'ajoutent rien au score : seuls les composants qui ciblent réellement un élément comptent dans le calcul.
Pourquoi empiler les classes plutôt que les id
Comme le Sélecteur d'ID pèse énormément dans ce calcul, une équipe qui prend l'habitude de styliser via des id se retrouve rapidement bloquée : la moindre exception nécessite un autre id, ou pire, un !important. En s'en tenant au Sélecteur de classe, même en en empilant plusieurs sur un même sélecteur, la spécificité reste dans une fourchette basse et prévisible, ce qui garde toute la feuille de style facile à surcharger proprement. Notre formation HTML et CSS insiste sur cette discipline dès les premiers exercices, pour prendre l'habitude de coder avec une spécificité basse avant même d'avoir besoin d'y penser.
Visualiser la spécificité rapidement
Pour estimer la spécificité d'un sélecteur d'un coup d'œil, il suffit de compter ses composants dans l'ordre : chaque id ajoute un chiffre à la première position, chaque classe, attribut ou pseudo-classe à la deuxième, chaque type d'élément à la troisième. Cette lecture rapide, une fois automatique, évite de sortir une calculatrice de spécificité à chaque conflit et permet de repérer immédiatement quelle règle l'emportera face à une autre dans la Cascade.
Questions fréquentes
:hover compte-t-elle dans la spécificité ?Oui, une pseudo-classe compte exactement comme une classe dans le calcul, tandis qu'un pseudo-élément comme ::before compte dans la même catégorie qu'un type d'élément.
:is() ou :not() ?Oui, mais leur calcul suit une règle particulière : la spécificité retenue est celle du sélecteur le plus fort passé en argument, pas une simple addition de tous les sélecteurs de la liste.
Non, jamais. Le calcul compare les catégories de gauche à droite sans jamais les additionner entre elles : la catégorie des id l'emporte toujours sur celle des classes, quel que soit le nombre de classes empilées.