Comprendre CSS au lieu de le subir : la cascade, les couches, les container queries et la couleur OKLCH.
Pourquoi ce livre
Tout le monde « connaît » CSS. Presque personne ne le connaît en profondeur. On écrit un sélecteur, deux propriétés, ça marche, et le jour où ça ne marche pas on ajoute un !important en priant. Keith Grant écrit pour exactement ce développeur : celui qui a passé des années avec CSS, qui s'est cogné à des murs, et qui en est sorti frustré.
J'anime des cours de CSS sur ce site, et c'est le livre que j'aurais voulu lire avant. Sa promesse n'est pas une liste d'astuces : c'est de comprendre comment les pièces du langage s'emboîtent. La 2e édition (2024) ajoute en plus tout ce qui a débarqué récemment et qui change la donne : les couches de cascade, le nesting natif, le scope, les container queries, la couleur OKLCH. Dans sa préface, Chris Coyier résume CSS par le slogan d'Othello : une minute pour apprendre les règles, une vie pour les maîtriser. Le livre, c'est le chemin entre les deux.
Les idées qui restent
1La cascade n'est pas un mystère, c'est six arbitrages dans l'ordre
On met des !important partout parce qu'on ne sait pas pourquoi une règle l'emporte sur une autre. Grant démonte le mécanisme : quand deux règles visent le même élément, le navigateur tranche par six critères, dans l'ordre :
- l'origine de la feuille de style ;
- les styles inline ;
- la couche (
@layer) ; - la spécificité du sélecteur (les ID battent les classes qui battent les balises) ;
- la proximité de scope ;
- l'ordre d'apparition.
La spécificité, qu'on croit reine, n'arrive qu'en quatrième. L'exemple du livre : #main-nav a (un ID, une balise) écrase .featured (une classe) ; un seul ID suffit à gagner, et c'est toute la source du problème. Quant à !important, il ne gagne pas en force brute : il fait remonter la déclaration d'un cran dans les origines. Grant prévient que c'est « une rustine naïve » : ça dépanne aujourd'hui, ça crée des ennuis plus tard. Une fois l'ordre connu, on vise le bon levier au lieu d'empiler les rustines.
2Arrête de penser en pixels
Coder chaque taille en pixels, c'est se condamner à toucher quinze valeurs pour redimensionner un composant. La proposition de Grant : em vaut la taille de police de l'élément courant, rem celle de la racine. Définis le padding, les marges et le border-radius d'un composant en em, et une seule modification de font-size redimensionne tout le bloc, proportionnellement.
Le piège des em, c'est qu'ils se composent : ul { font-size: 0.8em } sur des listes imbriquées donne 12,8 px, puis 10,24 px, puis 8,19 px, le texte rétrécit à chaque niveau. Les rem cassent cette chaîne, puisqu'ils repartent toujours de la racine. La règle d'or qu'il donne :
- rem pour la taille de police ;
- pixels pour les bordures ;
- em ou rem pour la plupart des autres propriétés.
Et les custom properties (--ma-couleur) ne sont pas des variables Sass : elles cascadent et héritent. Redéfinir --bg dans un sélecteur .dark repeint automatiquement tout son sous-arbre, sans rien à recompiler.
3Les couches : la priorité qu'on décide au lieu de la subir
Le scénario classique : ton a:any-link (une classe plus une balise) écrase ton .button (une classe), alors que tu veux exactement l'inverse, et tu finis en !important.
La règle @layer règle ça : elle découpe le CSS en couches ordonnées, et une couche déclarée plus tard l'emporte sur les précédentes, quelle que soit la spécificité à l'intérieur. Tu poses l'ordre en une ligne au début de ta feuille : @layer reset, theme, global, layout, modules, utilities;. Conséquence directe : tes classes utilitaires (.text-center) gagnent toujours, sans le moindre !important, simplement parce que leur couche est la dernière.
Le piège à connaître : tout style placé HORS d'une couche bat TOUTES les couches, comme s'il appartenait à une couche prioritaire invisible. Grant le dit noir sur blanc : « les styles définis hors d'une couche priment sur ceux d'une couche ». La règle qui en découle : mets tout dans une couche, sans exception. La spécificité cesse alors d'être un champ de bataille.
4Un module ignore où il est posé
.sidebar .card { … } : voilà la ligne qui pourrit une feuille de style. Ta carte se comporte différemment selon l'endroit où elle tombe, et plus rien n'est prévisible. La règle d'or de Grant est sans appel : « ne jamais utiliser de sélecteur descendant pour modifier un module selon sa place dans la page ».
Un module, c'est une classe racine, stylée indépendamment de son contexte ; une variante se fait avec une classe modifieur (la convention BEM, pour Block-Element-Modifier), jamais avec un sélecteur de contexte :
/* ✗ la carte dépend de l'endroit où elle tombe : imprévisible */
.sidebar .card { background: navy; }
/* ✓ une variante explicite, qui voyage avec le composant */
.card { background: white; }
.card--featured { background: navy; } /* le modifieur BEM */
La 2e édition ajoute un outil qui fait respecter ça par le navigateur lui-même, la règle @scope :
@scope (.media): les styles de la zone ne débordent plus sur le reste de la page ;- le « donut scoping »
@scope (.dropdown) to (.drawer > *): style une zone tout en découpant un trou en son milieu.
Résultat : un composant qu'on déplace n'importe où sans crainte, parce qu'il ne dépend plus de rien autour de lui.
5Les container queries : on interroge le conteneur, pas l'écran
C'est la nouveauté la plus marquante du livre. Une media query regarde la taille de la fenêtre. Le problème : ton module « carte » glissé dans une colonne latérale de 300 px reçoit quand même les styles « grand écran », parce que le viewport, lui, fait 1400 px. Résultat, la carte déborde de sa colonne.
La container query renverse la logique : elle répond à la taille du CONTENEUR qui accueille le module. Tu déclares container-type: inline-size sur le parent, puis tu écris @container (width >= 450px). Le même composant s'adapte alors à l'espace réel qu'on lui laisse, jamais à la taille de l'écran. Grant le formule simplement : « ce qui compte, c'est la taille du conteneur qui accueille le module ».
Deux compléments prolongent l'idée :
- les unités
cqi(1 % de la largeur du conteneur) dimensionnent la typo et les images relativement au conteneur ; - les style queries
@container style(--color-theme: dark)centralisent le mode sombre en un seul endroit au lieu de le dupliquer.
Le résultat, c'est un composant vraiment réutilisable, qui se débrouille dans n'importe quel trou de la mise en page.
6Flexbox ou Grid : du contenu vers l'extérieur, ou du layout vers l'intérieur
On hésite éternellement entre les deux. Grant tranche avec une phrase de Rachel Andrew (membre du groupe de travail CSS du W3C) : « Flexbox part du contenu vers l'extérieur, la grille part du layout vers l'intérieur ». Traduction opérationnelle :
- Flexbox : une seule dimension. C'est le contenu qui dicte la taille, et tu répartis l'espace RESTANT entre les éléments.
- Grid : deux dimensions. C'est TOI qui dessines la structure (les lignes, les colonnes, voire un plan en ASCII art avec
grid-template-areas) et le contenu vient s'y loger.
La règle pratique qui en sort : grid pour la structure de la page, flexbox pour aligner les éléments à l'intérieur d'une zone. Et le trio flex-basis / flex-grow / flex-shrink se lit alors comme un partage du reste :
.principal { flex: 2; } /* prend 2 parts de l'espace restant */
.lateral { flex: 1; } /* en prend 1 → un rapport 2/3 – 1/3, sans calc() */
La question « lequel des deux ? » se règle désormais en deux secondes.
7Pourquoi un z-index: 100 passe quand même derrière
Le grand classique de l'incompréhension : tu mets z-index: 9999 et l'élément reste obstinément caché. Grant désigne le coupable, le contexte d'empilement (stacking context). Un z-index ne classe un élément que DANS son contexte d'empilement, pas dans toute la page.
Or, ajouter un z-index à un élément positionné crée un nouveau contexte dont cet élément devient la racine : tous ses descendants y sont enfermés. Si cette racine est peinte derrière un autre élément, aucun de ses enfants, même à z-index: 100, ne peut passer devant.
Et le piège, c'est que plein de propriétés créent un contexte sans qu'on demande quoi que ce soit :
- une
opacityinférieure à 1 ; - un
transform; - un
filter; - un
position: fixed.
La phrase du livre à retenir : « si un élément est empilé devant un contexte d'empilement, aucun élément de ce contexte ne peut passer devant lui ». Le conseil pratique de Grant : range tous tes z-index dans des custom properties, au même endroit, par paliers de 10, pour garder la main.
8OKLCH : enfin une couleur alignée sur l'œil
En hexadécimal, #1a7a52 et #4fb985 n'ont aucun lien visible : impossible de deviner que c'est la même teinte, en plus clair. OKLCH décrit une couleur par trois valeurs :
- La luminosité : perçue, et perceptuellement uniforme contrairement à HSL, donc deux couleurs à 50 % de luminosité paraissent vraiment aussi claires l'une que l'autre ;
- Le chroma : la vivacité de la couleur ;
- La teinte : un angle sur le cercle des couleurs.
Dans la palette du livre, les trois verts ont tous la même teinte, 165 : une évidence dans le code OKLCH, totalement invisible en hex. Pour obtenir une nuance plus claire, tu gardes la teinte et tu montes la luminosité, point. OKLCH atteint en prime des couleurs plus vives que le sRGB (le gamut P3 des écrans récents) et reste valable si les écrans s'élargissent encore. Côté lisibilité, Grant rappelle le seuil de contraste WCAG de 4,5:1 pour le texte courant, qu'un outil de DevTools calcule pour toi. Tu finis par construire et dériver une palette de tête.
Trois choses que je ne savais pas avant de le lire
- Le raccourci
fontest si dangereux que Grant ne l'utilise que sur<body>: il réinitialise silencieusementfont-weight,font-styleet le reste à leur valeur initiale à chaque fois (ch. 1). - Le sélecteur « hibou lobotomisé »
* + *(de Heydon Pickering) espace des éléments de types variés sans gérer le premier ni le dernier :.stack > * + * { margin-block-start: 1.5em; }(ch. 3). :where()a une spécificité toujours nulle, contrairement à:is()qui prend celle de son argument le plus spécifique, d'oùa:where(:any-link)qui n'ajoute aucun poids (ch. 8).
Mon avis, honnêtement
C'est le livre CSS le plus à jour que je connaisse, et le plus utile à un développeur qui « sait déjà ». Sa force, c'est de traiter CSS comme un système cohérent et non comme un sac d'astuces : tu ressors en comprenant POURQUOI une règle gagne, pas juste quelle incantation tu tapes d'habitude. Les chapitres sur les couches, le scope et les container queries valent à eux seuls l'achat, parce que ce sont les morceaux de CSS arrivés depuis trois ans que la plupart d'entre nous n'ont jamais pris le temps d'apprendre proprement. Et chaque concept s'incarne dans un vrai projet (un café, une appli collaborative, un club de course), pas dans des div abstraites.
Les défauts ? Les idées ne sont pas « de Grant » : ce sont celles de la plateforme web, et ce qu'il fait, c'est les ranger et les expliquer mieux que la doc. Donc si tu cherches une thèse originale comme dans Clean Code ou Designing Data-Intensive Applications, ce n'est pas ce livre-là : c'est une référence, pas un manifeste. Certains chapitres de fin (dégradés, masques, filtres) tiennent plus du catalogue que de la révélation, et quelques fonctionnalités qu'il présente, comme les style queries, n'étaient pas supportées partout à la sortie. À vérifier sur caniuse avant de s'appuyer dessus en production.
Le livre se referme bien : sur les animations qui portent du sens plutôt que de la décoration (signaler qu'un formulaire est parti, attirer l'œil au bon moment), avec le rappel technique qui compte, n'anime que transform et opacity, les seules propriétés que le navigateur peut bouger sans tout recalculer, et respecte la préférence reduced-motion. Son dernier conseil est le bon : reste curieux, ouvre les DevTools sur les pages qui t'impressionnent, et continue d'apprendre. CSS bouge plus vite aujourd'hui qu'à n'importe quel moment de son histoire.
Odilon
Toujours valable en 2026 ?
Oui, et c'est même son meilleur argument : c'est l'édition qui couvre le CSS de 2022-2024, celui que la plupart des développeurs n'ont pas encore intégré. Les couches, le scope, les container queries et OKLCH ne sont pas des gadgets : ils résolvent des problèmes qu'on contournait depuis dix ans. Seul bémol, le support navigateur de quelques nouveautés (les style queries surtout) évolue encore : un coup d'œil sur caniuse.com reste de rigueur avant de s'appuyer dessus en prod.
Pour qui ?
Lisez-le si
- Vous écrivez du CSS régulièrement mais vous le « subissez » plus que vous ne le pilotez
- Vous avez appris CSS il y a plus de cinq ans et vous voulez rattraper couches, scope et container queries
- Vous mettez encore des
!importantsans trop savoir pourquoi ça marche (ou pas) - Vous voulez comprendre la cascade et le contexte d'empilement une fois pour toutes
Passez si
- Vous débutez totalement en HTML/CSS : commencez par une vraie intro, ce livre suppose les bases
- Vous cherchez un livre de design graphique : il touche au design mais reste un livre de développeur
- Vous voulez une thèse originale et provocante : c'est une référence solide, pas un manifeste
Pour aller plus loin
Le cours CSS et le cours HTML de ce site couvrent les fondations en pratique ; le chapitre sur les modules et le scope se prolonge dans tout ce qui touche à l'architecture front maintenable.
Commentaires (0)