Règles d'application des styles CSS et gestion des conflits

Comprendre les notions de cascade, d'héritage et de spécificité

Didier Mouronval

Lequel d'entre nous ne s'est jamais torturé l'esprit pendant des heures en se demandant pourquoi telle règle CSS ne s'appliquait pas aux éléments ciblés par un sélecteur ? Ou pire, pourquoi cette règle s'appliquait à certains éléments et pas à d'autres ?
Je pense que c'est arrivé à tout le monde, surtout en période d'apprentissage.

Cet article va vous expliquer les trois principes permettant d'appliquer des styles à un élément : la cascade, l'héritage et la spécificité.

Commentez

- Titre : Règles d'application des styles CSS et gestion des conflits

- Auteur : Didier Mouronval

- Parution : 7 janvier 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Didier Mouronval. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Voir également :

- Premiers pas avec les CSS

- La syntaxe d'une règle CSS

- Les sélecteurs en CSS

- Glossaire des propriétés CSS

Sommaire

- I. La cascade

- I-A. En cas de conflit

- II. L'héritage

- II-A. En cas de conflit

- III. La spécificité

- III-A. Calcul du poids d'un sélecteur

- III-B. En cas de conflit

- III-C. Exemple

- IV. Conclusion et remerciements

- IV-A. Conclusion

- IV-B. Remerciements

I - La cascade

La cascade correspond à la possibilité pour une balise de définir des styles dans plusieurs règles distinctes.

Au fur et à mesure que vous allez rajouter du contenu et des fonctionnalités à votre site, le besoin de mise en forme va augmenter.
Vous allez donc devoir créer des styles spécifiques pour différents éléments, mais parmi ces éléments, certains devront aussi se voir appliquer des styles liés à d'autres besoins.
Grâce à la cascade, un même élément peut recevoir des styles à partir de plusieurs règles différentes.

Par exemple, vous souhaitez que toutes les balises ayant la classe surround possèdent une bordure fine et arrondie. Vous créez donc la règle CSS :

.surround{
border: 1px solid gray;
border-radius: 25px;
margin: 10px 40px;
padding: 10px;
}

Vous intégrez ensuite sur votre site une notion de type de contenu et souhaitez ajouter une couleur de fond en fonction de ce type. Vous créez donc deux nouvelles classes :

.info{
background-color: #DDEEFF;
}
.idee{
background-color: #FFFFDD;
}

Vous pouvez donc donner à vos éléments plusieurs de ces classes, les règles CSS de chacune de ces classes seront appliquées aux éléments ciblés :

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Code de l'exemple

<p class="surround info">Voici un message d'information. Il contient les styles de <code>.surround</code> et <code>.info</code>.</p>
<p class="surround idee">Voici un message d'idée. Il contient les styles de <code>.surround</code> et <code>.idee</code>.</p>

I-A - En cas de conflit

Il existe un risque de conflit entre plusieurs règles CSS, par exemple, si vous donnez à un élément à la fois la classe info et idee, alors la même règle sera définie deux fois avec deux valeurs différentes.
Dans ce cas, c'est la dernière règle définie dans le code CSS qui sera appliquée :

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Code de l'exemple

<p class="surround idee info">Voici un message créant un conflit. Il contient les styles de <code>.surround</code>, <code>.idee</code> et <code>.info</code>.</p>

Attention : on parle bien de la dernière règle définie dans les feuilles de style et non de la règle correspondant à la dernière classe précisée dans le code HTML.
Dans l'exemple précédent, la classe idee est précisée avant dans le code HTML de l'attribut class, mais c'est son style qui est appliqué, car il apparaît en dernier dans la balise <style>.

Notes.

Le moteur de rendu parse les feuilles de style de façon séquentielle, c'est-à-dire dans l'ordre où elles sont écrites dans le code.

Les styles dits inline, c'est-à-dire définis dans l'attribut HTML style, sont toujours prioritaires sur ceux définis dans les feuilles de style.

En revanche, il n'y a pas de notion de priorité entre les styles internes (balises <style>) et les styles externes (balises <link />). On peut assimiler les feuilles de style externes comme des balises <style> qui seraient présentes à cet emplacement et dont le contenu serait celui appelé par la balise <link />.

II - L'héritage

L'héritage correspond à la possibilité pour une balise de se voir appliquer les styles d'une balise ancêtre.

Rappel.

Les différentes normes HTML ont en commun la façon de dériver du XML, avec comme conséquence que deux balises ne peuvent pas se chevaucher : une balise ouverte à l'intérieur d'une autre doit être fermée avant la fermeture de la balise dans laquelle elle se trouve. Concrètement, cela signifie que vous pouvez faire :

<balise1>
<balise2>
</balise2>
</balise1>

mais pas :

<balise1>
<balise2>
</balise1>
</balise2>

De cette règle stricte découle une représentation arborescente de la structure du document (appelée DOM) qui sera utilisée par CSS.

Dans cette structure, on distingue, par rapport à un élément référent :

- ses ancêtres ;

- son parent ;

- ses frères ;

- ses enfants ;

- ses descendants.

Notez qu'une balise ne peut avoir qu'une seule balise parent, mais elle peut avoir zéro, une ou plusieurs balises des autres types.

Attention : en CSS, la notion de frère (et les sélecteurs d'éléments frères associés + et ~) correspond en fait aux frères suivants (following siblings), c'est-à-dire les balises se trouvant au même niveau de l'arborescence (même balise parent) mais après elle dans le code HTML.

Ainsi, à quelques exceptions près (lister ces exceptions sort du cadre de cet article), les styles appliqués à une balise seront aussi appliqués à toutes ses balises descendantes à moins qu'une règle spécifique annule cet héritage.

Par exemple, vous souhaitez introduire dans votre page la notion de message d'erreur. Ces messages devront être écrits en rouge et en gras avec un fond rouge pâle.
Vous pouvez donc déclarer des règles spécifiques pour ce type de message :

.erreur{
background-color: #FFBCC5;
color: red;
font-weight: bold;
}

Tous les éléments descendants de la balise sur laquelle cette classe est définie hériteront de ce style :

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Code de l'exemple

<p class="surround erreur">Ceci est <i>un message</i> d'<del>erreur</del> (<small>ne pas en abuser</small>). Il contient des balises enfants qui héritent de la classe <code>.erreur</code>.</p>

Imaginons enfin que dans ce message, vous souhaitiez que la balise <code> soit écrite en noir normal avec un fond gris. Vous pouvez annuler l'héritage en définissant une classe pour cet élément :

.format-code{
background-color: #F1F2F1;
color: black;
font-weight: normal;
}

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Code de l'exemple

<p class="surround erreur">Ceci est <i>un message</i> d'<del>erreur</del> (<small>ne pas en abuser</small>). Il contient des balises enfants qui héritent de la classe <code class="format-code">.erreur</code>.</p>

Note.

Dans l'exemple précédent (ainsi que dans les autres), il aurait été possible d'affecter les styles directement aux balises <code>, mais dans le cadre de cet article, nous sommes obligés d'utiliser une classe pour éviter d'impacter les autres éléments qui n'ont pas de rapport avec les exemples.

Il n'est pas possible d'indiquer en CSS que l'on veut empêcher l'héritage. La seule façon de le faire est donc de redéfinir les styles qui ne doivent plus être hérités.
Même si cela peut sembler contraignant au premier abord, c'est néanmoins tout à fait logique : la mise en forme d'une page se doit de respecter une cohérence (notamment visuelle) que l'héritage rend simple à mettre en place.

II-A - En cas de conflit

On peut considérer qu'il y a conflit lorsque plusieurs éléments ancêtres définissent des valeurs différentes pour une même règle.
Dans ce cas, ce sont les styles de l'élément le plus proche dans l'arborescence qui sont appliqués :

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Code de l'exemple

<div class="surround erreur">
<div class="info">
<div>Voici un message créant un conflit. Il hérite à la fois de la classe <code class="format-code">.erreur</code> et de la classe <code class="format-code">.info</code>.</div>
</div>
</div>

Dans l'exemple ci-dessus, l'ancêtre .erreur va faire hériter tous ses descendants de ses styles.
Puis les styles du parent .info vont être appliqués.
On remarque donc que les styles du message héritent du parent (couleur de fond), mais que ceux qui n'ont pas été redéfinis (couleur et graisse du texte) restent appliqués.

Note.

On remarque dans l'exemple précédent que les propriétés liées aux bordures ne sont pas concernées par l'héritage. Ce qui est souhaitable, car si tous les éléments descendants héritaient d'une bordure fine arrondie, le texte deviendrait vite illisible !

III - La spécificité

La spécificité va permettre de déterminer le poids d'un sélecteur pour affecter les styles qui entrent en conflit.

C'est vrai qu'il a été dit qu'en cas de conflit, c'est la dernière règle définie dans le code CSS qui est appliquée.

En réalité, ceci n'est appliqué que dans certains cas ultimes. Lorsqu'un même élément se voit attribuer plusieurs styles contradictoires, CSS va calculer le poids de chacun des sélecteurs ciblant cet élément.
C'est ensuite le style défini par le sélecteur ayant le poids le plus élevé (le plus spécifique) qui sera appliqué.

Note.

On entend ici par sélecteur l'ensemble des termes permettant de cibler un ou plusieurs éléments. Concrètement, il s'agit de tout ce qui précède l'accolade ouvrante.

Cependant, on parle aussi de sélecteur pour chacun des termes définissant un sélecteur composé.

III-A - Calcul du poids d'un sélecteur

Le poids d'un sélecteur est calculé en fonction de la nature et du nombre de chacun de ses termes. Il va permettre de déterminer trois nombres a, b et c  :

- a est le nombre d'identifiants présents dans le sélecteur ;

- b est le nombre de classes, d'attributs ou de pseudoclasses ;

- c est le nombre d'éléments ou de pseudoéléments.

Comme il est rare de trouver plus de dix éléments dans un sélecteur, on peut ensuite se contenter de concaténer ces chiffres pour trouver le poids du sélecteur. Mais retenez tout de même que dix éléments de type classe ne valent pas un élément de type identifiant, ainsi un identifiant, 10 classes ou équivalents et 15 éléments donnent un poids de 1-10-15.

Une fois le poids de chaque sélecteur calculé, les styles appliqués seront ceux de celui ayant le poids le plus élevé.

Notes.

Le sélecteur universel (*) n'est pas pris en compte dans le calcul.

Les sélecteurs d'adjacence (~ et +) et de descendance (>) n'ont pas d'impact sur la spécificité.

La pseudoclasse de négation (:not()) n'est pas prise en compte pour le calcul, mais les éléments à l'intérieur de cette pseudoclasse le sont.

Attention : cette règle de la spécificité ne s'applique que pour les styles entrant en conflit.
Si un sélecteur a et un sélecteur b définissent différents styles pour une même balise, mais qu'un seul de ces styles est présent dans a et dans b, alors tous les autres styles seront appliqués et c'est le poids le plus élevé entre a et b qui déterminera la valeur du style commun.

Exemples.

Sélecteur

Poids

Explications

*

0

le sélecteur universel n'est pas compté

div

0-0-1

un élément

div span

0-0-2

deux éléments

div > ul::before

0-0-3

deux éléments et un pseudoélément

.classe

0-1-0

une classe

a[target=_blank]

0-1-1

un élément et un attribut

div .champ[disabled]

0-2-1

une classe, un attribut et un élément

#identifiant

1-0-0

un identifiant

#main .inner:empty

1-2-0

un identifiant, une classe et une pseudoclasse

#main :not(div)

1-0-1

la pseudoclasse :not() n'est pas comptée, mais son contenu l'est

#main .c1.c2.c3.c4.c5.c6.c7.c8.c9.c10

1-10-0

dix classes n'équivalent pas un identifiant

Note.

La spécificité des règles déclarées dans l'attribut HTML style sera toujours supérieure à celle des règles déclarées dans les feuilles de style.

Attention, le poids du sélecteur n'intervient jamais pour déterminer quelle règle sera héritée d'un ancêtre.
Concernant l'héritage, c'est toujours la règle définie par l'ancêtre le plus proche dans l'arborescence qui est appliquée.

III-B - En cas de conflit

Si deux sélecteurs ont un poids identique et définissent des valeurs différentes pour une règle, c'est alors la dernière règle définie dans le code qui est appliquée.

III-C - Exemple

Dans l'exemple suivant, nous utilisons un élément de classe .surround.
Nous créons différents sélecteurs correspondant à divers exemples qui vont permettre de permuter différentes combinaisons d'identifiants et de classes avec certains styles en conflit, d'autres non (il n'est pas possible de tester avec des sélecteurs de balise sous peine d'impacter l'ensemble de l'article).
Alternez les différentes combinaisons pour voir les répercussions sur l'affichage.

Voici les différents sélecteurs utilisés :

#identifiant{                                         /* Poids : 1-0-0 */
background-color: #3A5486;
box-shadow: 0 0 2px 5px #5096C8;
}
/* Doubler le sélecteur permet de renforcer son poids */
#identifiant2#identifiant2{                          /* Poids : 2-0-0 */
background-color: #5096C8;
box-shadow: 0 0 2px 5px #3A5486;
}
.c2.c3.c4.c5.c6.c7.c8.c9.c10.c11.c12.c13.c14.c15{    /* Poids : 0-14-0 */
background-color: #FFFFDD;
}
/* #identifiant2.c1 ne sera jamais appliqué à cause de #identifiant2#identifiant2 */
#identifiant.c1, #identifiant2.c1{                   /* Poids : 1-1-0 */
background-color: #DDEEFF;
box-shadow: 0 0 2px 5px #5096C8 inset;
}
.c2{                                                 /* Poids : 0-1-0 */
background-color: #FFEEBB;
}
.c3{                                                 /* Poids : 0-1-0 */
background-color: #F1F2F1;
}

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

IV - Conclusion et remerciements

IV-A - Conclusion

Savoir comment sont appliqués les styles notamment en cas de conflit est très important et permet de mieux organiser ses feuilles de style.
En général, il est recommandé de définir les styles des plus généraux aux plus particuliers sans utiliser de sélecteurs trop complexes.

N'oubliez pas non plus que les outils de développement intégrés à tous les navigateurs récents seront vos meilleurs amis pour déterminer quels styles sont appliqués à un élément ainsi que ceux qui ont été écrasés.

IV-B - Remerciements

Je tiens à remercier Torgar, NoSmoking, Bisûnûrs et jreaux62 pour leur aide technique, ainsi que ClaudeLELOUP et f-leb pour leur relecture attentive.

N'hésitez pas à faire part de vos remarques et commentaires sur le forum. Commentez