Niveau 3 — La Cascade

Calcul des priorités

Mettre ensemble cascade, héritage, spécificité et importance pour résoudre n'importe quel conflit CSS.

1. Comprendre le calcul des priorités

En une phrase : Le calcul des priorités est la synthèse de toutes les règles vues dans les chapitres précédents — c'est l'étape finale qui détermine exactement quelle valeur s'applique à chaque propriété de chaque élément.

Pour résoudre n'importe quel conflit, suis ces 5 étapes dans l'ordre :

MÉTHODE EN 5 ÉTAPES :
━━━━━━━━━━━━━━━━━━━━━

  ÉTAPE 1 — ORIGINE
  ┌─────────────────────────────────────────┐
  │  D'où vient la règle ?                  │
  │  User-Agent / User / Author             │
  └───────────────────┬─────────────────────┘
                      ▼
  ÉTAPE 2 — IMPORTANCE
  ┌─────────────────────────────────────────┐
  │  La déclaration a-t-elle !important ?   │
  │  Si OUI → phase !important séparée      │
  └───────────────────┬─────────────────────┘
                      ▼
  ÉTAPE 3 — SPÉCIFICITÉ
  ┌─────────────────────────────────────────┐
  │  Calcule le score (a, b, c, d)          │
  │  IDs / Classes / Éléments               │
  └───────────────────┬─────────────────────┘
                      ▼
  ÉTAPE 4 — ORDRE SOURCE
  ┌─────────────────────────────────────────┐
  │  En cas d'égalité totale :              │
  │  la dernière déclaration gagne          │
  └───────────────────┬─────────────────────┘
                      ▼
  ÉTAPE 5 — HÉRITAGE
  ┌─────────────────────────────────────────┐
  │  Si aucune règle ne cible l'élément :   │
  │  héritage du parent OU valeur initiale  │
  └─────────────────────────────────────────┘

Algorithme complet de résolution des conflits CSS

2. Méthode de calcul pas à pas

2.1 Comment analyser un conflit

/* Pour chaque propriété conflictuelle, pose-toi ces questions : */

/* 1. Combien de règles s'appliquent à l'élément pour cette propriété ? */
/* 2. Quelle est l'origine de chaque règle ? */
/* 3. Laquelle a !important ? */
/* 4. Calcule la spécificité de chaque sélecteur */
/* 5. Si égalité → regarde l'ordre source */
/* 6. Si aucune règle → héritage ou valeur initiale */

2.2 Notation rapide pour les exercices

/* On peut noter rapidement : */
/* IDs : N */
/* Classes + pseudo-classes + attributs : N */
/* Éléments + pseudo-éléments : N */
/* !important : ★ */

/* Exemple : #header .nav li a::before !important
   → IDs:1, Cls:1, Élm:2, ★
   → (1, 1, 2) + !important */

2.3 Tableau de référence rapide

Sélecteur IDs Classes Éléments Total
* 0 0 0 0
p 0 0 1 1
p span 0 0 2 2
.a 0 1 0 10
.a .b 0 2 0 20
.a .b .c 0 3 0 30
#x 1 0 0 100
#x .a 1 1 0 110
#x .a p 1 1 1 111
:hover 0 1 0 10
[type="text"] 0 1 0 10
::before 0 0 1 1
:is(#a, .b) 1 0 0 100
:where(#a, .b) 0 0 0 0
style="" inline

3. Visualisation

3.1 Le calculateur de priorités visuel

┌──────────────────────────────────────────────────────────────────┐
│                  CALCULATEUR DE PRIORITÉS                        │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Colonne A (inline)   Colonne B (IDs)                           │
│  ┌──────────────┐     ┌──────────────┐                          │
│  │ 0 ou 1       │     │ nombre d'#'  │                          │
│  └──────────────┘     └──────────────┘                          │
│                                                                  │
│  Colonne C (classes)  Colonne D (éléments)                      │
│  ┌──────────────┐     ┌──────────────┐                          │
│  │ .classe      │     │ p, div, span │                          │
│  │ :pseudo      │     │ ::pseudo-élm │                          │
│  │ [attribut]   │     │              │                          │
│  └──────────────┘     └──────────────┘                          │
│                                                                  │
│  Spécial :                                                      │
│  ┌──────────────────────────────────────────┐                   │
│  │ !important → phase séparée (surpasse tout)│                   │
│  │ :is()      → prend le max des arguments  │                   │
│  │ :where()   → toujours 0                  │                   │
│  │ :has()     → prend le max des arguments  │                   │
│  └──────────────────────────────────────────┘                   │
│                                                                  │
│  Comparaison : (a,b,c,d) vs (a',b',c',d')                      │
│  → Comparer a vs a', puis b vs b', etc.                         │
│  → La première colonne qui diffère détermine le gagnant         │
└──────────────────────────────────────────────────────────────────┘

Schéma du calculateur de priorités CSS

3.2 Arbre de décision complet

Arbre de décision pour résoudre un conflit :
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  Conflit détecté sur une propriété
                │
                ▼
  Les règles ont-elles des origines différentes ?
      ├── OUI → L'origine la plus haute gagne
      │         (Author > User > User-Agent)
      └── NON ↓
                │
  Certaines règles ont-elles !important ?
      ├── OUI toutes → comparer spécificité
      ├── NON toutes → comparer spécificité
      └── Mixte → !important gagne toujours
                │
                ▼
  Comparer les scores (a,b,c,d)
      ├── Plus grand score gagne
      └── Scores égaux ↓
                │
  Ordre source : la dernière déclaration gagne
                │
                ▼
  Valeur déterminée ✓

Guide de résolution de conflit complet

4. Exemples avec résolution détaillée

4.1 Conflit simple : deux classes

Résolution détaillée

/* HTML : <p class="a b">Test</p> */

.a { color: red; }    /* (0,0,1,0) = 10 */
.b { color: blue; }   /* (0,0,1,0) = 10 */

/* Résolution :
   1. Même origine : author ✓
   2. Pas de !important ✓
   3. Spécificité : 10 vs 10 → égalité
   4. Ordre source : .b vient en dernier
   → color: blue gagne */

4.2 Conflit avec !important

Résolution détaillée

#id { color: red !important; }      /* (1,0,0) + !important */
.cls { color: blue !important; }    /* (0,1,0) + !important */

/* Résolution :
   1. Même origine : author ✓
   2. Les deux ont !important → phase !important
   3. Spécificité : (1,0,0) vs (0,1,0)
      → 100 vs 10 → ID gagne
   → color: red gagne */

4.3 Conflit multi-niveaux

Résolution détaillée

/* Feuille base.css */
p { color: black; }                  /* (0,0,0,1) = 1 */

/* Feuille theme.css */
p { color: blue; }                   /* (0,0,0,1) = 1 */

/* <style> dans le HTML */
p { color: green; }                  /* (0,0,0,1) = 1 */

/* Inline */
<p style="color: orange;">          /* inline */

/* Résolution :
   1. Même origine : author ✓
   2. Pas de !important ✓
   3. Spécificité : 1 vs 1 vs 1 → égalité
   4. Ordre source : inline gagne (dans le DOM, c'est le dernier nœud)
   → color: orange gagne */

5. Exercices de calcul — Difficulté progressive

Niveau facile

Exercice 1 : Deux éléments

p { color: red; }      /* (0,0,0,1) = 1 */
span { color: blue; }  /* (0,0,0,1) = 1 */

/* HTML : <p><span>Test</span></p> */
/* Quelle couleur a le texte "Test" ? */

Réponse : blue — le span est ciblé directement par span, le p hérite. Le span a sa propre règle avec même score, mais c'est le span qui reçoit color: blue directement.

Exercice 2 : Classe vs élément

.texte { color: red; }    /* (0,0,1,0) = 10 */
p { color: blue; }        /* (0,0,0,1) = 1 */

/* HTML : <p class="texte">Test</p> */

Réponse : red — la classe (10) gagne sur l'élément (1).

Exercice 3 : ID vs classe

#titre { color: red; }    /* (0,1,0,0) = 100 */
.titre { color: blue; }   /* (0,0,1,0) = 10 */

/* HTML : <p id="titre" class="titre">Test</p> */

Réponse : red — l'ID (100) gagne sur la classe (10).

Exercice 4 : Ordre source

.a { color: red; }    /* (0,0,1,0) = 10, premier */
.b { color: blue; }   /* (0,0,1,0) = 10, dernier */

/* HTML : <p class="a b">Test</p> */

Réponse : blue — même spécificité, la dernière règle (.b) gagne.

Niveau intermédiaire

Exercice 5 : Deux classes vs un ID

.a .b { color: red; }    /* (0,0,2,0) = 20 */
#c { color: blue; }      /* (0,1,0,0) = 100 */

/* HTML : <p class="a b" id="c">Test</p> */

Réponse : blue — l'ID (100) gagne sur deux classes (20).

Exercice 6 : !important vs ID

.texte { color: red !important; }   /* 10 + !important */
#titre { color: blue; }              /* 100 sans !important */

/* HTML : <p id="titre" class="texte">Test</p> */

Réponse : red — !important surpasse la spécificité de l'ID.

Exercice 7 : !important vs !important

#a { color: red !important; }    /* (1,0,0) + !important */
.b { color: blue !important; }   /* (0,1,0) + !important */

/* HTML : <p id="a" class="b">Test</p> */

Réponse : red — les deux ont !important, c'est la spécificité qui départage (100 > 10).

Exercice 8 : Sélecteur composé

nav .menu a { color: red; }         /* (0,0,1,2) = 12 */
nav a { color: blue; }              /* (0,0,0,2) = 2 */
a { color: green; }                 /* (0,0,0,1) = 1 */

/* HTML : <nav><div class="menu"><a href="#">Lien</a></div></nav> */

Réponse : rednav .menu a a la spécificité la plus élevée (12 > 2 > 1).

Exercice 9 : Pseudo-classe

.link { color: blue; }              /* (0,0,1,0) = 10 */
a:hover { color: red; }             /* (0,0,1,1) = 11 */

/* HTML : <a href="#" class="link">Survole-moi</a> */

Réponse : Au repos : blue (même score mais .link et a ont le même poids, ici c'est l'ordre source). Au survol : red (11 > 10).

Niveau avancé

Exercice 10 : :is() vs :where()

:is(#id, .cls) { color: red; }      /* (1,0,0) = 100 */
:where(#id, .cls) { color: blue; }   /* (0,0,0) = 0 */

/* HTML : <p id="id" class="cls">Test</p> */

Réponse : red:is() prend la spécificité du plus spécifique (100), :where() est toujours 0.

Exercice 11 : :has() hérite la spécificité

.card:has(.featured) { border: 2px solid gold; }  /* (0,0,2,0) = 20 */
.card { border: 1px solid gray; }                   /* (0,0,1,0) = 10 */
.card:has(#special) { border: 3px solid red; }       /* (0,1,1,0) = 110 */

/* HTML : <div class="card featured" id="special">...</div> */

Réponse : red.card:has(#special) a la spécificité la plus élevée (110 > 20 > 10).

Exercice 12 : Conflit complexe multi-règles

/* CSS */
body p { color: black; }             /* (0,0,0,2) = 2 */
.texte { color: red; }               /* (0,0,1,0) = 10 */
#main .texte { color: blue; }        /* (0,1,1,0) = 110 */
p.texte { color: green; }            /* (0,0,1,1) = 11 */

/* HTML : <body><div id="main"><p class="texte">Test</p></div></body> */

Réponse : blue#main .texte a la spécificité la plus élevée (110).

Exercice 13 : Héritage + !important

body { color: red !important; }     /* hérité par les enfants */
.texte { color: blue; }             /* (0,0,1,0) = 10, pas de !important */

/* HTML : <body><p class="texte">Test</p></body> */

Réponse : red — le !important hérité du body ne peut être écrasé que par un autre !important.

Niveau expert

Exercice 14 : Scénario complet

/* base.css (chargé en 1er) */
* { color: black; }                     /* (0,0,0,0) = 0 */

/* theme.css (chargé en 2ème) */
p { color: blue; }                      /* (0,0,0,1) = 1 */

/* <style> dans le HTML */
.texte { color: green; }                /* (0,0,1,0) = 10 */

/* Feuille override.css (chargée en 3ème) */
p.texte { color: orange !important; }   /* (0,0,1,1) + !important */

/* Inline */
<p class="texte" style="color: purple;">Test</p>

/* Résolution :
   1. Les 4 règles sont de type author
   2. override.css a !important → phase !important
   3. Les autres : inline (spécificité max) mais pas !important
   4. !important gagne sur non-!important
   → color: orange gagne ! */

Réponse : orange — le !important dans override.css bat le style inline car le style inline n'a pas de !important.

Exercice 15 : Calcul de score complet

Calcule le score et détermine le gagnant :

#page #main .content p.intro::first-line { color: red; }
/* IDs: 2, Classes: 2, Éléments: 2 (p + ::first-line) */
/* Score : (0, 2, 2, 2) = 222 */

#main .content p.intro { color: blue; }
/* IDs: 1, Classes: 2, Éléments: 1 */
/* Score : (0, 1, 2, 1) = 121 */

/* 222 > 121 → rouge gagne */

Exercice 16 : Le piège du sélecteur universel

* { color: red; }                      /* (0,0,0,0) = 0 */
* * { color: blue; }                   /* (0,0,0,0) = 0 */
* * * { color: green; }                /* (0,0,0,0) = 0 */
* * * * * * * * * * { color: yellow; } /* (0,0,0,0) = 0 */

/* HTML : <div><p><span>Test</span></p></div> */

/* Tous les scores sont 0 → c'est l'ordre source qui départage */
/* La dernière règle gagne → color: yellow */

Exercice 17 : Debugging réaliste

/* Un développeur se plaint que son style ne s'applique pas : */
/* Il veut que les liens dans .sidebar soient rouges */

.sidebar a { color: blue; }          /* (0,0,1,1) = 11 */

/* Son collègue a ajouté : */
#sidebar .nav-link { color: green; } /* (0,1,1,1) = 111 */

/* Le lien a la classe "nav-link" et est dans #sidebar */

/* Question : pourquoi le bleu ne marche-t-il pas ? */
/* Réponse : le sélecteur du collègue a une spécificité plus élevée (111 > 11)
   Solution : augmenter la spécificité de .sidebar a ou réfactoriser */

Exercice 18 : :where() comme solution

/* Refactoriser avec :where() pour rendre les styles surchargeables */
:where(.sidebar) a { color: blue; }        /* (0,0,0,0) = 0 */
.sidebar .nav-link { color: green; }        /* (0,0,2,0) = 20 */

/* Maintenant il est facile de surcharger le style de .sidebar
   avec un sélecteur de classe simple */

6. Erreurs fréquentes

Erreur 1 : Additionner les scores comme des nombres décimaux
/* FAUX : croire que 10 classes (100) battent 1 ID (100) */
.a .b .c .d .e .f .g .h .i .j { }  /* (0,10,0,0) → c'est bien 100 */
#seul { }                             /* (0,1,0,0) → c'est bien 100 */

/* EN RÉALITÉ : la comparaison se fait colonne par colonne */
/* (0, 10, 0, 0) vs (0, 1, 0, 0) */
/* En colonne b : 10 > 1 → les classes gagnent... sauf que
   en CSS standard, un ID bat toujours N classes.
   Les specifications définissent que si la colonne b d'un sélecteur
   est > 0 et celle de l'autre est aussi > 0, on compare.
   Si un sélecteur a un ID et l'autre non, l'ID gagne. */
Erreur 2 : Oublier que les animations ont la priorité maximale
/* Les transitions et animations CSS ont une priorité
   supérieure à !important pendant leur exécution */
.anime {
  animation: slide 0.3s ease;
}

@keyframes slide {
  from { transform: translateX(0); }
  to { transform: translateX(100px); }
}

/* Même avec transform: none !important sur l'élément,
   l'animation continuera de s'appliquer pendant l'exécution */
Erreur 3 : Confondre héritage et spécificité
/* L'héritage n'est JAMAIS en compétition avec la spécificité */
.parent { color: red; }       /* défini sur le parent */
.enfant { color: blue; }      /* défini directement sur l'enfant */

/* Le style direct de .enfant gagne toujours sur l'héritage du parent,
   même avec une spécificité plus faible.
   L'héritage n'est que le DERNIER recours. */

7. Bonnes pratiques

Guide de résolution de conflits :
  • Toujours commencer par la spécificité — dans 90% des cas, c'est là que se trouve le problème.
  • Utiliser les DevTools pour voir quelles règles sont barrées (écrasées) et pourquoi.
  • Garder une spécificité constante au sein d'un même niveau de composants.
  • Documenter les !important avec des commentaires expliquant pourquoi ils sont nécessaires.
  • Refactoriser dès que possible — ne pas laisser les conflits de spécificité s'accumuler.
  • Utiliser :where() pour les defaults et :is() pour les listes de sélecteurs.

8. Projet d'application

Projet : Débugger une page avec 10 conflits

Crée une page HTML intentionallement cassée avec des conflits de cascade, puis corrige-les :

Conflits à créer puis résoudre :

  • Un style inline qui écrase une classe légitime
  • Un !important qui empêche le responsive
  • Un ID qui écrase un style de composant
  • Un conflit entre deux feuilles de style
  • Un héritage de couleur qui ne fonctionne pas sur un lien

Objectif : Pour chaque conflit, documenter dans un commentaire le score de chaque sélecteur et expliquer pourquoi le style actuel gagne.

9. Révision

Quiz rapide

  1. Quel est l'ordre de résolution des conflits CSS ?
  2. Calcule la spécificité de #header .nav li a::before
  3. Un !important avec spécificité 10 bat-il un !important avec spécificité 100 ?
  4. Quelle est la valeur par défaut du sélecteur universel * ?
  5. Quand :is() et :where() ont-ils le même effet ?
  6. Un style inline peut-il être écrasé par !important ?
  7. Quel est le problème principal de l'abus de !important ?
  8. Comment résoudre un conflit sans utiliser !important ?
  9. Qu'est-ce qui a la priorité la plus haute dans la cascade ?
  10. Peut-on calculer la spécificité d'un sélecteur avec :not() ?
Réponses :
  1. Origine → Importance (!important) → Spécificité → Ordre source → Héritage
  2. (0, 1, 1, 2) = 112 — 1 ID, 1 classe + :before, 2 éléments
  3. Non — l'ID (100) gagne sur la classe (10) même avec !important des deux côtés.
  4. (0, 0, 0, 0) = 0
  5. Quand il n'y a pas d'ID dans les arguments (ou que les arguments n'ont pas de spécificité).
  6. Oui — un !important peut écraser un style inline.
  7. L'escalade de spécificité : on finit par ajouter toujours plus de !important pour surcharger.
  8. En trouvant la cause (sélecteur trop spécifique, mauvaise architecture) et en corrigeant cela.
  9. Les transitions/animations CSS pendant leur exécution.
  10. Oui : :not() a la spécificité de son argument le plus spécifique.

10. Mémo rapide (Cheat Sheet)

┌─────────────────────────────────────────────────────────────┐
│               CHEAT SHEET — CASCADE CSS                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  SPÉCIFICITÉ (de bas en haut) :                             │
│  ─────────────────────────────                              │
│  *                    → (0,0,0)     = 0                     │
│  p, div, span         → (0,0,1)     = 1                     │
│  .classe, :hover      → (0,1,0)     = 10                    │
│  #id                  → (1,0,0)     = 100                   │
│  style=""             → (1,0,0,0)   = inline                │
│  !important           → phase séparée (surpasse tout)       │
│                                                             │
│  :is()   → prend la spécificité du plus spécifique         │
│  :where() → toujours (0,0,0) = 0                           │
│  :has()  → prend la spécificité du plus spécifique         │
│                                                             │
│  ORIGINES (de bas en haut sans !important) :                │
│  ─────────────────────────────────────────────              │
│  User-Agent (navigateur)                                    │
│  User (utilisateur)                                         │
│  Author (développeur)                                       │
│                                                             │
│  AVEC !important → l'ordre s'INVERSE :                     │
│  Author !important > User !important > UA !important        │
│                                                             │
│  HÉRITAGE :                                                 │
│  ─────────                                                  │
│  Héritées : color, font-*, line-height, text-*, cursor     │
│  NON héritées : margin, padding, border, width, background │
│  Mots-clés : inherit, initial, unset, revert               │
│                                                             │
│  RÈGLES D'OR :                                              │
│  ─────────────                                              │
│  1. Garder la spécificité basse                             │
│  2. Éviter les IDs pour le CSS                              │
│  3. Utiliser !important uniquement pour les utilitaires     │
│  4. Utiliser :where() pour les defaults                     │
│  5. Utiliser :is() pour des sélecteurs concis               │
│  6. Résoudre les conflits en corrigeant la cause           │
└─────────────────────────────────────────────────────────────┘

Résumé complet de toutes les règles de la cascade CSS