Retour au cours

frontend / css

Couleurs et unités : rem, vh, clamp, calc

Leçon 31 exercice

Explication

Ce que vous allez apprendre

  • Distinguer em (relatif au parent, cumulatif) de rem (relatif à la racine, stable)
  • Choisir entre vh/vw et dvh selon le risque de barre d'adresse mobile
  • Utiliser clamp() pour une taille fluide sans multiplier les media queries
  • Choisir un format de couleur adapté (hex, hsl, oklch) selon le besoin de variation
  • Combiner min(), max() et calc() pour des dimensions robustes

Dans quel contexte ?

Sur landing.css, un titre en font-size: 3rem déborde du conteneur sur un écran de smartphone en portrait, obligeant à ajouter une media query dédiée à chaque nouveau palier de largeur testé. En remplaçant par font-size: clamp(1.5rem, 4vw + 1rem, 3.5rem), le titre s'adapte en continu à la largeur de l'écran, sans jamais dépasser les bornes définies ni nécessiter de media query supplémentaire.

Étape 1 : une unité n'est jamais neutre

Choisir une couleur ou une unité de mesure n'est pas un détail esthétique : c'est une décision qui détermine comment votre design va réagir face aux changements — un écran plus petit, une préférence utilisateur, un parent redimensionné.

Étape 2 : em, une unité qui s'accumule

Commençons par em, relatif au font-size de l'élément PARENT. Le problème arrive avec l'imbrication : un élément à 1.2em contenant un enfant lui aussi à 1.2em ne fait pas +20% par rapport à la racine, mais +20% par rapport à un parent qui a DÉJÀ +20%.

Étape 3 : rem, la solution stable

Voici pourquoi rem est généralement préféré : il se réfère TOUJOURS au font-size de la balise <html>, quel que soit le niveau d'imbrication. Le calcul reste stable et prévisible, sans effet cumulatif surprise.

UnitéRelative àCumulative avec l'imbrication ?
emfont-size de l'élément parentOui (piège classique)
remfont-size de <html>Non, toujours stable
vh / vwDimensions du viewportNon
%Dimension du parentDépend de la propriété

Piège fréquent

Imbriquer plusieurs éléments en font-size: 1.2em fait grossir le texte à chaque niveau d'imbrication, un effet cumulatif rarement voulu. Préférez rem dès que la taille ne doit pas dépendre du niveau d'imbrication dans le DOM.

Étape 4 : les unités de viewport, et leur piège mobile

Passons à vh/vw, relatives à la taille visible de la fenêtre, parfaites pour des sections plein écran. Sur mobile pourtant, la barre d'adresse apparaît et disparaît en scrollant, faisant varier la hauteur réellement disponible : un 100vh calculé au chargement peut alors dépasser l'espace visible.

Étape 5 : la correction récente, dvh

L'unité dvh (dynamic viewport height) corrige justement ce problème en s'ajustant dynamiquement à cette barre d'adresse mobile — un correctif à connaître dès qu'on vise du plein écran sur téléphone.

Bonne pratique

Remplacez height: 100vh par height: 100dvh dès qu'une section doit occuper tout l'écran sur mobile (une hero section, un menu plein écran). Le comportement reste identique sur desktop, mais évite le débordement causé par la barre d'adresse mobile rétractable.

Étape 6 : clamp(), la fin de nombreuses media queries

Une fois les unités de base comprises, voici un outil plus puissant : clamp(min, idéal, max) fait grandir ou rétrécir une valeur en continu entre deux bornes garanties, sans jamais les dépasser. Au lieu de définir des paliers fixes par media query, on définit une plage de comportement continue.

Et ensuite ?

Une fois ces unités maîtrisées, la prochaine étape logique consiste à appliquer ces mêmes réflexes au texte lui-même, avec la typographie.

Commandes & code

Couleurs et unités

Choisir la bonne unité selon ce qu'on veut faire réagir : la police, le viewport, le parent.

css
/* Formats de couleur */
.couleurs {
    color: red;                        /* mot-clé */
    color: #3366ff;                    /* hexadécimal */
    color: #36f;                       /* hexadécimal court */
    color: rgb(51, 102, 255);          /* rouge vert bleu */
    color: rgba(51, 102, 255, 0.5);    /* + alpha (transparence) */
    color: rgb(51 102 255 / 50%);      /* syntaxe moderne, espaces + slash */
    color: hsl(225, 100%, 60%);        /* teinte saturation luminosité : + intuitif pour varier */
    color: hsl(225 100% 60% / 0.5);
    color: oklch(60% 0.2 260);         /* espace colorimétrique perceptuellement uniforme */
    color: currentColor;               /* hérite de la couleur `color` courante -- utile sur border/fill */
    color: transparent;
}

/* HSL est le plus pratique pour créer des variations d'une même teinte */
:root {
    --teinte-marque: 225;
}
.bouton { background: hsl(var(--teinte-marque) 100% 60%); }
.bouton:hover { background: hsl(var(--teinte-marque) 100% 50%); } /* juste -10% de luminosité */
css
/* Unités relatives à la police */
.texte {
    font-size: 16px;      /* absolu : ne réagit à rien */
    font-size: 1em;       /* relatif au font-size du PARENT (composé si imbriqué -> piège) */
    font-size: 1rem;      /* relatif au font-size de <html> UNIQUEMENT -> prévisible, préféré */
}

/* Unités de viewport */
.hero {
    height: 100vh;   /* 100% de la hauteur du viewport */
    width: 100vw;    /* 100% de la largeur -- attention à la scrollbar qui peut créer un débord */
    height: 100dvh;  /* dynamic viewport height : s'ajuste avec la barre d'adresse mobile */
    font-size: 5vw;  /* réagit à la largeur, utile pour du texte fluide */
}

/* Unités relatives au conteneur */
.boite { width: 50%; }   /* relatif au parent */
.boite { width: 50cqw; } /* relatif au container query (voir leçon container queries) */
css
/* calc() : mélange d'unités différentes dans une seule expression */
.sidebar {
    width: calc(100% - 250px); /* pleine largeur moins une colonne fixe */
}
.titre {
    font-size: calc(1rem + 1vw); /* base fixe + un peu de fluide */
}

/* clamp(min, idéal, max) : typographie/spacing fluides SANS media query */
.titre-hero {
    font-size: clamp(1.5rem, 4vw + 1rem, 3.5rem);
    /* jamais plus petit que 1.5rem, jamais plus grand que 3.5rem,
       entre les deux : suit "4vw + 1rem" */
}

.conteneur {
    padding-inline: clamp(1rem, 5vw, 4rem);
    width: min(90%, 1200px); /* le plus petit des deux -> plafond de largeur simple */
    width: max(300px, 20%);  /* le plus grand des deux -> plancher de largeur */
}
UnitéRelative à
remfont-size de <html>
emfont-size du parent (cumulatif)
vh/vwdimensions du viewport
%dimension du parent
cqw/cqhdimensions du container query

Résumé

  • rem est préféré à em pour éviter la composition en cascade sur les imbrications profondes.
  • oklch/hsl facilitent la création de variations cohérentes d'une même teinte.
  • clamp() remplace souvent plusieurs media queries pour une taille fluide.
  • dvh corrige le bug classique de 100vh sur mobile avec barre d'adresse rétractable.

Exercices pratiques

1 disponible
1

Mission : un titre hero qui casse sur mobile

Objectif : Diagnostiquer les effets cumulatifs de em et le bug de 100vh sur mobile, puis proposer des unités robustes.

Contexte

Sur landing.css, un titre hero utilise height: 100vh pour occuper tout l'écran, et son texte est stylé avec trois <span> imbriqués ayant chacun font-size: 1.2em. Sur smartphone, l'utilisateur voit une bande vide en bas de la section au chargement, qui disparaît en scrollant ; par ailleurs le texte du troisième span paraît anormalement énorme comparé à la maquette.

Résoudre l’exercice →