Comprendre comment ces mécanismes fonctionnent va nous permettre de savoir quelle règle CSS va être appliquée à quel élément et pourquoi et ainsi de véritablement contrôler le résultat graphique de nos pages HTML.
Comprendre l’importance d’établir un ordre d’application des règles CSS : le problème des conflits
Pour comprendre les mécanismes fondamentaux de cascade et d’héritage en CSS, il faut avant tout comprendre ce qu’est un conflit CSS.
Parfois, plusieurs sélecteurs différents vont nous permettre d’appliquer des styles CSS à un même élément.
Imaginons par exemple un élément p
auquel on attribuerait un attribut class
et un attribut id
. Nous allons pouvoir appliquer des styles CSS à cet élément de trois façons évidentes différentes :
- en utilisant un sélecteur élément ;
- en le ciblant via son attribut
class
; - en le ciblant via son attribut
id
.
See the Pen Cours HTML CSS 3.6.1 by Pierre (@pierregiraud) on CodePen.
Dans l’exemple ci-dessus, par exemple, nous avons une page HTML qui contient un titre de niveau 1 et trois paragraphes. Un de nos paragraphes possède un attribut class="bigorange"
tandis qu’un autre possède à la fois un attribut class="bigorange"
et un attribut id="green"
.
Enfin, un dernier paragraphe possède à la fois un attribut class="bigorange"
, un attribut id="yellow"
et un attribut style
dans lequel nous allons directement préciser un comportement pour la propriété color
qui ne s’appliquera donc qu’à cet élément.
Du côté du CSS, on cible nos éléments HTML via quatre sélecteurs : un sélecteur éléments p
, un sélecteur .bigorange
et deux sélecteurs #green
et #yellow
. Certains de nos paragraphes vont donc être ciblés plusieurs fois avec plusieurs sélecteurs différents et recevoir les styles définis dans ces différents sélecteurs.
Ici, on voit que le sélecteur p
est le seul sélecteur qui définit le comportement de la propriété text-decoration
tandis que le sélecteur .bigorange
est le seul qui définit le comportement de la propriété font-size
. Il n’y aura donc pas de conflit sur ces deux propriétés puisqu’elles ne sont définies qu’une fois en CSS pour les mêmes éléments.
En revanche, on définit un comportement différent pour la propriété color
au sein de chaque sélecteur. Dans ce cas-là, il va y avoir un conflit puisque le CSS va devoir déterminer quelle valeur de la propriété appliquer pour chaque élément ciblé avec plusieurs sélecteurs.
Pour comprendre comment le CSS va procéder dans ce cas, il faut avant tout bien se persuader que le CSS (comme tout autre langage web) repose sur un ensemble de règles. Les règles définissant l’ordre de préférence d’application des propriétés définies dans différents sélecteurs sont contrôlées par un mécanisme qu’on appelle la cascade. Connaitre ces règles va nous permettre de prédire quel style sera appliqué dans telle ou telle situation.
Le mécanisme de cascade CSS
Il n’est pas toujours simple de prédire quels styles CSS vont s’appliquer à quel élément pour la simple et bonne raison que le CSS peut être défini à des endroits différents (dans un élément style
, dans la balise ouvrante d’un élément dans un attribut style
ou dans un fichier CSS séparé) et qu’on va également pouvoir appliquer des styles à un élément en particulier en le ciblant via plusieurs sélecteurs CSS différents.
Il est donc essentiel de bien comprendre comment le CSS va fonctionner pour déterminer quels styles devront être appliqués à tel élément. L’ordre de préférence et d’application d’un style va dépendre de trois grands facteurs qui vont être :
- La présence ou non du mot clef
!important
; - La précision du sélecteur ;
- L’ordre de déclaration dans le code ;
A noter que ces trois facteurs vont être analysés dans l’ordre donné et que le premier va primer sur le deuxième qui va primer sur le dernier : par exemple, si une règle utilise la syntaxe !important
elle sera jugée comme prioritaire peu importe la précision du sélecteur ou sa place dans le code.
Le mot clef !important
La mot clef !important
sert à forcer l’application d’une règle CSS. La règle en question sera alors considérée comme prioritaire sur toutes les autres déclarations et le style sera appliqué à l’élément concerné.
Nous allons placer ce mot clef à la fin d’une déclaration CSS lorsqu’on souhaite qu’un style s’applique absolument à un élément.
See the Pen Cours HTML CSS 3.6.2 by Pierre (@pierregiraud) on CodePen.
Comme vous pouvez le constater dans l’exemple ci-dessus, le fait d’ajouter !important
dans la définition du comportement de la propriété color
liée à notre sélecteur p
fait que c’est cette définition qui s’appliquera par-dessus toutes les autres.
Ici, en particulier, vous pouvez voir que tous nos paragraphes sont rouges, même lorsque la propriété color
a été définie différemment dans un sélecteur de class
ou d’id
et même lorsqu’un comportement différent a été précisé dans un attribut style
dans la balise ouvrante d’un élément en particulier.
Le mot clef !important
est donc extrêmement puissant en CSS et peut ainsi sembler très pratique et très utile aux yeux des débutants. Cependant, en pratique, nous essaierons tant que possible de nous en passer tout simplement car ce mot clef est une sorte de « joker » qui court-circuite toute la logique normale du CSS.
L’utiliser à outrance et lorsque ce n’est pas strictement nécessaire peut donc amener de nombreux problèmes par la suite comme par exemple des problèmes de styles définis autrement et qui ne s’appliqueraient pas car déjà définis avec !important
ailleurs dans le code.
De manière générale, on préfèrera toujours aller dans le sens des langages et essayer de respecter et d’utiliser les normes qu’ils ont mis en place.
Le degré de précision du sélecteur
Le deuxième critère déterminant dans l’application d’un style plutôt que d’un autre va être le degré de précision du sélecteur où le style a été défini une première fois par rapport aux autres degrés de précision des autres sélecteurs où le style a été à nouveau défini.
Le sélecteur le plus précis imposera ses styles aux sélecteurs moins précis en cas de conflit.
Pour rappel, la « précision » désigne ici le fait d’identifier de manière plus ou moins unique un élément. Les sélecteurs peuvent être rangés dans l’ordre suivant (du plus précis au moins précis) :
- Un style défini dans un attribut HTML
style
sera toujours le plus précis et notamment plus précis qu’un style défini avec un sélecteur CSS ; - Le sélecteur
#id
va être le sélecteur le plus précis mais sera moins précis qu’un style défini dans un attribut HTMLstyle
; - Un sélecteur
.class
ou un autre sélecteur d’attribut* (*les autres sélecteurs d’attributs sont des sélecteurs complexes que nous étudierons plus tard) ou un sélecteur de pseudo-classe** (**nous verrons ce qu’est une pseudo-classe plus tard dans ce cours) sera moins précis qu’un sélecteur#id
; - Un sélecteur d’élément ou de pseudo-élément*** (***nous étudierons les pseudo éléments plus tard dans ce cours) sera moins précis qu’un sélecteur d’attribut ou de pseudo-classe.
Si deux sélecteurs différents sont au même degré de précision, alors c’est le sélecteur le plus « complet » c’est-à-dire celui qui utilisera le plus de combinateurs qui sera jugé le plus précis.
Prenons immédiatement un exemple pour illustrer cela :
See the Pen Cours HTML CSS 3.6.3 by Pierre (@pierregiraud) on CodePen.
Ici, on voit que deux propriétés sont définies dans plusieurs sélecteurs qui servent à sélectionner le même élément : les propriétés color
et font-size
. Ce sont donc nos deux propriétés qui vont générer des conflits.
On commence par vérifier la présence du mot clef !important
: il n’est défini nulle part ici. On passe donc au deuxième critère qui est le degré de précision.
On regarde déjà si des attributs style
sont présents dans le code. C’est le cas pour notre dernier paragraphe qui possède un attribut style="color:purple"
. Comme la règle ne peut pas être appliquée plus précisément, on sait que ce paragraphe sera de couleur violette.
Ensuite, on s’intéresse à la présence d’attributs id
. Notre troisième paragraphe possède un id="green"
et le sélecteur correspondant définit la règle color : green
. Ce paragraphe sera donc vert.
Ensuite, on regarde la présence de sélecteur .class
ou de sélecteurs d’autres attributs ou de sélecteurs de pseudo-classes. Notre deuxième élément de liste possède deux attributs class : bigorange
et petit
et on va définir le comportement de la propriété font-size
dans chacun des deux sélecteurs associés.
Ici, les deux sélecteurs sont des sélecteurs .class
et possèdent donc le même degré de précision à priori. Il va donc falloir regarder si un sélecteur est plus complet que l’autre c’est-à-dire s’il utilise différents combinateurs pour le rendre plus précis ou pas. C’est le cas de notre sélecteur ul .petit
qui va finalement nous servir à ne cibler que les éléments possédant un attribut class= "petit"
contenus dans un élément ul
.
L’ordre d’écriture des règles
Le troisième et dernier critère qui va nous permettre de définir quel style doit primer sur tel autre et doit donc être appliqué à un élément va tout simplement être l’ordre d’écriture d’une règle dans le code.
Ce critère va être utilisé dans le cas où plusieurs sélecteurs concurrents définissent le comportement d’une même propriété et ont la même importance et la même spécificité.
La règle ici est très simple : c’est la dernière déclaration dans le code qui primera sur des déclarations précédentes.
Regardez plutôt l’exemple suivant :
See the Pen Cours HTML CSS 3.6.4 by Pierre (@pierregiraud) on CodePen.
Ici, chacun de mes deux paragraphes possèdent deux attributs class
qui vont à chaque fois définir le comportement d’une même propriété. Les sélecteurs CSS associés ont la même importance et le même degré de spécificité. Il va donc falloir regarder leur ordre d’écriture pour savoir quelles règles vont être appliquées.
Ici, le sélecteur .grand
apparait après le sélecteur .petit
dans le code. C’est donc la taille de texte définie dans .grand
qui va être appliquée à notre premier paragraphe.
De même, le sélecteur .orange
apparait après le sélecteur .bleu
dans le code. Le texte de notre deuxième paragraphe sera donc orange et non pas bleu.
Notez que c’est exactement la même règle d’ordre d’écriture des styles qui va s’appliquer, à sélecteur égal, pour déterminer si ce sont les styles définis dans un élément style
ou si ce sont ceux définis dans un fichier CSS séparés qui vont s’appliquer.
Ici, notre titre h1
s’affiche en orange car nous avons précisé l’élément style
après l’élément link
qui fait appel à notre fichier CSS dans notre fichier HTML. Les styles définis dans l’élément style
seront donc lus après ceux définis dans notre fichier CSS liés et seront donc appliqués dans le cas où plusieurs sélecteurs concurrents définissent le comportement d’une même propriété et ont la même importance et la même spécificité.
Pour vous en convaincre, échangeons la place des éléments link
et style
dans notre code HTML et observons le résultat sur notre code HTML :
Une convention en HTML va être de toujours préciser notre élément style
après notre élément link
dans le code pour ne pas s’embrouiller et c’est la raison pour laquelle on retient généralement qu’à sélecteur égal les styles définis dans l’élément style
sont prioritaires sur ceux définis dans un fichier CSS séparé.
Notez qu’ici notre titre h1
va toujours avoir la taille définie dans le sélecteur .petit
puisqu’un sélecteur d’attribut class
est toujours plus précis qu’un sélecteur élément et que ce critère de précision passe avant le critère de l’ordre d’écriture des styles dans le code.
L’héritage en CSS
La notion d’héritage est une autre notion fondamentale du CSS. Elle signifie que certains styles CSS appliqués à un élément vont être hérités par les éléments enfants de cet élément, c’est-à-dire par les éléments contenus dans cet élément.
Cette notion d’héritage est conditionnée par deux choses :
- Toutes les propriétés ne vont pas être héritées pour la simple et bonne raison que cela ne ferait aucun sens pour certaines de l’être ;
- Les éléments enfants n’hériteront des styles de leur parent que si il n’y a pas de conflit c’est-à-dire uniquement dans la situation où ces mêmes styles n’ont pas été redéfinis pour ces éléments enfants en CSS.
Pour savoir quelles propriétés vont pouvoir être héritées et quelles autres ne vont pas pouvoir l’être il va soit falloir faire preuve de logique (et bien connaitre le langage CSS), soit falloir apprendre par cœur pour chaque propriété si elle peut être héritée ou pas.
Les propriétés qui vont pouvoir être héritées sont en effet celles dont l’héritage fait du sens. Par exemple, la propriété font-family
qui sert à définir un jeu de polices à utiliser pour du texte va pouvoir être hérité car il semble logique que l’on souhaite avoir la même police pour tous les textes de nos différents éléments par défaut.
En revanche, les propriétés liées aux marges par exemple ou plus généralement les propriétés de mise en page et de positionnement des éléments ne vont pas pouvoir être héritées car cela ne ferait aucun sens d’un point de vue design de rajouter une marge d’une taille définie pour chaque élément enfant.
See the Pen Cours HTML CSS 3.6.5 by Pierre (@pierregiraud) on CodePen.
Dans l’exemple ci-dessus, je définis un jeu de police avec la propriété font-family
dans mon sélecteur html
. Comme tous les éléments d’une page HTML sont des enfants de cet élément (ils sont contenus dans l’élément html
) et comme la propriété font-family
peut être héritée, tous les textes de ma page utiliseront la police d’écriture définie dans cette propriété sauf si une autre police est définie de manière plus précise avec un sélecteur plus précis comme c’est le cas pour mon titre h1
ici.
J’attribue ensuite une marge extérieure gauche égale à 50px à mon élément ul
représentant ma liste. Ma liste sera donc décalée de 50px par rapport au bord gauche de son élément parent qui est ici l’élément body
qui représente la page. Cependant, comme la propriété margin
ne peut pas être héritée, les éléments de liste de vont pas hériter de ce même margin-left : 50px
par défaut.
Ici, vous devez bien comprendre que la marge se calcule par rapport au début de l’élément parent. La liste entière est décalée de 50px par rapport à l’élément body
mais les éléments de liste ne sont pas décalés par défaut de 50px par rapport à l’élément ul
qui est leur élément parent. Pour bien illustrer cela, j’ai ajouté manuellement un margin-left : 50px
au deuxième élément de liste afin de vous prouver que le premier élément de liste n’a pas hérité de la propriété margin
appliquée à son élément parent ul
.
Notez que le CSS nous laisse toutefois la possibilité de « forcer » un héritage pour des propriétés non héritées par défaut ou plus exactement la possibilité de définir des comportements d’héritage pour chaque propriété définie dans chaque sélecteur.
Pour faire cela, nous allons pouvoir utiliser quatre valeurs qui vont fonctionner avec toutes les propriétés CSS (elles sont dites universelles) et qui vont nous permettre d’indiquer que telle propriété définie dans tel sélecteur doit avoir le même comportement que celle définie pour l’élément parent ou pas.
Ces valeurs sont les suivantes :
Valeur | Signification |
---|---|
inherit |
Sert à indiquer 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 |
Sert à indiquer 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 |
unset |
Permet de réinitialiser la propriété à sa valeur naturelle, ce qui signifie que si la propriété est naturellement héritée elle se comporte comme si on avait donné la valeur inherit . Dans le cas contraire, son comportement sera le même que si on lui avait donné la valeur initial |
revert |
Réinitialise la propriété à la valeur qu’elle aurait eue si aucun style ne lui avait été appliqué. La valeur de la propriété va donc être fixée à celle de la feuille de style de l’utilisateur si elle est définie ou sera récupérée dans la feuille de style par défaut de l’agent utilisateur |
En pratique, la valeur la plus utilisée parmi ces quatre va être inherit
. Notez également que le support pour la valeur revert
n’est pas encore acquis pour la plupart des navigateurs. Je n’ai évoqué cette valeur ici que par souci d’exhaustivité mais vous déconseille de l’utiliser pour le moment. Pour cette raison, je ne l’évoquerai plus dans la suite de ce cours.
See the Pen Cours HTML CSS 3.6.6 by Pierre (@pierregiraud) on CodePen.
Ici, on définit un h1{font-family: initial;}
en CSS. Ainsi, c’est la valeur de font-family
définie pour cet élément dans la feuille de style par défaut du navigateur qui va être appliquée. En l’occurrence, dans mon cas, cela va être la valeur Times
.
Ensuite, on demande explicitement à ce que les éléments de liste li
héritent de la valeur donnée à la propriété margin-left
à leur parent. Pour notre première liste, on définit margin-left: 50px
. Les éléments li
vont donc également posséder une marge extérieure gauche de 50px par rapport à la liste en soi qui est leur élément parent.
Pour notre deuxième liste, en revanche, on a défini une marge gauche de 10px seulement. Les éléments de liste vont donc utiliser cette même valeur pour leur propriété margin-left
et être décalés de 10px par rapport à la liste en soi.
Conclusion sur les mécanismes de cascade et d’héritage en CSS
Les mécanismes de cascade et d’héritage en CSS vont permettre de définir via un ensemble de règles quels styles vont être appliqués à quel élément en cas de conflit.
Ces mécanismes vont en pratique très souvent entrer en jeu. En effet, la plupart des sites sont aujourd’hui complexes et vont utiliser plusieurs feuilles de styles (fichiers CSS) différentes qui vont définir de nombreuses règles à appliquer à chaque élément.
Comprendre comment ces mécanismes fonctionnent et connaitre ces règles est essentiel et fondamental puisque cela va nous permettre de toujours obtenir le résultat visuel espéré.
Notez que la cascade et l’héritage sont le cœur même du CSS et sont en grande partie sa puissance puisque ces mécanismes vont nous permettre d’un côté de pouvoir « surcharger » des styles en utilisant des sélecteurs plus ou moins précis et de l’autre côté de transmettre des styles d’un élément parent à ces enfants et donc nous éviter de définir tous les styles voulus pour chaque élément.
Salut,
Il me semble qu’il y ait une petite erreur.
“ Enfin, un dernier paragraphe possède à la fois un attribut class= »bigorange », un attribut id= »green » et un attribut style dans lequel nous allons directement préciser un comportement pour la propriété color qui ne s’appliquera donc qu’à cet élément.”
C’est un attribut id=“yellow”, non ?
Bonjour,
Oui, c’est corrigé, merci !