Variables, transitions, animations et projet
Cours · CSS : mettre en forme le Web (école d'ingénieur), chapitre 10 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
La Librairie du Campus a maintenant une charte, une mise en page en grille et une feuille qui s'adapte du téléphone au grand écran. Mais ouvrons cette feuille : le bleu nuit #1f3a68 y est écrit une douzaine de fois, l'ocre presque autant ; le thème sombre du chapitre 9 répète chaque sélecteur qui porte une couleur ; changer l'espacement général obligerait à retoucher trente déclarations. Et la page est figée : rien ne réagit quand on survole une carte, rien ne signale clairement l'élément qui a le focus au clavier.
Ce dernier chapitre apporte les outils qui rendent une feuille de style maintenable — les variables, calc(), une organisation claire — et ceux qui la rendent vivante — transitions, transformations, animations — sans jamais oublier les visiteurs qui ne veulent pas de mouvement. Il se termine par la feuille complète de la Librairie, qui habille les six pages du site.
Objectifs du chapitre
- Déclarer des propriétés personnalisées (
--accent), les lire avecvar(), prévoir une valeur de repli. - Construire un thème clair et un thème sombre avec des variables et
light-dark(). - Calculer avec
calc()en mêlant les unités. - Animer un changement d'état avec
transition; déplacer, tourner, agrandir avectranslate,rotate,scale. - Écrire une animation avec
@keyframes, et la réserver aux visiteurs qui l'acceptent. - Rendre le focus clavier toujours visible.
- Organiser une feuille de style : sections, nommage des classes, imbrication.
10.1 Les propriétés personnalisées
Une propriété personnalisée (on dit couramment variable CSS) est une propriété dont le nom commence par deux tirets : --accent: #e07a1f;. Elle ne produit aucun effet par elle-même ; sa valeur est lue ailleurs par la fonction var(--accent), qui la remplace là où elle est écrite.
On les déclare d'ordinaire sur la racine du document, la pseudo-classe :root (qui désigne l'élément <html>), pour qu'elles soient connues partout :
:root {
--bleu-nuit: #1f3a68;
--ocre: #e07a1f;
--accent: var(--ocre);
--espace: 1rem;
}
.bouton {
display: inline-block;
padding: calc(var(--espace) * 0.6) calc(var(--espace) * 1.2);
margin-right: var(--espace);
background: var(--accent);
color: var(--texte-bouton, #222);
border-radius: 4px;
font-weight: 700;
text-decoration: none;
}
Jusqu'ici, rien que le chapitre 4 ne savait faire avec des valeurs écrites en dur. La force des variables vient de ce qu'elles sont des propriétés comme les autres : elles suivent la cascade et l'héritage.
Une propriété personnalisée est héritée : un élément qui ne la déclare pas reçoit la valeur de son parent. On peut donc la redéfinir pour une partie du document seulement : tous les descendants de l'élément qui la redéfinit voient la nouvelle valeur. var() est évaluée sur chaque élément, avec la valeur que cet élément a reçue.
Trois boutons et une zone promotionnelle suffisent à le voir :
.secondaire {
--accent: var(--bleu-nuit);
--texte-bouton: #fff;
}
.grand {
--espace: 1.5rem;
}
.promo {
--accent: #f3e3d3;
padding: 0 var(--espace) var(--espace);
border: 2px dashed #8a8070;
}

Le premier bouton utilise les valeurs de la racine : fond ocre, texte #222 (la valeur de repli, car --texte-bouton n'est définie nulle part), marges internes de et pixels. Le deuxième redéfinit --accent et --texte-bouton : fond bleu nuit, texte blanc. Le troisième ne change que --espace : toutes les longueurs qui en dépendent suivent, 14,4 et 28,8 pixels de marge interne, 24 pixels de marge externe. Le dernier bouton n'a aucune classe particulière ; il hérite simplement le --accent de sa zone. Chromium calcule exactement ces valeurs.
Attention — une variable ne remplace pas n'importe quoi
Une variable remplace une valeur, jamais un nom de propriété ni un sélecteur : var(--cote): 0 n'a pas de sens. Et sa valeur n'est vérifiée qu'au moment où elle est utilisée : avec --taille: rouge, la déclaration font-size: var(--taille) est valide à la lecture, mais devient invalide au calcul ; la propriété prend alors sa valeur héritée (ou initiale), et la valeur de repli de var() ne sert pas, puisque la variable existe. L'exercice 10.2 le vérifie.
Méthode — deux étages de variables
- Les valeurs de la charte, nommées d'après ce qu'elles sont :
--bleu-nuit,--ocre,--creme. Elles ne changent jamais. - Les rôles, nommés d'après ce à quoi ils servent :
--fond,--texte,--titre,--accent,--surface. Les règles n'utilisent que les rôles.
Changer de thème, c'est alors réaffecter les rôles, sans toucher aux règles.
10.2 Un thème clair, un thème sombre
Au chapitre 9, le thème sombre répétait chaque sélecteur qui portait une couleur. Avec les rôles, il suffit de réaffecter les variables :
:root {
--fond: #faf8f5;
--texte: #222;
--titre: #1f3a68;
}
@media (prefers-color-scheme: dark) {
:root {
--fond: #15181d;
--texte: #e8e6e3;
--titre: #a9c1ee;
}
}
body {
background: var(--fond);
color: var(--texte);
}
Chaque couleur du thème sombre n'est écrite qu'une fois, et une règle ajoutée plus tard qui utilise var(--titre) fonctionne d'emblée dans les deux thèmes.
10.2.1 La fonction light-dark()
Depuis 2024, tous les navigateurs connaissent une écriture plus compacte : la fonction light-dark(clair, sombre) renvoie sa première valeur si l'élément est affiché en thème clair, la seconde en thème sombre. Le thème de l'élément est donné par la propriété color-scheme :
color-scheme: light dark: la page sait faire les deux, le navigateur suit le réglage du système ;color-scheme: lightoudark: le thème est imposé.
C'est ce que fait la feuille de la Librairie :
:root {
color-scheme: light dark;
--fond: light-dark(var(--creme), #15181d);
--texte: light-dark(var(--encre), #e8e6e3);
--titre: light-dark(var(--bleu-nuit), #a9c1ee);
--surface: light-dark(#fff, #1f242b);
}
[data-theme="clair"] {
color-scheme: light;
}
[data-theme="sombre"] {
color-scheme: dark;
}
Les deux dernières règles permettent de forcer un thème en ajoutant un attribut à la balise <html> : <html lang="fr" data-theme="sombre">. Pour l'instant, on l'écrit à la main ; au livre JavaScript, un bouton « thème sombre » modifiera cet attribut et mémorisera le choix du visiteur. Voici l'accueil avec data-theme="sombre", à 768 pixels.

Toutes les couleurs ont changé, sans une seule règle propre au thème sombre en dehors des variables. Chromium calcule un fond rgb(21, 24, 29), des titres rgb(169, 193, 238), des cartes rgb(31, 36, 43). Les contrastes ont été calculés une fois pour toutes : 14,3:1 pour le texte, 9,8:1 pour les titres sur le fond, 8,6:1 sur les cartes, 7,5:1 pour les prix.
10.3 Calculer avec calc()
calc(expression) calcule une valeur à partir d'additions, de soustractions, de multiplications et de divisions, en mêlant les unités : calc(100% - 2rem) est une largeur que ni les pourcentages ni les rem ne savent exprimer seuls. Les opérateurs + et - doivent être entourés d'espaces ; on ne multiplie ou ne divise que par un nombre sans unité.
Avec une variable d'espacement, calc() construit toute une gamme cohérente : la Librairie n'écrit plus jamais 0.5rem ou 1.5rem, mais calc(var(--espace) / 2) et calc(var(--espace) * 1.5). À partir de 48rem, une seule déclaration agrandit tous les espacements du site :
@media (width >= 48rem) {
:root {
--espace: 1.25rem;
}
}
La gouttière de la grille des cartes, calc(var(--espace) * 1.5), passe ainsi de 24 à 30 pixels, et la marge latérale du contenu, calc(var(--espace) * 1.6), vaut 32 pixels. Les fonctions min(), max() et clamp() du chapitre 9 acceptent les mêmes expressions sans qu'on ait à écrire calc().
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.