Cours CSS / Héritage, spécificité et cascade : déterminer le style final

La cascade choisit entre des déclarations CSS en concurrence, tandis que la spécificité compare la force des sélecteurs dans les conditions pertinentes de la cascade. L'héritage fournit certaines valeurs d'un parent lorsqu'aucune déclaration applicable ne les détermine ; ce sont des mécanismes liés mais différents.

Distinguer l'héritage des déclarations directes

L'héritage se produit lorsqu'un élément enfant hérite de la valeur de son élément parent. Alors que les propriétés relatives à la couleur du texte et à la police sont généralement héritées, les bordures et les marges intérieures (padding) ne sont pas héritées par défaut. Si vous supposez que toutes les propriétés seront identiques à celles de leur parent, vous risquez de chercher à tort une bordure héritée sur les paragraphes alors qu’elle entoure seulement leur parent.

#main {
  color: purple;
  border: 1px solid purple;
}
p { color: #555; }
<main id="main">
  <h1>Small Garden</h1>
  <p>Visitor information.</p>
</main>

En supposant que nous appliquons uniquement ces deux éléments, h1 héritera de la couleur violette de son parent main. La couleur grise que vous spécifiez est appliquée à p. Ce n'est pas parce que l'ID du parent #main est un sélecteur d'ID qu'il l'emporte sur l'attribution directe de p. Cela s'explique par le fait qu'il ne s'agit pas d'une compétition entre deux sélecteurs appliqués au même élément. La bordure est créée autour de la boîte de main et n'est pas dupliquée par les bordures de h1 et p respectivement.

Les propriétés non héritées peuvent également hériter de la valeur calculée du parent en la spécifiant comme border: inherit. En revanche, même s'il existe une déclaration qui s’applique directement à l’élément pour une propriété normalement héritée, cette déclaration est traitée avant l’héritage. Vous pouvez savoir quelles propriétés sont héritées et ce que signifie inherit dans la description de l'héritage CSS de MDN.

Suivez le processus de décision de la cascade

Commencez par le CSS de l'auteur ordinaire, comme les règles que vous écrivez pour cette page. Avant de comparer les déclarations, vérifiez que le sélecteur correspond, que la syntaxe est valide et que des conditions telles que les requêtes média s'appliquent. Excluez les règles non correspondantes et les déclarations invalides de la comparaison.

  1. Vérifiez les conditions principales telles que l’origine, l’importance et la couche. Il faut notamment distinguer les styles de l’auteur, ceux de l’utilisateur et ceux du navigateur, puis vérifier !important et les couches @layer. Si les conditions principales sont différentes, la décision n'est pas prise en comparant uniquement les scores des sélecteurs.
  2. Comparez la spécificité des sélecteurs dans les mêmes conditions. La priorité (spécificité) est un critère pour comparer la spécificité d'un sélecteur. Comparez l'ID, la famille de classes et la famille d'éléments dans cet ordre.
  3. Si la priorité est la même, la déclaration écrite en dernier est utilisée. Dans les situations où il n'y a pas de conditions supplémentaires comme @scope, comme dans les exemples de cette leçon, les égalités finales sont résolues dans l'ordre de source.
  4. Pour chaque propriété, CSS résout les déclarations applicables puis effectue la valeur par défaut si nécessaire. Les propriétés héritables peuvent obtenir une valeur parente ; les autres propriétés utilisent normalement leur valeur initiale. Cela ne signifie pas que chaque déclaration dans une règle gagnante remplace une règle antérieure entière.

La cascade est le processus complet de résolution des conflits CSS ; la spécificité des sélecteurs en est un critère. Les animations, transitions, couches et portées introduisent des règles supplémentaires, donc les exemples simples ici ne représentent pas tous les cas. Les règles de tri de la cascade du W3C définissent les relations entre origine, importance et couches. L'exemple complet ci-dessous n'utilise ni animations, ni transitions, ni @layer, ni @scope. Le Guide pour organiser le CSS avec styles partagés, imbrication et couches montre comment regrouper les règles en couches.

Comparer la spécificité sur trois colonnes

Le sélecteur de base dans cette leçon peut être lu dans trois colonnes : nombre d'ID, nombre de classes/attributs/pseudo-classes, et nombre d'éléments/pseudo-éléments. Comparez à partir de la colonne de gauche, et s'il y a une différence, décidez en fonction du résultat. Ne vous contentez pas de le mémoriser comme un tableau de scores décimaux en ajoutant simplement ID=100, classe=10, élément=1. Ce n'est pas parce qu'il y a beaucoup de classes que cela dépasse la colonne ID.

sélecteur ID Classe, attribut, pseudo-classe Élément/Pseudo-élément notation
p 0 0 1 0-0-1
.notice 0 1 0 0-1-0
p.notice 0 1 1 0-1-1
[required] 0 1 0 0-1-0
a:hover 0 1 1 0-1-1
p::before 0 0 2 0-0-2
#opening 1 0 0 1-0-0
#main .notice 1 1 0 1-1-0

p.notice et .notice ont la même colonne ID et la même colonne de classe. Il y a une différence entre 1 et 0 dans la colonne de l'élément, donc p.notice a la spécificité la plus élevée. #opening et p.notice ont une différence de 1 et 0 dans la première colonne, donc #opening a la spécificité la plus élevée. Les espaces blancs, les notations relationnelles telles que >, +, ~ et le sélecteur universel * n'augmentent pas directement le nombre de ces comparaisons.

p.notice { color: #087f5b; }
.notice { color: tomato; }

Sous les mêmes conditions d'origine, d'importance et de couche, p.notice a une spécificité plus élevée que .notice, donc sa couleur verte l'emporte même lorsqu'elle apparaît en premier. Les sélecteurs identiques ont une spécificité égale, laissant l'ordre dans le code source résoudre une égalité. Certaines pseudo-classes fonctionnelles ont des règles de spécificité supplémentaires. Voir MDN : Spécificité.

Résoudre chaque propriété séparément

.notice {
  color: blue;
  padding: 12px;
  border: 1px solid #087f5b;
}
.notice {
  color: tomato;
  padding: 20px;
}

Si seules ces deux règles sont appliquées au même élément, la couleur et le padding utilisent la déclaration de la seconde, tandis que la bordure de la règle précédente reste. Le fait qu'il n'y ait pas de bordure dans la règle suivante ne signifie pas que l'intégralité de la règle précédente disparaît. Le CSS doit être compris comme « déterminer les valeurs pour chaque propriété » plutôt que « sélectionner un bloc de règles entier ».

Par exemple, la couleur du texte peut provenir d'une règle ID, la marge peut provenir d'une règle de classe ultérieure, et la bordure peut provenir d'une règle de classe précédente. Même lorsque vous voyez le texte barré dans les outils de développement, lisez les déclarations une par une. Ce n’est pas parce que la couleur est barrée que le padding du même bloc n'est pas appliqué.

Prédisez l'exemple complet avant de l'exécuter

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Small Garden</title>
  <style>
body { margin: 24px; font-family: sans-serif; line-height: 1.7; }
    #main { color: purple; }
    p { color: #555; }
    .notice {
      color: blue;
      padding: 12px;
      border: 1px solid #087f5b;
    }
    p.notice { color: #087f5b; }
    .notice { color: tomato; padding: 20px; }
    #opening { color: #1d4ed8; }
  </style>
</head>
<body>
<main id="main">
    <h1>Small Garden</h1>
    <p id="opening" class="notice">A: We open at 9am every day.</p>
    <p class="notice">B: Prepare a water bottle and comfortable shoes.</p>
    <p>C: Please inquire at the information desk.</p>
  </main>
</body>
</html>

Prédisez les couleurs de A·B·C et l’espacement intérieur (padding) de la notice avant de l'ouvrir dans votre navigateur. Si vous enregistrez tout le code en tant que index.html et l'ouvrez, vous obtiendrez le résultat suivant. La police et la hauteur de ligne du corps sont des réglages auxiliaires pour faciliter la lecture.

objet et propriétés résultat final Raison de la décision
couleur de h1 Violet Hérite de la couleur du main. Il n'y a pas d'autres déclarations de couleur dans h1.
couleur de A Bleu #1d4ed8 #opening a une spécificité plus élevée que p.notice, .notice, p appliqué au même A.
couleur de B Vert #087f5b p.notice a une spécificité plus élevée que la règle .notice écrite plus loin.
couleur de C Gris #555 Il utilise une valeur directe pour p et n'hérite pas de la couleur violette du parent.
remplissage de A·B 20px Les deux déclarations .notice ont la même spécificité, donc le dernier remplissage l'emporte.
bordure de A·B ligne solide verte de 1px Puisqu'il n'y a pas de déclaration de bordure dans la dernière règle, la déclaration valide précédente reste.

Lorsqu'on vérifie dans Computed, la couleur de A peut être affichée comme rgb(29, 78, 216), la couleur de B peut être affichée comme rgb(8, 127, 91), et la couleur de C peut être affichée comme rgb(85, 85, 85). Même si la notation des couleurs est différente, cela indique la même valeur. Vérifiez que le remplissage est de 20px dans chaque direction et que l'épaisseur et la forme de la ligne de la bordure sont les mêmes.

Comparer les styles !important et en ligne

!important est une notation qui change l'importance d'une déclaration. Ce n'est pas une fonction qui ajoute un grand nombre aux trois colonnes du sélecteur. Selon la source du même auteur, les déclarations importantes prévalent sur les déclarations normales, donc ajouter la règle ci-dessous à la fin de l'exemple entier changera la couleur de A et B à la valeur tomato. La déclaration de couleur simple dans l'ID ne prévaut également pas sur cette déclaration importante.

.notice { color: tomato !important; }

Ce résultat ne signifie pas que important est toujours plus fort que tous les styles. Les styles importants de l'utilisateur, les valeurs pendant les transitions, etc., suivent un ordre séparé. Même parmi les déclarations importantes, vous devez d'abord vérifier l’origine et la couche. Dans cet exercice, observez important puis supprimez la règle que vous avez ajoutée. Habituellement, il est préférable pour la maintenance d'organiser d'abord la plage de sélection et la structure.

<p class="notice" style="color: purple;">B: Information on what to prepare</p>

Dans l'exemple original où la règle importante a été supprimée, si vous mettez une couleur en ligne dans B comme ci-dessus, elle aura la priorité sur les règles standard de la feuille de style et B deviendra violet. Les styles en ligne sont traités séparément des trois colonnes de sélecteurs par défaut dans le tableau ci-dessus. Encore une fois, mettre important dans la règle de classe générale lui permet de passer avant la couleur en ligne normale. Par conséquent, « si c'est en ligne, c'est inconditionnellement le plus fort » est également une explication sans conditions.

Exercez-vous à diagnostiquer les conflits

  1. Dans l'exemple complet original, déplacez la règle p.notice vers le bas. Assurez-vous que B est toujours vert et que A conserve la couleur bleue de son ID. Dans les états avec une grande spécificité, ils ne seront pas renversés simplement en changeant leur ordre.
  2. Ensuite, changez uniquement le sélecteur p.notice en .notice et placez-le en bas. La couleur de B utilise le vert spécifié par le dernier .notice. Comme nous avons le même sélecteur, l'ordre devient maintenant le critère de décision. Après confirmation, restaurez l'original.
  3. Supprimez uniquement id="opening" de A. Voyez si A devient du même vert que B. Même si l'ID du parent main reste le même, il ne l'emporte pas sur la couleur verte attribuée directement à A. Après vérification, rétablissez cet attribut id.
  4. En étapes séparées, appliquez les exemples importants et en ligne ci-dessus un par un et comparez-les. Au lieu de laisser derrière vous les règles de pratique précédentes, revenez à l'original et appliquez ensuite la condition à tester.

Si le résultat diffère de vos attentes, Inspectez le CSS avec DevTools : Styles et Computed. Sélectionnez l’élément correct, trouvez les déclarations pour la propriété, comparez leur importance, les sélecteurs et l’ordre, puis inspectez la valeur finale. Ajouter des IDs ou !important sans en trouver la cause peut changer la couleur maintenant tout en rendant les modifications ultérieures plus difficiles.