Spécificité

Le podcast CSS – 003 : Spécificité

Supposons que vous utilisiez le code HTML et CSS suivant :

<button class="branding">Hello, Specificity!</button>
.branding {
  color: blue;
}

button {
  color: red;
}

Ici, deux règles ciblent le même élément. Chaque règle contient une déclaration qui vise à définir la couleur du bouton : l'une essaie de colorer le bouton en rouge et l'autre en bleu. Quelle déclaration est appliquée à l'élément ?

Pour comprendre comment le CSS fait son choix entre des déclarations concurrentes, vous devez comprendre l'algorithme de spécificité CSS.

La spécificité est l'une des étapes distinctes de la cascade, qui a été abordée dans le dernier module, sur la cascade.

Calcul de la spécificité

Chaque règle de sélecteur dans une origine reçoit un score. Vous pouvez considérer la spécificité comme un score total, et chaque type de sélecteur rapporte des points à ce score. Les déclarations des règles ayant la spécificité la plus élevée sont prioritaires.

Dans un projet réel, la spécificité consiste à s'assurer que les règles CSS que vous souhaitez appliquer s'appliquent réellement, tout en maintenant généralement des scores bas pour éviter la complexité. La spécificité ne doit être aussi élevée que nécessaire, plutôt que de viser la spécificité la plus élevée possible. À l'avenir, il peut être nécessaire d'appliquer un CSS plus important. Si vous optez pour la spécificité la plus élevée, vous rendrez cette tâche difficile.

La spécificité n'est pas un nombre décimal, mais une triade composée de trois composants : A, B et C.

  • A : spécificité de type ID
  • B : spécificité de type classe
  • C : spécificité de type élément

Elle est souvent représentée à l'aide de la notation (A,B,C). Exemple : (1,0,2). La notation alternative A-B-C est également couramment utilisée.

Diagramme montrant les trois composantes de la spécificité (A,B,C). Pour chaque composant, le diagramme indique ce qu'il représente et quelques exemples de sélecteurs qui l'affectent.
Schéma illustrant le composant de spécificité affecté par différents sélecteurs.

Comparer les spécificités

Les spécificités sont comparées en comparant les trois composants dans l'ordre : la spécificité avec une valeur A plus élevée est plus spécifique ; si les deux valeurs A sont identiques, la spécificité avec une valeur B plus élevée est plus spécifique ; si les deux valeurs B sont également identiques, la spécificité avec une valeur C plus élevée est plus spécifique ; si toutes les valeurs sont identiques, les deux spécificités sont égales.

Par exemple, (1,0,0) est considéré comme une spécificité plus élevée que (0,4,3) car la valeur A dans (1,0,0) (qui est 1) est supérieure à la valeur A de (0,4,3) (qui est 0).

Les sélecteurs influencent la spécificité

Chaque partie de la triade de spécificité commence par une valeur de 0, la spécificité par défaut est donc (0,0,0). Chaque partie d'un sélecteur augmente la spécificité qui, selon le type de sélecteur, incrémente la valeur de A, B ou C.

Sélecteur universel

Un sélecteur universel (*) n'ajoute aucune spécificité, laissant sa valeur à la spécificité initiale de (0,0,0).

* {
  color: red;
}

Sélecteur d'élément ou de pseudo-élément

Un sélecteur d'élément (type) ou de pseudo-élément ajoute une spécificité de type élément qui incrémente le composant C de 1.

Les exemples suivants ont une spécificité globale de (0,0,1).

Sélecteur de type

div {
  color: red;
}

Sélecteur de pseudo-élément

::selection {
  color: red;
}

Sélecteur de classe, de pseudo-classe ou d'attribut

Un sélecteur de classe, pseudo-classe ou d'attribut ajoute une spécificité de type classe qui incrémente le composant B de 1.

Les exemples suivants ont une spécificité de (0,1,0).

Sélecteur de classe

.my-class {
  color: red;
}

Sélecteur de pseudo-classe

:hover {
  color: red;
}

Sélecteur d'attribut

[href='#'] {
  color: red;
}

Sélecteur d'ID

Un sélecteur d' ID ajoute une spécificité de type ID qui incrémente le composant A de 1, à condition d'utiliser un sélecteur d'ID (#myID) et non un sélecteur d'attribut ([id="myID"]).

Dans l'exemple suivant, la spécificité est (1,0,0).

#myID {
  color: red;
}

Autres sélecteurs

Le CSS comporte de nombreux sélecteurs. Ils n'ajoutent pas tous de spécificité. Par exemple, la :not() pseudo-classe elle-même n'ajoute rien au calcul de la spécificité.

La spécificité de la pseudo-classe :not() est remplacée par la spécificité du sélecteur le plus spécifique dans son argument de sélecteurs séparés par une virgule, ce qui donne la même spécificité que si elle avait été écrite :not(:is(argument)).

div:not(.my-class) {
  color: red;
}

La pseudo-classe :is() n'ajoute pas non plus au calcul de la spécificité. Comme :not(), elle prend la spécificité de son argument le plus spécifique.

:is(h1, h2, h3) {
  color: blue;
}

Cet exemple a une spécificité de (0,0,1) , car il ne comporte qu'un seul type de sélecteur (type).

Avec l'ajout d'un id, la spécificité passe à (1,0,0).

:is(h1, h2, h3, #my-heading) {
  color: blue;
}

La :where() pseudo-classe est différente. Quelle que soit la spécificité de l'un de ses arguments, elle a toujours une spécificité de (0,0,0).

:where(h1, h2, h3, #my-heading) {
  color: blue;
}

Les styles de faible spécificité appliqués à l'aide de :where() vous permettent de remplacer ces styles par des sélecteurs de base plus tard dans votre feuille de style :

:where(#my-content) {
   color: red;
}

p {
   color: blue;
}

Même si :where() comporte un id dans sa liste d'arguments, sa spécificité n'est que de (0, 0, 0). Le sélecteur p de base le remplace donc et le texte sera blue.

Vérifiez vos connaissances

Testez vos connaissances sur le calcul de la spécificité

Quelle est la spécificité de a[href="#"] ?

(0,0,1)
Le a vaut (0,0,1), mais le [href="#"] vaut (0,1,0).
(0,1,0)
Essayez encore. Le a vaut (0,0,1), mais le [href="#"] vaut (0,1,0).
(0,1,1)
Le a vaut (0,0,1) et le [href="#"] vaut (0,1,0), ce qui donne une spécificité totale de (0,1,1).

Facteurs qui n'affectent pas la spécificité

Il existe des idées fausses courantes concernant l'incidence des facteurs suivants sur la spécificité.

Attributs de style intégrés

Le CSS appliqué directement à l'attribut style d'un élément, n'affecte pas la spécificité, car il s'agit d'une étape différente de la cascade qui est évaluée avant la spécificité.

<div style="color: red"></div>

Pour remplacer cette déclaration à partir d'une feuille de style, vous devez recourir à pour que la déclaration soit prioritaire à une étape antérieure de la cascade.

Par exemple, vous pouvez ajouter !important pour qu'il fasse partie de l'origine !important créée.

Déclarations !important

Un !important à la fin d'une déclaration CSS n'affecte pas la spécificité mais place la déclaration dans une origine différente, à savoir Authored !important.

Dans l'exemple suivant, la spécificité de .my-class n'est pas pertinente pour que la déclaration !important soit prioritaire.

.my-class {
  color: red !important;
  color: white;
}

Lorsque deux déclarations sont !important, la spécificité entre à nouveau en jeu, car l'étape d'origine de la cascade n'a pas encore pu déterminer le gagnant.

.branding {
  color: blue !important;
}

button {
  color: red !important;
}

Spécificité en contexte

Lorsqu'un sélecteur complexe ou composé est utilisé, chaque partie de ce sélecteur s'ajoute à la spécificité. Prenons l'exemple de code HTML suivant :

<a class="my-class another-class" href="#">A link</a>

Ce lien comporte deux classes. La règle du CSS suivant a une spécificité de (0,0,1) :

a {
  color: red;
}

Si vous référencez l'une des classes dans le sélecteur, sa spécificité est désormais de (0,1,1) :

a.my-class {
  color: green;
}

Ajoutez l'autre classe au sélecteur, sa spécificité est désormais de (0,2,1) :

a.my-class.another-class {
  color: rebeccapurple;
}

Ajoutez l'attribut href au sélecteur, sa spécificité est désormais de (0,3,1) :

a.my-class.another-class[href] {
  color: goldenrod;
}

Enfin, ajoutez une :hover pseudo-classe à tout cela. Le sélecteur se retrouve avec une spécificité de (0,4,1) :

a.my-class.another-class[href]:hover {
  color: lightgrey;
}

Vérifiez vos connaissances

Testez vos connaissances sur le calcul de la spécificité

Lequel des sélecteurs suivants a une spécificité de (0,2,1) ?

article > section
Les éléments ajoutent une spécificité de type élément (composant `C`). Le sélecteur comporte deux éléments, ce qui lui donne une spécificité de (0,0,2).
article.card.dark
Les éléments ajoutent une spécificité de type élément (composant `C`) et les classes ajoutent une spécificité de type classe (composant `B`). Avec deux classes et un élément, ce sélecteur a une spécificité de (0,2,1).
article:hover a[href]
Les éléments ajoutent une spécificité de type élément (composant `C`), et les pseudo-classes et les attributs ajoutent une spécificité de type classe (composant `B`). Il existe deux sélecteurs d'éléments (2 × (0,0,1)), un sélecteur d'attribut (valeur (0,1,0)) et un sélecteur de classe (valeur (0,1,0)). La spécificité totale de ce sélecteur est donc de (0,2,2).

Augmenter la spécificité de manière pragmatique

Supposons que vous ayez un CSS qui ressemble à ceci :

.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

Avec un code HTML qui ressemble à ceci :

<button class="my-button" onclick="alert('hello')">Click me</button>

Le bouton a un arrière-plan gris, car le deuxième sélecteur a une spécificité de (0,1,1). En effet, il comporte un sélecteur de type (button), qui est (0,0,1) , et un sélecteur d'attribut ([onclick]), qui est (0,1,0).

La règle précédente, .my-button, est égale à (0,1,0) , car elle comporte un sélecteur de classe, qui a une spécificité inférieure à (0,1,1).

Si vous souhaitez donner un coup de pouce à cette règle, vous pouvez répéter le sélecteur de classe comme suit :

.my-button.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

Le bouton aura désormais un arrière-plan bleu, car le nouveau sélecteur obtient une spécificité de (0,2,0).

En cas d'égalité de spécificité, la cascade passe à l'étape suivante

Pour l'instant, reprenons l'exemple du bouton et modifions le CSS comme suit :

.my-button {
  background: blue;
}

[onclick] {
  background: grey;
}

Le bouton a un arrière-plan gris, car les deux sélecteurs ont une spécificité identique de (0,1,0).

Si vous inversez les règles dans l'ordre source, le bouton devient bleu.

[onclick] {
  background: grey;
}

.my-button {
  background: blue;
}

En effet, les deux sélecteurs ont la même spécificité. Dans ce cas, la cascade revient à l'étape de l'ordre d'apparition.

Ressources