Cascade et héritage

L'objectif de cette leçon est de développer votre compréhension de concepts parmi les plus fondamentaux de CSS — cascade, spécificité et héritage — qui contrôlent comment CSS s'applique au HTML et comment les conflits de règles sont résolus.

Les contenus de cette leçon vous apparaîtront peut-être moins directement applicables et un peu plus académiques que d'autres parties de ce cours, mais une bonne compréhension de ces sujets vous économisera bien des peines à l'avenir ! Nous vous encourageons donc à travailler cette section avec attention, en vous assurant d'avoir bien saisi les concepts avant d'aller plus loin.

Prérequis : Maîtrise élémentaire de l'informatique, suite logicielle de base installée, compétences élémentaires pour travailler avec des fichiers, connaissance de base du HTML  (cf. Introduction à HTML), et une idée de Comment fonctionne CSS.
Objectif : Découvrir la cascade et la spécificité, et comment l'héritage fonctionne en CSS.

Règles en conflit

CSS est l'acronyme de Cascading Style Sheets, qu'on peut traduire par feuilles de style en cascade et la compréhension de ce premier mot cascading est cruciale — le comportement de cascade est l'un des points clé de CSS.

Vous travaillez sur un projet et vous buttez sur une règle CSS qui ne fonctionne pas. Le problème est souvent que deux règles sont applicables au même élément. La cascade, et le concept relié de spécificité, sont des mécanismes qui permettent de résoudre de tels conflits. La règle qui s'applique de fait pour mettre en forme votre élément n'est peut être pas celle que vous attendiez, du coup il est essentiel de comprendre le fonctionnement de ces mécanismes.

Le concept d'héritage est aussi à garder en tête dans ces situations : certaines propriétés CSS par défaut héritent de la valeur donnée à l'élément parent de l'élément courant, d'autres non. Cela peut être à l'origine de comportements inattendus.

Commençons par passer en revue tous les ingrédients mentionnés ci-dessus, dans un deuxième temps nous les examinerons chacun plus en détails, nous verrons alors comment ils interagissent entre eux et avec le CSS. Au premier abord, tout cela peut faire un peu peur mais avec la pratique, le fonctionnement de ces mécanismes vous semblera plus clair.

La cascade

À un niveau élémentaire, la cascade des styles signifie que l'ordre d'apparition des règles dans le CSS a une importance ; quand deux règles applicables ont la même spécificité, c'est la dernière déclarée qui sera utilisée pour la mise en forme.

Dans l'exemple ci-dessous deux règles pourraient s'appliquer à h1. Au final h1 est coloré en bleu — ces règles ont les mêmes sélecteurs, elles ont donc la même spécificité ; dans ce cas, c'est la règle écrite en dernier dans le CSS qui l'emporte.

 

Spécificité

Quand des règles avec des sélecteurs différents s'appliquent sur un même élément, le navigateur choisit la règle qui a la plus grande spécificité. La spécificité mesure essentiellement combien la sélection est précise :

  • Un sélecteur d'élément est peu spécifique — il cible tous les éléments d'un type donné dans la page — son score est donc faible ;
  • Un sélecteur de classe est plus spécifique — dans la page, il ne cible que les éléments dont l'attribut class a la valeur choisie — son score est plus important.

Voyons cela sur un exemple. Ci-dessous, on retrouve deux règles qui pourraient s'appliquer à h1. Au final h1 est coloré en rouge — le sélecteur de classe donne une plus grande spécificité à sa règle, et du coup c'est cette règle qui est choisie même si elle apparaît plus tôt dans le CSS.

 

Nous expliquerons le score de spécificité et d'autres points reliés un peu plus loin.

Héritage

L'héritage est lui aussi à comprendre dans ce contexte — certaines valeurs pour une propriété CSS se transmettent des éléments parents vers leurs enfants, d'autres non.

Par exemple, si vous fixez une color et une font-family pour un élément, tout élément contenu dans le premier sera mis en forme avec la même couleur et la même police, à moins qu'on lui ait appliqué directement des règles.

 

Certaines propriétés ne se transmettent pas — par exemple si vous attribuez un width de 50% à un élément, aucun de ses descendants n'aura une largeur diminuée de moitié par rapport à celle de son parent. Si c'était le cas, l'usage de CSS serait particulièrement frustrant !

Note : Sur MDN, dans les pages de référence des propriétés CSS, vous trouverez des encarts d'information technique, le plus souvent au pied de la section de spécifications, dans lesquels sont listés nombre de données sur la propriété, notamment si elle est héritée ou non. Voir la section des spécifications de la propriété color, par exemple.

Comprendre comment ces concepts se combinent

Ces trois concepts combinés permettent de décider dans tous les cas quelle règle CSS s'applique à quel élément ; dans les sections ci-dessous nous les verrons en action, ensemble. Cela peut parfois sembler compliqué, mais avec l'expérience les choses vous sembleront plus claires, et de toute façon, si vous oubliez, vous pourrez toujours retrouver tous les détails ici !

Comprendre l'héritage

Commençons par l'héritage. Dans l'exemple ci-dessous nous avons un  <ul>, contenant plusieurs niveaux de listes imbriquées. Nous avons spécifié une bordure, un remplissage (padding) et une couleur de police pour la <ul> extérieure.

La couleur est transmise aux enfants directs, et aussi enfants indirects — les <li> de la première liste, et ceux de la première liste de deuxième niveau. Nous avons ensuite ajouté une classe special à la seconde liste imbriquée et nous lui appliquons une autre couleur. Cette dernière est transmise aux descendants.

 

Les propriétés telles que largeur (comme mentionnée plus haut), marges, remplissage, et bordures ne se transmettent pas par héritage. Si les bordures se transmettaient aux enfants de la liste, on obtiendrait un effet étrange !

Dans la plupart des cas, le sens commun permet de comprendre quelles propriétés sont héritées par défaut et quelles propriétés ne se transmettent pas.

Contrôler l'héritage

CSS propose quatre valeurs spéciales universelles pour contrôler l'héritage. Ces valeurs sont applicables à toute propriété CSS.

inherit
La propriété correspondante prend la valeur définie dans l'élément parent. Dans les faits, cela "active l'héritage".
initial
La propriété correspondante prend la valeur par défaut définie dans la feuille de style du navigateur. Si aucune valeur n'est définie par défaut dans le navigateur et que la propriété est transmise par héritage la propriété est redéfinie à inherit.
unset
Redéfinit la propriété à sa valeur naturelle : si la propriété est transmise par héritage, le comportement est le même que  inherit, sinon il est identique à initial.

Note : Il existe aussi la valeur revert, dont le support par les navigateurs est limité.

Note : Voir Origin of CSS declarations in Introducing the CSS Cascade pour plus d'informations sur chacune de ces valeurs et comment elles fonctionnent.

Voyons maintenant comment les valeurs universelles fonctionnent sur un exemple : une liste de liens. Dans l'exemple live ci-dessous, vous pourrez manipuler CSS et observer directement les effets de vos modifications. Jouer avec le code est vraiment le meilleur moyen pour progresser en HTML et CSS.

Par exemple :

  1. Le deuxième item de la liste est dans la classe my-class-1. Cela définit la couleur de l'élément <a> qu'il contient à inherit. Si vous supprimez cette règle, quelle est la couleur du lien ?
  2. Comprenez vous pourquoi les troisième et quatrième liens sont de la couleur qu'ils sont ? Dans la négative, relisez la description des valeurs spéciales ci-dessus.
  3. Quels liens changeront de couleur si on redéfinit la couleur de l'élément  <a> — par exemple a { color: red; } ?

 

Réinitialiser la valeur de toutes les propriétés

En CSS, la propriété raccourci all peut être utilisée pour appliquer l'une des valeurs d'héritage à (presque) toutes les propriétés d'un coup. Elle peut prendre n'importe laquelle des valeurs d'héritage (inherit, initial, unset, ou revert). C'est un moyen pratique d'annuler les modifications apportées aux styles pour revenir à un point de départ connu avant de commencer de nouvelles modifications.

Dans l'exemple ci-dessous, nous avons deux blocs de citations. Pour le premier, un style est appliqué à l'élément <blockquote> lui-même, le second <blockquote> appartient à une classe qui définit la valeur de all à unset.

 

Essayez de donner à all l'une des autres valeurs possibles et observez les changements.

Comprendre la cascade

Nous comprenons maintenant pourquoi un paragraphe imbriqué profondément dans la structure du code HTML a la même couleur que le <body>, et à partir des parties précédentes, nous comprenons comment changer la mise en forme d'un élément où qu'il soit dans le document — que ce soit par un sélecteur de type ou en créant une classe. Nous allons maintenant examiner comment la cascade détermine la règle CSS qui s'applique quand plusieurs règles ciblent le même élément.

Il y a trois facteurs à prendre en compte, listés ci-dessous par ordre décroissant d'importance. Les premiers critères prennent le dessus sur ceux qui viennent après :

  1. Importance
  2. Spécificité
  3. Ordre d'apparition dans le source

Passons les en revue en partant de la fin, pour voir comment les navigateurs déterminent quel CSS doit être appliqué.

Ordre d'apparition dans le source 

Nous avons déjà vu comment l'ordre d'apparition dans le source compte dans la cascade. Si deux règles avec le même poids s'appliquent alors celle qui vient en dernier dans le CSS l'emporte. L'intuition est la suivante : plus on avance dans le CSS plus on s'approche de l'élément ciblé ; quand une règle le sélectionne, elle écrase la précédente jusqu'à la dernière règle rencontrée dans le source qui l'emporte et met en forme l'élément..

Spécificité

L'ordre des règles dans le source est important. On rencontre pourtant des situations où deux règles ciblent le même élément mais c'est la première écrite dans le source qui s'applique. C'est que la première règle a une spécificité plus élevée —  elle est plus spécifique, elle est donc choisie par le navigateur pour mettre en forme l'élément.

Comme nous l'avons vu plus haut dans cette leçon, un sélecteur de classe a plus de poids qu'un sélecteur d'élément, de sorte que les propriétés définies sur la classe remplaceront celles appliquées directement à l'élément.

Un point important à noter : dans la cascade, on pourrait penser qu'une règle postérieure écrase une règle antérieure. En fait, ce n'est pas la règle toute entière qui est écrasée, mais seulement les propriétés communes aux deux règles qui sont redéfinies par la dernière version rencontrée.

Ce comportement permet d'éviter la répétition dans votre CSS. Une pratique courante consiste à définir des styles génériques pour les éléments de base, puis à créer des classes pour les cas particuiers. Par exemple, dans la feuille de style ci-dessous, nous avons défini des styles génériques pour les titres de niveau 2, puis créé des classes qui ne modifient que certaines des propriétés et valeurs. Les valeurs définies initialement sont appliquées à tous les titres, puis les valeurs plus spécifiques sont appliquées seulement aux titres avec l'attribut classe.

 

Voyons maintenant comment le navigateur calcule la spécificité. Nous savons déjà qu'un sélecteur d'élément a une faible spécificité et peut être écrasé par une classe. Essentiellement, une valeur en points est attribuée à différents types de sélecteurs, et leur addition donne le poids de ce sélecteur particulier, qui peut ensuite être évalué par rapport à d'autres correspondances potentielles.

Le score de spécificité d'un sélecteur est codé par quatre valeurs (ou composants) différentes, qui peuvent être considérées comme des milliers, des centaines, des dizaines et des unités — quatre chiffres simples dans quatre colonnes :

  1. Milliers : ajouter 1 dans cette colonne si la déclaration apparaît dans un  style , (style inline). De telles déclarations n'ont pas de sélecteurs, leur spécificité est toujours simplement 1000.
  2. Centaines : ajouter 1 dans cette colonne pour chaque sélecteur d'ID contenu à l'intérieur du sélecteur global.
  3. Dizaines : ajouter 1 dans cette colonne pour chaque sélecteur de classe, d'attribut ou de pseudo-classe contenu à l'intérieur du sélecteur global.
  4. Unités : ajouter 1 dans cette colonne pour chaque sélecteur d'élément ou pseudo-élément contenu à l'intérieur du sélecteur global.

Note : Le sélecteur  universel (*), les combinateurs (+, >, ~, ' '), et la pseudo-class de négation (:not) n'affectent en rien la spécificité.

Le tableau suivant montre quelques exemples isolés pour vous mettre dans l'ambiance. Assurez-vous de comprendre dans chaque cas la spécificité annoncée. Nous n'avons pas encore couvert les sélecteurs en détail, mais vous pouvez trouver les informations à propos de chaque sélecteur sur la référence MDN des sélecteurs.

Sélecteur Milliers Centaines Dizaines Unités Spécificité
h1 0 0 0 1 0001
h1 + p::first-letter 0 0 0 3 0003
li > a[href*="fr"] > .inline-warning 0 0 2 2 0022
#identifiant 0 1 0 0 0100
pas de sélecteurs, la règle est déclarée dans l'attribut style d'un élément  1 0 0 0 1000

Avant de continuer, regardons un exemple en action.

 

Alors qu'est-ce qui se passe ici ? Tout d'abord, nous ne sommes intéressés que par les sept premières règles de cet exemple, et comme vous le remarquerez, nous avons inclus leurs valeurs de spécificité dans un commentaire avant chacune.

  • Les deux premiers sélecteurs se disputent le style de la couleur d'arrière-plan du lien — le deux gagne et rend la couleur d'arrière-plan bleue car il a un sélecteur d'ID supplémentaire : sa spécificité est de 201 contre 101 pour le sélecteur un.
  • Les sélecteurs trois et quatre se disputent le style de la couleur du texte du lien — le quatre gagne et rend le texte blanc car bien qu'il ait un sélecteur d'élément en moins, le sélecteur manquant est remplacé par un sélecteur de classe, qui vaut dix au lieu de un. La spécificité gagnante est donc de 113 contre 104.
  • Les sélecteurs cinq, six et sept se disputent le style de la bordure du lien en survol. Le six perd clairement devant le cinq avec une spécificité de 23 contre 24 — il a un sélecteur d'éléments en moins dans la chaîne. Le sept, cependant, bat à la fois cinq et six — il a le même nombre de sous-sélecteurs dans la chaîne que cinq, mais un sélectecur d'élément a été échangé contre un sélecteur de classe. La spécificité gagnante est donc de 33 contre 23 et 24.

Note : Cet exemple est simplificateur. En fait, chaque type de sélecteur voit sa spécificité comptée à un niveau qui lui est propre, qui ne peut pas être dépassé par des sélecteurs d'un type avec une spécificité moindre. Par exemple, un million de sélecteurs de class combinés ne prendront jamais le dessus sur un sélecteur d'id.

Une manière plus précise d'évaluer la spécificité serait de noter individuellement les niveaux de spécificité en commençant par le plus élevé et en passant au plus bas si nécessaire. Ce n'est que lorsqu'il existe un lien entre les scores des sélecteurs au sein d'un niveau de spécificité que vous devez évaluer le niveau suivant ; sinon, vous pouvez ignorer les sélecteurs de niveau de spécificité inférieur car ils ne peuvent jamais écraser les niveaux de spécificité supérieurs.

!important

Vous pouvez annuler tous les calculs ci-dessus à l'aide de la valeur CSS spéciale  !important mais vous devez être très prudent avec son utilisation. Ce mot-clé est utilisé pour donner la plus grande spécificité à la propriété à laquelle il s'applique, remplaçant ainsi les règles normales de la cascade.

Jetez un œil à cet exemple : nous avons deux paragraphes, dont l'un a un ID.

 

Regardons ça d'un peu plus près pour mieux comprendre — si vous avez du mal à suivre, supprimez telle ou telle déclaration pour voir ce qui se passe.

  1. Vous verrez que les valeurs de couleur et de remplissage de la troisième règle ont été appliquées, mais pas la couleur d'arrière-plan. Pourquoi ? On pourrait penser que les trois déclarations s'appliquent, puisque la règle en question, venant plus tard dans le code source, prend le dessus sur les règles antérieures.
  2. Mais rappelez vous, les sélecteurs de classe sont plus spécifiques !
  3. Les deux éléments sont dans la classe better, mais le deuxième a aussi l'id  winning. Étant donné que les ID ont une spécificité encore plus élevée que les classes (sur une page, pour une ID donnée, il y a un seul élément,  alors qu'on peut trouver de nombreux éléments dans la même classe — les sélecteurs d'ID sont donc très spécifiques dans ce qu'ils ciblent), le deuxième élément aura une couleur d'arrière-plan rouge et une bordure noire de 1 px ; pour le premier élément, la couleur d'arrière-plan sera grise, sans bordure, comme spécifié par la classe.
  4. Le deuxième élément a un arrière-plan rouge, mais pas de bordure. Pourquoi ? En raison de la déclaration !important dans la deuxième règle — écrit après  border: none,  ce mot-clé signifie que cette déclaration l'emporte sur le border définie dans la règle précédente, même si l'ID a une spécificité plus élevée.

Note : La seule façon de dépasser cette déclaration  !important serait d'ajouter un !important dans une déclaration de même spécificité apparaissant plus bas dans l'ordre du source, ou avec une spécificité plus grande.

Il est utile de connaître  !important , ne serait-ce que pour le reconnaître dans le code des autres. Cependant, nous vous recommandons fortement de ne jamais l'utiliser, sauf en dernier recours. !important modifie le fonctionnement normal de la cascade, de sorte qu'il peut être très difficile de résoudre les problèmes de débogage CSS, en particulier dans une grande feuille de style.

Lorsque vous travaillez sur un CMS où vous ne pouvez pas modifier les modules CSS de base et que vous souhaitez malgré tout remplacer un style, vous serez peut être amené à utiliser !important. Mais vraiment, si vous pouvez l'éviter, ne l'utilisez pas.

Où sont écrites les règles CSS

Enfin, 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 spécifiée — il est possible pour les utilisateurs de définir des feuilles de style personnalisées pour remplacer les styles du développeur, par exemple si un utilisateur est malvoyant, il peut vouloir doubler la taille de la police sur toutes les pages web visitées afin de faciliter la lecture.

En résumé

Les déclarations en conflit seront appliquées dans l'ordre suivant, les déclarations ultérieures remplaçant 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 normales dans les feuilles de style utilisateur (styles personnalisés définis par un utilisateur).
  3. Déclarations normales dans les feuilles de style d'auteur (ce sont les styles définis par nous, les développeurs web).
  4. Déclarations !important dans les feuilles de style d'auteur.
  5. Déclarations !important dans les feuilles de style utilisateur.

Il est logique que les feuilles de style des développeurs web remplacent les feuilles de style utilisateur, de sorte que la conception peut être conservée comme prévu, mais parfois, les utilisateurs ont de bonnes raisons de remplacer les styles des développeur web, comme mentionné ci-dessus — cela peut être réalisé en utilisant !important dans leurs règles.

Activité : jouer dans la cascade

Dans cette activité, nous vous engageons à tenter l'expérience suivante : écrire une règle redéfinissant les couleurs de police et de fond pour les liens par défaut. La contrainte est de réinitialiser la couleur d'arrière-plan en blanc sans utiliser de valeur <color>. Vous pouvez par contre utiliser l'une des valeurs spéciales que nous avons examinées dans la section Contrôler_lhéritage dans une nouvelle règle.

Si vous faites une erreur, vous pouvez toujours remettre à zéro l'exemple live à l'aide du bouton Reset. Si vous êtes vraiment coincé, jetez un coup d'œil à la solution ici.

À suivre

Si vous avez compris cet article, alors, bravo — vous commencez à appréhender les mécanismes fondamentaux de CSS. L'étape suivante est une étude détaillée des sélecteurs.

Si la cascade, la spécificité et l'héritage gardent encore de leur mystère, pas de panique ! C'est vraiment le point le plus compliqué qu'on ait abordé depuis le début de ce cours, et même les web developers professionnels s'y cassent parfois les dents. Notre avis : poursuivez le cours et revenez régulièrement lire cet article, continuez à y réfléchir.

En particulier quand vous rencontrez des comportements étranges où vos règles de style ne s'appliquent pas, revenez ici. Ce pourrait être un problème de spécificité.

Dans ce cours

  1. Cascade and inheritance
  2. CSS selectors
  3. The box model
  4. Backgrounds and borders
  5. Handling different text directions
  6. Overflowing content
  7. Values and units
  8. Sizing items in CSS
  9. Images, media, and form elements
  10. Styling tables
  11. Debugging CSS
  12. Organizing your CSS