Cascade et héritage

Cette traduction est en cours.

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 ciblant un élément 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 la plus récente 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 parents vers les éléments 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.

 

Cetaines 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 stressant !

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 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 provides four special universal property values for controlling inheritance. Every CSS property accepts these values.

inherit
Sets the property value applied to a selected element to be the same as that of its parent element. Effectively, this "turns on inheritance".
initial
Sets the property value applied to a selected element to be the same as the value set for that property on that element in the browser's default style sheet. If no value is set by the browser's default style sheet and the property is naturally inherited, then the property value is set to inherit instead.
unset
Resets the property to its natural value, which means that if the property is naturally inherited it acts like inherit, otherwise it acts like initial.

Note : There is also a newer value, revert, which has limited browser support.

Note : See Origin of CSS declarations in Introducing the CSS Cascade for more information on each of these and how they work.

We can look at a list of links and explore how the universal values work. The live example below allows you to play with the CSS and see what happens when you make changes. Playing with code really is the best way to get to grips with HTML and CSS.

Par exemple :

  1. The second list item has the class my-class-1 applied. This sets the color of the <a> element nested inside to inherit. If you remove the rule how does it change the color of the link?
  2. Do you understand why the third and fourth links are the color that they are? Check the description of the values above if not.
  3. Which of the links will change color if you define a new color for the <a> element — for example a { color: red; }?

 

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

En CSS, la propriété raccourci all can be used to apply one of these inheritance values to (almost) all properties at once. Its value can be any one of the inheritance values (inherit, initial, unset, or revert). It's a convenient way to undo changes made to styles so that you can get back to a known starting point before beginning new changes.

In the below example we have two blockquotes. The first has styling applied to the blockquote element itself, the second has a class applied to the blockquote which sets the value of all to unset.

 

Try setting the value of all to some of the other available values and observe what the difference is.

Comprendre la cascade

We now understand why a paragraph nested deep in the structure of your HTML is the same color as the CSS applied to the body, and from the introductory lessons we have an understanding of how to change the CSS applied to something at any point in the document — whether by assigning CSS to an element or creating a class. We will now take a proper look at how the cascade defines which CSS rules apply when more than one thing could style an element.

There are three factors to consider, listed here in decreasing order of importance. Earlier ones overrule later ones:

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

We will look at these from the bottom up, to see how browsers figure out exactly what CSS should be applied.

Ordre d'apparition dans le source 

We have already seen how source order matters to the cascade. If you have more than one rule, which has exactly the same weight, then the one that comes last in the CSS will win. You can think of this as rules which are nearer the element itself overwriting early ones until the last one wins and gets to style the element.

Spécificité

Once you understand the fact that source order matters, at some point you will run into a situation where you know that a rule comes later in the stylesheet, but an earlier, conflicting, rule is applied. This is because that earlier rule has a higher specificity — it is more specific, and therefore is being chosen by the browser as the one that should style the element.

As we saw earlier in this lesson, a class selector has more weight than an element selector, so the properties defined on the class will override those applied directly to the element.

Something to note here is that although we are thinking about selectors, and the rules that are applied to the thing they select, it isn't the entire rule which is overwritten, only the properties which are the same.

This behavior helps avoid repetition in your CSS. A common practice is to define generic styles for the basic elements, and then create classes for those which are different. For example, in the stylesheet below we have defined generic styles for level 2 headings, and then created some classes which change only some of the properties and values. The values defined initially are applied to all headings, then the more specific values are applied to the headings with the classes.

 

Let's now have a look at how the browser will calculate specificity. We already know that an element selector has low specificity and can be overwritten by a class. Essentially a value in points is awarded to different types of selectors, and adding these up gives you the weight of that particular selector, which can then be assessed against other potential matches.

The amount of specificity a selector has is measured using four different values (or components), which can be thought of as thousands, hundreds, tens and ones — four single digits in four columns:

  1. Thousands: Score one in this column if the declaration is inside a style attribute, aka inline styles. Such declarations don't have selectors, so their specificity is always simply 1000.
  2. Hundreds: Score one in this column for each ID selector contained inside the overall selector.
  3. Tens: Score one in this column for each class selector, attribute selector, or pseudo-class contained inside the overall selector.
  4. Ones: Score one in this column for each element selector or pseudo-element contained inside the overall selector.

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

The following table shows a few isolated examples to get you in the mood. Try going through these, and making sure you understand why they have the specificity that we have given them. We've not covered selectors in detail yet, but you can find details of each selector on the MDN  selectors reference.

Selector Thousands Hundreds Tens Ones Total specificity
h1 0 0 0 1 0001
h1 + p::first-letter 0 0 0 3 0003
li > a[href*="en-US"] > .inline-warning 0 0 2 2 0022
#identifier 0 1 0 0 0100
No selector, with a rule inside an element's style attribute 1 0 0 0 1000

Before we move on, let's look at an example in action.

 

So what's going on here? First of all, we are only interested in the first seven rules of this example, and as you'll notice, we have included their specificity values in a comment before each one.

  • The first two selectors are competing over the styling of the link's background color — the second one wins and makes the background color blue because it has an extra ID selector in the chain: its specificity is 201 vs. 101.
  • The third and fourth selectors are competing over the styling of the link's text color — the second one wins and makes the text white because although it has one less element selector, the missing selector is swapped out for a class selector, which is worth ten rather than one. So the winning specificity is 113 vs. 104.
  • Selectors 5–7 are competing over the styling of the link's border when hovered. Selector six clearly loses to five with a specificity of 23 vs. 24 — it has one fewer element selectors in the chain. Selector seven, however, beats both five and six — it has the same number of sub-selectors in the chain as five, but an element has been swapped out for a class selector. So the winning specificity is 33 vs. 23 and 24.

Note : Cet exemple est simplificateur. In actuality, each selector type has its own level of specificity that cannot be overwritten by selectors with a lower specificity level. Par exemple, un million de class sélecteurs combinés ne prendront jamais le dessus sur un id selector.

A more accurate way to evaluate specificity would be to score the specificity levels individually starting from highest and moving on to lowest when necessary. Only when there is a tie between selector scores within a specificity level do you need to evaluate the next level down; otherwise, you can disregard the lower specificity level selectors since they can never overwrite the higher specificity levels.

!important

There is a special piece of CSS that you can use to overrule all of the above calculations, however you should be very careful with using it — !important. This is used to make a particular property and value the most specific thing, thus overriding the normal rules of the cascade.

Take a look at this example where we have two paragraphs, one of which has an ID.

 

Let's walk through this to see what's happening — try removing some of the properties to see what happens if you are finding it hard to understand:

  1. You'll see that the third rule's color and padding values have been applied, but the background-color hasn't. Why? Really all three should surely apply, because rules later in the source order generally override earlier rules.
  2. However, The rules above it win, because class selectors have higher specificity than element selectors.
  3. Both elements have a class of better, but the 2nd one has an id of winning too. Since IDs have an even higher specificity than classes (you can only have one element with each unique ID on a page, but many elements with the same class — ID selectors are very specific in what they target), the red background color and the 1 pixel black border should both be applied to the 2nd element, with the first element getting the gray background color, and no border, as specified by the class.
  4. The 2nd element does get the red background color, but no border. Why? Because of the !important declaration in the second rule — including this after border: none means that this declaration will win over the border value in the previous rule, even though the ID has higher specificity.

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.

It is useful to know that !important exists so that you know what it is when you come across it in other people's code. However, we strongly recommend that you never use it unless you absolutely have to. !important changes the way the cascade normally works, so it can make debugging CSS problems really hard to work out, especially in a large stylesheet.

One situation in which you may have to use it is when you are working on a CMS where you can't edit the core CSS modules, and you really want to override a style that can't be overridden in any other way. But really, don't use it if you can avoid it.

The effect of CSS location

Finally, it is also useful to note that the importance of a CSS declaration depends on what stylesheet it is specified in — it is possible for users to set custom stylesheets to override the developer's styles, for example the user might be visually impaired, and want to set the font size on all web pages they visit to be double the normal size to allow for easier reading.

En résumé

Conflicting declarations will be applied in the following order, with later ones overriding earlier ones:

  1. Declarations in user agent style sheets (e.g. the browser's default styles, used when no other styling is set).
  2. Normal declarations in user style sheets (custom styles set by a user).
  3. Normal declarations in author style sheets (these are the styles set by us, the web developers).
  4. Important declarations in author style sheets
  5. Important declarations in user style sheets

It makes sense for web developers' stylesheets to override user stylesheets, so the design can be kept as intended, but sometimes users have good reasons to override web developer styles, as mentioned above — this can be achieved by using !important in their rules.

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. Can you use one of the special values we looked at in the Controlling inheritance section to write a declaration in a new rule that will reset the background color back to white, without using an actual color value?

If you make a mistake, you can always reset it using the Reset button. If you get really stuck, take a look at the solution here.

À 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