Dans un article précédent, nous avons vu les divers sélecteurs CSS. Au fur et à mesure de leur utilisation, il pourra arriver que plusieurs règles, plusieurs sélecteurs s'appliquent au même élément. Dans un tel scénario, quelle règle CSS sera finalement appliquée à l'élément ? La chose est contrôlée par un mécanisme nommé Cascade ; il est également lié à la notion d'héritage (éléments prenant des valeurs de propriétés de leur parent, mais pas d'autres). Dans cet article, nous définirons ce qu'est la cascade dans les CSS, quelles sont ses particularités, son importance et comment les propriétés sont héritées à partir des diverses règles.

Prérequis : Notions de base sur les ordinateurs, installation des logiciels de base, connaissances élémentaires sur la  gestion des fichiers et des notions de HTML (étude de Introduction au HTML) et une idée sur le fonctionnement des CSS (voir les articles précédents de ce module).
Objectif : Apprendre la notion de cascade et ses particularités, ainsi que le fonctionnement de l'héritage dans les CSS.

Le style final d'un élément peut être défini à de nombreux endroits différents, qui peuvent interagir de manière complexe. Cette interaction complexe rend le CSS puissant, mais elle peut aussi le rendre confus et difficile à déboguer. Cet article vise à clarifier cette complexité ; si vous ne la comprenez pas immédiatement, ne vous inquiétez pas — c'est l'une des parties les plus difficiles à comprendre de la théorie des CSS. Nous vous conseillons d'essayer dès maintenant, mais gardez-le à portée de main comme guide pratique pour y revenir lorsque vous aurez des questions sur la cascade et l'héritage.

La cascade

CSS est l'acronyme de Cascading Style Sheets (« feuilles de style en cascade ») ce qui montre combien est importante de la notion de cascade. À son niveau le plus élémentaire, il indique que l'ordre des règles CSS est important, mais c'est plus complexe que cela. Le fait qu'un sélecteur l'emporte dans la cascade dépend de trois facteurs (ceux-ci sont listés par ordre de poids - les premiers l'emportent sur les suivants) :

  1. importance
  2. particularité
  3. ordre dans le source

Importance

Dans une CSS, pour vous assurer qu'une déclaration aura toujours la priorité sur les autres vous disposez de l'élément de syntaxe  : !important.

Voyons un exemple :

<p class="better">Voici un paragraphe.</p>
<p class="better" id="winning">Un sélecteur qui règne sur tous !</p>
#winning {
  background-color: red;
  border: 1px solid black;
}

.better {
  background-color: gray;
  border: none !important;
}

p {
  background-color: blue;
  color: white;
  padding: 5px;
}

créera ceci :

Parcourons ce code pour voir ce qui s'est passé :

  1. Vous pouvez voir que la troisième règle sur les valeurs de color et padding a été appliquée, mais que celle sur la valeur de background-color ne l'a pas été. Pourquoi ? En fait, les trois règles s'appliquent assurément, car les règles ultérieures dans l'ordre du source prennent le pas sur les précédentes.
  2. Toutefois, les règles au-dessus l'emportent, car les sélecteurs d'identifiant ou de classe, plus caractérisés, ont priorité sur les sélecteurs simples (vous en saurez plus dans les prochains paragraphes).
  3. Les deux éléments sont de la classe (class) better, mais le 2e a un id égal à winning également. Comme les ID ont toujours une caractérisation plus forte que les classes (vous ne pouvez avoir qu'un seul élément avec l'unique ID de la page, mais beaucoup d'éléments de la même classe — les sélecteurs d'ID sont d'une grande précision dans ce qu'ils ciblent), la couleur de fond rouge et l'encadrement noir de 1 pixel d'épaisseur seront donc appliqués tous deux au deuxième élément, alors que le premier élément conservera le fond gris, et aucun encadrement, comme défini par la classe.
  4. Le deuxième élément a bien un fond rouge, mais pas d'encadrement. Pourquoi ? En raison de la déclaration !important dans la deuxième règle — mettre cette dernière après border: none indique que cette déclaration l'emporte sur la valeur de l'encadrement de la règle précédente, même si ID a une forte caractérisation.

Note : Le seul moyen de prendre le pas sur une déclaration !important consiste à mettre une autre déclaration !important de caractérisation identique ou plus forte dans l'ordre du source.

Il est bon de savoir que !important existe, ainsi vous savez ce qu'il représente quand vous parcourez du code d'autres personnes. Toutefois, nous vous recommandons vivement de ne l'utiliser que si vous y êtes absolument obligé. Le cas où vous serez amené à le faire est celui où vous travaillez sur un CMS dans lequel vous ne pouvez pas modifier le cœurs des modules des CSS et que vous voulez vraiment modifier un style qui ne peut l'être d'aucune autre manière. Mais assurément, ne l'utilisez pas si vous pouvez vous en passer, car !important change la façon dont la cascade travaille naturellement ; cela peut amener des problèmes de débogage des CSS très difficiles à détecter, en particuler dans une grande feuille de style.

Il est également utile de noter que l'importance d'une déclaration CSS dépend de la feuille de style dans laquelle elle est définie - il est possible pour les utilisateurs de définir des feuilles de style personnalisées pour remplacer les styles du développeur, par exemple, l'utilisateur peut avoir une déficience visuelle, et souhaite que la taille de la police sur toutes les pages Web qu'il visite soit le double de la taille normale pour faciliter la lecture.

Les conflits de déclarations seront résolus dans l'ordre suivant, les déclarations ultérieures l'emportant sur les déclarations antérieures :

  1. Déclarations dans les feuilles de style de l'agent utilisateur (par exemple, les styles par défaut du navigateur, utilisés lorsqu'aucun autre style n'est défini).
  2. Déclarations normale des feuilles de style utilisateur (styles personnalisés définis par un utilisateur).
  3. Déclarations normales dans les feuilles des style de l'auteur (styles définis par le développeur web).
  4. Déclarations important dans les feuilles de style de l'auteur.
  5. Déclarations important dans les feuilles de style de l'utilisateur.

Il est logique que les feuilles de style des développeurs web remplacent les feuilles de style utilisateur pour que le design puisse être conservée comme prévu, mais parfois les utilisateurs ont de bonnes raisons de remplacer les styles développeur, comme nous l'indiquons ci-dessus — utiliser !important dans les règles utilisateur le permet.

Spécificité

La spécificité est à la base une mesure du caractère spécifique d'un sélecteur — à combien d'éléments peut-il correspondre. Comme l'exemple ci-dessus le montre, les sélecteurs d'éléments ont une faible spécificité. Les sélecteurs de classes ont une spécificité plus grande, dont ils prennent le pas sur les sélecteurs d'éléments. Les sélecteurs d'ID ont même une spécificité plus importante, ils l'emporteront donc sur les sélecteurs de classe. La seule manière de l'emporter sur un sélecteur d'ID est d'utiliser !important.

La quantité de spécificité d'un sélecteur se mesure avec quatre valeurs différentes (ou composantes), que l'on peut imaginer comme étant des milliers, des centaines, des dizaines ou des unités — quatre simples chiffres dans quatre colonnes :

  1. Milliers : mettez un dans cette colonne si la déclaration est dans un attribut style (de telles déclarations n'ont pas de sélecteurs, leur spécificité est toujours simplement 1000) sinon 0.
  2. Centaines : mettez un dans cette colonne pour chaque sélecteur d'ID contenu dans un sélecteur général.
  3. Dizaines : mettez un dans cette colonne pour chaque sélecteur de classe, sélecteur d'attribut ou de pseudo-classe contenu dans une sélecteur général.
  4. Unités : mettez un dans cette colonne pour chaque sélecteur d'élément ou de pseudo-élément contenu dans un sélecteur général.

Note : Sélecteurs universels (*), combinaisons (+, >, ~, ' ') et pseudo-classes de négation (:not) n'ont pas d'effet sur la spécificité.

Le tableau suivant montre quelques exemples isolés pour vous mettre dans l'ambiance. Allez au delà de ces exemples et assurez-vous d'avoir compris pourquoi il possèdent la spécificité que nous leur avons attribuée.

Sélecteur Milliers Centaines Dizaines Unités

Spécificité totale

h1 0 0 0 1 0001
#identifier 0 1 0 0 0100
h1 + p::first-letter 0 0 0 3 0003
li > a[href*="en-US"] > .inline-warning 0 0 2 2 0022

Pas de sélecteur, la règle est dans l'attribut style de l'élément

1 0 0 0 1000

Note : Si plusieurs sélecteurs ont la même importance et  même spécificité, le sélecteur qui l'emporte est celui qui vient en dernier dans l'orde du source (Source order).

Avant de poursuivre, voyons un exemple en action. Voici le HTML utilisé :

<div id="outer" class="container">
  <div id="inner" class="container">
    <ul>
      <li class="nav"><a href="#">Un</a></li>
      <li class="nav"><a href="#">Deux</a></li>
    </ul>
  </div>
</div>

et voici la CSS pour cet exemple :

/* spécificité : 0101 */
#outer a {
  background-color: red;
}

/* spécificité : 0201 */
#outer #inner a {
  background-color: blue;
}

/* spécificité : 0104 */
#outer div ul li a {
  color: yellow;
}

/* spécificité : 0113 */
#outer div ul .nav a {
  color: white;
}

/* spécificité : 0024 */
div div li:nth-child(2) a:hover {
  border: 10px solid black;
}

/* spécificité : 0023 */
div li:nth-child(2) a:hover {
  border: 10px dashed black;
}

/* spécificité : 0033 */
div div .nav:nth-child(2) a:hover {
  border: 10px double black;
}

a {
  display: inline-block;
  line-height: 40px;
  font-size: 20px;
  text-decoration: none;
  text-align: center;
  width: 200px;
  margin-bottom: 10px;
}

ul {
  padding: 0;
}

li {
  list-style-type: none;
}

Le résultat obtenu à partir de ce code est :

Que se passe-t-il ici ? Tout d'abord, nous ne sommes concernés que par les sept premières règles de cet exemple et comme vous l'avez remarqué, nous avons inclus les valeurs de leur spécificité dans un commentaire en-tête de chacune.

  • Les deux premiers sélecteurs sont en concurrence pour le style de la couleur de fond du lien — le second l'emporte et la couleur de fond sera bleue parce qu'il a un sélecteur d'ID supplémentaire dans la chaîne : sa spécificité est de 201 contre 101.
  • Les troisième et quatrième sélecteurs sont en concurrence pour le style de la couleur du texte du lien — le second l'emporte et le texte est blanc, car, même s'il a un sélecteur d'éléments en moins, le sélecteur manquant est remplacé par un sélecteur de classe, qui vaut dix au lieu d'un. La spécificité gagnante est donc de 113 contre 104.
  • Les sélecteurs 5 à 7 sont en concurrence pour le style de la bordure du lien lorsqu'il est survolé par le pointeur de souris. Le sélecteur six perd clairement contre le cinq avec une spécificité de 23 contre 24 — il y a un sélecteur d'éléments en moins dans la chaîne. Le sélecteur sept, cependant, bat à la fois le cinq et le six — il a le même nombre de sous-sélecteurs dans la chaîne que le cinq, mais un élément a été remplacé par un sélecteur de classe. La spécificité gagnante est donc de 33 contre 23 et 24.

Note : Si vous ne l'avez pas déjà fait, revoyez tous les sélecteurs une fois de plus, juste pour vous assurer de bien comprendre pourquoi les valeurs de spécificité ont été valorisées comme il est indiqué.

Ordre du source

Comme mentionné plus haut, si plusieurs sélecteurs concurrents ont la même importance et la même spécificité, le troisième facteur qui entre en jeu pour aider à décider quelle règle gagne est l'ordre du source — les dernières règles l'emportent sur les premières. Par exemple :

p {
  color: blue;
}

/* Cette règle l'emportera sur celle ci-dessus */
p {
  color: red;
}

Alors que dans cet exemple, la première règle l'emporte parce que l'ordre du source est surclassé par la spécificité :

/* Cette règle l'emporte */
.footnote {
  color: blue;
}

p {
  color: red;
}

Une note sur le mélange des règles

Une chose à garder en mémoire en considérant l'ensemble de la théorie de la cascade et l'application relative des styles est que tout cela se produit au niveau de la propriété — certaines propriétés l'emportent sur les autres propriétés, mais il n'y a pas de règles entières qui l'emporteraient sur d'autres règles. Quand plusieurs règles CSS sont relatives à un même élément, elles sont toutes appliquées à cet élément. Ce n'est qu'à la suite que les propriétés conflictuelles sont évaluées pour voir quels styles l'emporteront individuellement sur les autres.

Voyons un exemple. Primo, le HTML :

<p>Je suis <strong>important</strong></p>

et maintenant une CSS pour composition :

/* spécificité: 0002 */
p strong {
  background-color: khaki;
  color: green;
}

/* spécificité: 0001 */
strong {
  text-decoration: underline;
  color: red;
}

Résultat :

Dans cet exemple, en raison de la mesure de spécificité, la propriété color de la première règle remplace celle de la deuxième règle. Cependant, background-color de la première règle et text-decoration de la seconde règle sont appliqués à l'élément <strong>. Vous remarquerez également que le texte de cet élément est en gras : cela provient de la feuille de style par défaut des navigateurs.

Héritage

L'héritage dans les CSS est le dernier élément dont nous avons besoin pour obtenir toutes les informations et comprendre quel style est appliqué à un élément. L'idée est que certaines valeurs de propriété appliquées à un élément seront héritées par les enfants de cet élément, et d'autres non.

  • Par exemple, il est logique que font-family et color soient hérités : cela permet de définir facilement une police de base pour l'ensemble du site en appliquant une famille de polices à l'élément <html> ; il reste toujours possible de remplacer au cas par cas les polices sur certains éléments si nécessaire. Il serait vraiment ennuyeux d'avoir à définir la police de base séparément sur chaque élément.
  • Comme autre exemple, il est logique que margin, padding, border et background-image ne soient pas hérités.  Imaginez le désordre dans la composition et la mise en page qui résulterait d'une définition de ces  propriétés sur un élément conteneur avec héritage sur chaque élément enfant, définitions et héritages que vous deviez tous désactiver individuellement sur chaque élément !

L'héritage des propriétés par défaut ou le non-héritage est en grande partie une question de bon sens. Si vous voulez être sûr cependant, vous pouvez consulter la Référence des CSS  — chaque page de propriété séparée contient un tableau récapitulatif comprenant divers détails sur cet élément, y compris s'il est hérité ou non.

Contrôle de l'héritage

Les CSS disposent de quatre valeurs de propriétés universelles spéciales pour contrôler l'héritage :

inherit
indique que la valeur de propriété appliquée à l'élément sélectionné est la même que celle de l'élément parent.
initial
indique que la valeur de propriété appliquée à l'élément sélectionné est la même que celle définie pour cet élément dans la feuille de style par défaut du navigateur. Si aucune valeur n'est définie par la feuille de style par défaut du navigateur et que la propriété est naturellement héritée, alors la valeur de la propriété est fixée à inherit à la place.
unset
réinitialise la propriété à sa valeur naturelle, ce qui signifie que si la propriété est naturellement héritée, elle se comporte comme inherit, autrement elle agit comme initial.
revert
rétablit la propriété à la valeur qu'elle aurait eue si aucun style ne lui avait été appliqué. En d'autres termes, la valeur de la propriété est fixée à celle de la feuille de style de l'utilisateur (si elle est définie), sinon la valeur de la propriété est prise dans celle de la feuille de style par défaut de l'agent utilisateur.

Voir Origine des déclarations dans les CSS in Introducing the CSS Cascade pour plus de précisions et d'informations sur le mode de fonctionnement de ce qui précède.

Note : initial et unset ne sont pas pris en charge par Internet Explorer.

Parmi celles-ci inherit est souvent la plus intéressante — elle nous permet de faire en sorte qu'un élément hérite explicitement d'une valeur de propriété de son parent.

Voyons un exemple. Primo, le HTML :

<ul>
  <li>Couleur par défaut du <a href="#">lien</a></li>
  <li class="my-class-1">Héritage de la couleur du <a href="#">lien</a></li>
  <li class="my-class-2">Réinitialisation de la couleur du <a href="#">lien</a></li>
  <li class="my-class-3">Désactivation de la couleur du <a href="#">lien</a></li>
</ul>

Maintenant une CSS pour la composition :

body {
  color: green;
}

.my-class-1 a {
  color: inherit;
}

.my-class-2 a {
  color: initial;
}

.my-class-3 a {
  color: unset;
}

Résultat :

Expliquons ce qui se passe ici :

  • Nous définissons au préalable color pour <body> à la valeur green (vert).
  • Comme la propriété color est naturellement héritée, tous les enfants de body auront la même couleur verte. Notons bien que les navigateurs fixent la couleur des liens à la valeur blue par défaut au lieu d'autoriser lun héritage naturel de la propriété couleur, et donc, le premier lien de la liste est bleu.
  • La deuxième règle établit une liaison interne dans l'élément avec la classe inherit. Dans ce cas, le lien hérite de la couleur de son parent <li>}, qui, par défaut, hérite de la couleur de son propre parent <ul>}, qui hérite finalement de la couleur de l'élément <body>}, dont color a été défini à vert par la première règle.
  • La troisième règle établit toute les liaisons des éléments avec la classe initial et définit donc la couleur à la valeur initial. Généralement, la valeur initiale pour la couleur du texte définie par les navigateurs est noir, et donc le lien est écrit en noir.
  • La dernière règle établit toute les liaisons des éléments avec la classe unset et définit donc la couleur à la valeur unset. Donc, nous réinitialisons la valeur. Comme la propriété color est naturellement héritée, fixer cette propriété à unset agit exactement comme si elle valait inherit. Par conséquent, la couleur du lien est fixée à la même couleur que celle définie pour body — vert.

Réinitialisation de toutes les valeurs de propriétés

Dans les CSS, la propriété « raccourcie » all peut être utilisée pour appliquer les valeurs d'héritage à (pratiquement) toutes les propriétés d'un coup. Sa valeur peut être l'une des valeurs d'héritage (inherit, initial, unset ou revert). C'est une méthode commode pour défaire les changements apportés aux styles pour repartir d'une page blanche avant d'entamer de nouveaux changements.

Apprentissage actif :« cascader »

Dans cet apprentissage actif, vous allez expérimenter l'écriture d'une nouvelle règle unique remplaçant les couleurs de police et de fond appliquées par défaut aux liens. Pouvez-vous utiliser l'une des valeurs spéciales que nous avons regardées dans la section Contrôle de l'héritage} pour écrire une déclaration dans une nouvelle règle qui réinitialise la couleur de fond au blanc, sans utiliser de valeur de couleur ?

Si vous faites une erreur, vous pouvez tout Réinitialiser à l'aide du bouton correspondant. Si vous êtes vraiment bloqué, pressez Voir la solution pour afficher une réponse possible.

Qu'y a-t-il à la suite ?

 

Si vous avez compris la majeure partie de cet article, alors c'est bien — vous commencez à vous familiariser avec les mécanismes fondamentaux des CSS. Le dernier élément de la théorie centrale est le modèle de la boîte, dont nous parlerons prochainement.

Si vous n'avez pas bien compris la cascade, la spécificité et l'héritage, alors ne vous inquiétez pas ! C'est certainement la chose la plus compliquée que nous ayons traitée jusqu'à présent dans le cours, et c'est quelque chose que même les développeurs web professionnels trouvent parfois délicat. Nous vous conseillons de revenir à cet article plusieurs fois au fur et à mesure que vous continuez à suivre le cours et de continuer à y penser. Reportez-vous ici si vous commencez à rencontrer des problèmes étranges avec des styles qui ne s'appliquent pas comme prévu. Il pourrait s'agir d'un problème de spécificité.

Dans ce module

Étiquettes et contributeurs liés au document

Contributeurs à cette page : Dralyab, edspeedy, SphinxKnight, jumperparis
Dernière mise à jour par : Dralyab,