LES FONDAMENTAUX
DU CSS

Fondamentaux du CSS

© Nicolas Lamy - Tous droits réservés.

Par Nicolas Lamy
Mise à jour du 6 Février 2026

1. Introduction au CSS

Le CSS (Cascading Style Sheets) est un langage fondamental du web. Il intervient systématiquement dans la création de sites internet modernes, quels que soient leur taille, leur objectif ou leur public. Avant d’apprendre à écrire du CSS, il est indispensable de comprendre pourquoi ce langage existe, ce qu’il fait exactement, et comment il s’articule avec le HTML et le navigateur.

Cette première partie pose des bases solides, conceptuelles et techniques, afin d’éviter les erreurs classiques des débutants : mélange des rôles, duplication de styles, code difficilement maintenable.

1.1 Qu’est-ce que le CSS et son rôle dans le développement web

Le CSS est un langage informatique dédié exclusivement à la présentation visuelle des documents HTML. Il ne permet pas de créer du contenu, mais d’en définir l’apparence.

1.1.1 Le CSS comme langage de présentation

Le CSS est un langage déclaratif.
Cela signifie que vous ne donnez pas des instructions étape par étape (comme dans un langage de programmation), mais que vous déclarez des règles visuelles que le navigateur va appliquer automatiquement.

Exemple simple :

p {
  color: blue;
}

Ce code signifie :

  • le navigateur sélectionne tous les paragraphes (p)
  • il applique la couleur bleue au texte

Le CSS permet de gérer :

  • les couleurs
  • les tailles de texte
  • les marges et espacements
  • les alignements
  • les arrière-plans
  • les bordures

À ce stade, il est essentiel de retenir une idée centrale :

Le CSS ne donne jamais de sens au contenu.
Il ne fait que définir son apparence.

1.1.2 La séparation des responsabilités entre HTML et CSS

Le développement web repose sur une séparation claire des rôles :

  • HTML : structure et sens du contenu
  • CSS : présentation visuelle de ce contenu

Exemple de HTML sans CSS :

<h1>Titre principal</h1>
<p>Paragraphe de texte</p>

Ce code décrit :

  • qu’il s’agit d’un titre
  • suivi d’un paragraphe

Il ne dit rien sur :

  • la couleur
  • la taille
  • la position

C’est volontaire. Le HTML doit rester neutre visuellement.

À l’inverse, le CSS ne doit jamais :

  • créer de contenu
  • modifier la structure
  • changer le sens des éléments

1.1.3 Pourquoi le CSS est indispensable dans un projet web moderne

Sans CSS :

  • toutes les pages se ressemblent
  • l’expérience utilisateur est faible
  • le site manque de lisibilité et de hiérarchie

Le CSS permet :

  • une identité visuelle cohérente
  • une maintenance plus simple
  • un travail collaboratif efficace
  • une adaptation à différents supports

Dans un projet professionnel, aucun site n’existe sans CSS.

1.2 Évolution et standardisation du CSS

Le CSS n’est pas apparu tel qu’on le connaît aujourd’hui. Il a évolué progressivement pour répondre aux besoins du web.

1.2.1 Les premières versions du CSS

À l’origine, le web proposait très peu de possibilités de mise en forme. Les premières versions du CSS permettaient uniquement :

  • définir une couleur de texte
  • changer une police
  • ajouter des marges simples

Ces limitations expliquent pourquoi, historiquement, le HTML contenait parfois des informations visuelles. Le CSS est né pour corriger cette dérive.

1.2.2 L’évolution progressive des fonctionnalités

Avec le temps, le CSS s’est enrichi :

  • nouvelles propriétés
  • plus de précision
  • plus de contrôle visuel

Chaque évolution vise un objectif clair :

  • mieux séparer les rôles
  • faciliter la maintenance
  • améliorer l’expérience utilisateur

1.2.3 Le rôle des standards et des navigateurs

Le CSS est défini par des standards officiels afin que :

  • un même code fonctionne sur différents navigateurs
  • le rendu soit cohérent pour tous les utilisateurs

Le navigateur joue un rôle central :

  • il lit le HTML
  • il lit le CSS
  • il applique les règles selon un ordre précis

1.3 Appliquer du CSS à un document HTML

Il existe plusieurs façons d’appliquer du CSS à une page HTML. Toutes fonctionnent techniquement, mais elles n’ont pas le même intérêt pédagogique ni professionnel.

1.3.1 Styles en ligne : fonctionnement et limites

Le style en ligne consiste à écrire le CSS directement dans la balise HTML, via l’attribut style.

<p style="color: red;">Texte rouge</p>

Fonctionnement :

  • le navigateur lit l’attribut style
  • il applique immédiatement les propriétés définies

Limites majeures :

  • mélange du contenu et de la présentation
  • duplication des styles
  • maintenance très difficile
  • approche non professionnelle à grande échelle

Le style en ligne doit être considéré comme exceptionnel, jamais comme une méthode standard.

Exercice 1.3.1 — Séparer le HTML et le CSS (style inline → fichiers distincts)

Objectif pédagogique
Comprendre concrètement la séparation des responsabilités entre HTML et CSS.

Consignes

On vous fournit le fichier HTML suivant :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Profil</title>
</head>
<body>
  <h1 style="color: black;">Profil utilisateur</h1>
  <p style="color: gray; font-size: 16px;">Développeur web junior</p>
</body>
</html>
  • Supprimez tous les styles en ligne
  • Créez un fichier style.css
  • Déplacez l’intégralité des styles dans ce fichier
  • Reliez correctement le fichier CSS au HTML
  • Le rendu visuel final doit être strictement identique

Livrables attendus

  • index.html
  • style.css

Correction

index.html :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Profil</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Profil utilisateur</h1>
  <p>Développeur web junior</p>
</body>
</html>

style.css :

h1 {
  color: black;
}

p {
  color: gray;
  font-size: 16px;
}

Explications

  • Les styles ont été retirés du HTML
  • Le CSS est centralisé dans un fichier dédié
  • Le HTML retrouve un rôle purement structurel
  • Le code est plus lisible, maintenable et professionnel

1.3.2 Styles internes : cas d’usage et contraintes

Le style interne consiste à écrire le CSS dans une balise <style> située dans le <head> du document.

<style>
  p {
    color: blue;
  }
</style>

Avantages :

  • centralisation des styles sur une page
  • plus lisible que le style en ligne

Limites :

  • non réutilisable sur plusieurs pages
  • peu adapté aux projets de grande taille

Exercice 1.3.2 — Style interne → feuille de style externe

Objectif pédagogique
Comprendre pourquoi la feuille externe est la méthode de référence.

Consignes

On vous fournit ce fichier HTML :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Contact</title>
  <style>
    h1 {
      color: darkblue;
    }

    p {
      color: #333;
    }
  </style>
</head>
<body>
  <h1>Contact</h1>
  <p>contact@email.com</p>
</body>
</html>
  • Supprimez la balise <style>
  • Créez un fichier style.css
  • Déplacez les règles CSS dans ce fichier
  • Conservez exactement le même rendu

Correction

index.html :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Contact</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Contact</h1>
  <p>contact@email.com</p>
</body>
</html>

style.css :

h1 {
  color: darkblue;
}

p {
  color: #333;
}

Explications

  • Le CSS devient réutilisable
  • Le HTML est allégé
  • La structure du projet est plus claire

1.3.3 Feuilles de style externes : bonnes pratiques et organisation

La feuille de style externe est la méthode recommandée dans tous les projets professionnels.

Principe :

  • un ou plusieurs fichiers CSS
  • reliés aux pages HTML
  • responsables uniquement de l’apparence

1.4 Fonctionnement du navigateur face au CSS

Comprendre comment le navigateur traite le CSS permet d’éviter de nombreuses erreurs.

1.4.1 Chargement des feuilles de style

Le navigateur suit un ordre précis :

  • il charge le HTML
  • il repère les liens vers les fichiers CSS
  • il télécharge ces fichiers
  • il applique les règles

1.4.2 Interprétation et application des règles CSS

Chaque règle est analysée :

  • le navigateur vérifie si elle correspond à un élément
  • si oui, elle est appliquée
  • sinon, elle est ignorée

1.4.3 Ordre de lecture et impact sur le rendu final

Le CSS est lu de haut en bas.

p {
  color: blue;
}

p {
  color: red;
}

Résultat : le texte est rouge, car la dernière règle l’emporte.

1.5 Outils de développement pour analyser le CSS

1.5.1 Présentation des DevTools du navigateur

Les navigateurs modernes intègrent des outils de développement permettant :

  • d’inspecter le HTML
  • de voir les styles CSS appliqués
  • de tester des modifications en direct

1.5.2 Inspection des styles appliqués

Les DevTools indiquent :

  • quelles règles sont actives
  • quelles règles sont ignorées
  • d’où provient chaque style

1.5.3 Modification temporaire du CSS dans le navigateur

Les modifications effectuées via les DevTools :

  • sont immédiates
  • n’affectent pas les fichiers
  • disparaissent au rechargement

Exercice 1.5 — Observer et modifier le CSS via les DevTools

Consignes

  • Ouvrez une page HTML avec un fichier CSS
  • Inspectez un paragraphe
  • Modifiez temporairement sa couleur dans les DevTools
  • Rechargez la page

Objectif

  • constater que les changements ne sont pas persistants

1.6 Structure d’une règle CSS

1.6.1 Le rôle du sélecteur

Le sélecteur indique à quels éléments la règle s’applique.

p {
  color: black;
}

Ici, tous les paragraphes sont concernés.

1.6.2 Le couple propriété / valeur

Chaque règle contient :

  • une propriété
  • une valeur
color: black;

1.6.3 Syntaxe complète et erreurs courantes

p {
  color: gray;
  font-size: 14px;
}

Erreurs fréquentes :

  • point-virgule oublié
  • accolade manquante
  • faute de frappe dans une propriété

Exercice 1.6 — Corriger des règles CSS incorrectes

Consignes

Corrigez le CSS suivant pour qu’il soit valide :

p {
  color gray
  font-size 16px
}

Correction

p {
  color: gray;
  font-size: 16px;
}

Explications

  • chaque propriété est suivie de deux-points
  • chaque ligne se termine par un point-virgule
  • les accolades encadrent la règle

1.7 Commentaires et lisibilité du code CSS

1.7.1 Syntaxe des commentaires en CSS

/* Ceci est un commentaire */

1.7.2 Rôle des commentaires dans la maintenance du code

Les commentaires permettent :

  • d’expliquer l’intention du code
  • de structurer un fichier
  • de faciliter la lecture pour soi et pour les autres

Ils n’ont aucun impact sur le rendu visuel.

2. Sélecteurs CSS, cascade et héritage

Dans la partie précédente, vous avez appris à écrire des règles CSS et à comprendre comment le navigateur les applique.
Une question essentielle se pose maintenant :

Comment le navigateur sait-il quels éléments HTML doivent recevoir quelle règle CSS ?

La réponse repose sur trois notions fondamentales :

  • les sélecteurs
  • la cascade
  • l’héritage

Ces notions expliquent la majorité des comportements du CSS. Lorsqu’un style “ne s’applique pas” ou “est écrasé”, la cause se trouve presque toujours ici.

2.1 Sélecteurs simples

Les sélecteurs permettent de désigner les éléments HTML ciblés par une règle CSS.
Les sélecteurs simples sont les plus accessibles et constituent la base de tout le reste.

2.1.1 Sélecteurs par nom de balise

Le sélecteur par nom de balise cible tous les éléments d’un même type HTML, sans distinction.

Exemple :

p {
  color: black;
}

Cette règle signifie :

  • le navigateur sélectionne tous les éléments <p>
  • il applique la propriété color à chacun d’eux

Caractéristiques importantes :

  • ciblage global
  • aucune différenciation possible entre les éléments
  • simplicité maximale

Ce type de sélecteur est souvent utilisé pour :

  • des styles par défaut
  • une base typographique
  • une mise en forme homogène

Limite majeure :

  • impossibilité de cibler un élément précis parmi plusieurs similaires

Exercice 2.1.1 — Comprendre l’effet global d’un sélecteur par balise

Objectif pédagogique
Observer qu’un sélecteur par balise s’applique à tous les éléments concernés, sans exception.

Consignes (HTML à produire par les élèves)

  • Créez une page HTML complète et valide
  • Dans le <body>, ajoutez :
    • trois paragraphes avec des contenus textuels différents
  • Ne mettez aucune classe ni identifiant

Consignes CSS

  • Écrivez une règle CSS qui cible les paragraphes par leur nom de balise
  • Appliquez une même couleur de texte à tous

Correction

HTML :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Sélecteur par balise</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <p>Introduction</p>
  <p>Contenu principal</p>
  <p>Conclusion</p>
</body>
</html>

CSS :

p {
  color: blue;
}

Explications

  • les trois paragraphes sont ciblés
  • aucune distinction n’est possible
  • le sélecteur agit de manière globale

2.1.2 Sélecteurs de classe et logique de réutilisation

Le sélecteur de classe permet de cibler un ou plusieurs éléments spécifiques, indépendamment de leur type HTML.

Une classe est définie avec l’attribut class en HTML.

Exemple HTML :

<p class="important">Texte important</p>

En CSS, une classe est précédée d’un point . :

.important {
  color: red;
}

Caractéristiques fondamentales :

  • une classe peut être utilisée plusieurs fois
  • plusieurs éléments peuvent partager une même classe
  • un élément peut posséder plusieurs classes

La classe est l’outil principal de la réutilisation en CSS.

Exercice 2.1.2 — Introduire une classe pour un ciblage précis

Objectif pédagogique
Comprendre pourquoi la classe est plus adaptée qu’un sélecteur par balise pour cibler un élément précis.

Consignes (HTML à produire par les élèves)

  • Créez une page HTML avec :
    • deux paragraphes
  • Le second paragraphe doit représenter un message important
  • Ajoutez une classe uniquement à ce second paragraphe

Consignes CSS

  • Écrivez une règle CSS ciblant cette classe
  • Modifiez uniquement l’apparence du second paragraphe

Correction

HTML :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Classe CSS</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <p>Message standard</p>
  <p class="important">Message important</p>
</body>
</html>

CSS :

.important {
  color: red;
}

Explications

  • le premier paragraphe n’est pas affecté
  • la classe permet un ciblage précis
  • la règle est réutilisable ailleurs

2.1.3 Sélecteurs d’identifiant et notion d’unicité

Le sélecteur d’identifiant (id) cible un élément unique dans une page HTML.

HTML :

<h1 id="main-title">Titre principal</h1>

CSS :

#main-title {
  color: black;
}

Règle fondamentale :

  • un identifiant ne doit apparaître qu’une seule fois par page

Différences essentielles avec les classes :

  • classe : réutilisable
  • id : unique
  • id : plus spécifique

Exercice 2.1.3 — Identifier un élément unique

Objectif pédagogique
Comprendre la notion d’unicité liée aux identifiants.

Consignes (HTML à produire par les élèves)

  • Créez une page HTML
  • Ajoutez :
    • un titre principal
    • un paragraphe
  • Attribuez un identifiant uniquement au titre

Consignes CSS

  • Écrivez une règle CSS ciblant cet identifiant
  • Le paragraphe ne doit pas être affecté

Correction

HTML :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Identifiant CSS</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1 id="page-title">Accueil</h1>
  <p>Bienvenue sur le site</p>
</body>
</html>

CSS :

#page-title {
  color: darkblue;
}

Explications

  • l’identifiant cible un seul élément
  • il ne doit pas être dupliqué
  • il est réservé aux éléments structurants

2.2 Sélecteurs avancés

Les sélecteurs avancés permettent un ciblage basé sur :

  • des attributs
  • des états
  • des parties spécifiques d’un élément

Ils apportent une grande précision sans modifier le HTML.

2.2.1 Sélecteurs d’attributs

Les sélecteurs d’attributs ciblent des éléments en fonction de leurs attributs HTML.

Exemple :

input[type="text"] {
  border: 1px solid black;
}

Cette règle cible :

  • uniquement les <input>
  • dont l’attribut type vaut "text"

Exercice 2.2.1 — Cibler un élément par attribut

Consignes (HTML à produire par les élèves)

  • Créez un formulaire HTML
  • Ajoutez :
    • un champ de texte
    • un champ de mot de passe

Consignes CSS

  • Écrivez une règle CSS ciblant uniquement le champ texte
  • Le champ mot de passe ne doit pas être affecté

Correction

HTML :

<form>
  <input type="text">
  <input type="password">
</form>

CSS :

input[type="text"] {
  background-color: #f0f0f0;
}

Explications

  • le sélecteur repose sur un attribut
  • aucun id ni classe n’est nécessaire
  • le ciblage reste précis

2.2.2 Pseudo-classes liées aux états utilisateur

Les pseudo-classes décrivent un état particulier d’un élément.

Exemple :

a:hover {
  color: red;
}

La règle s’applique uniquement :

  • lorsque l’utilisateur survole le lien

Les pseudo-classes permettent :

  • une interaction visuelle
  • sans JavaScript
  • de manière déclarative

Exercice 2.2.2 — Styliser un état utilisateur

Consignes (HTML à produire par les élèves)

  • Créez une page avec :
    • un lien hypertexte
  • Le lien doit être visible sans style particulier

Consignes CSS

  • Ajoutez une règle CSS
  • Modifiez l’apparence du lien uniquement au survol

Correction

HTML :

<a href="#">Voir plus</a>

CSS :

a:hover {
  color: red;
}

Explications

  • la règle dépend d’un état
  • elle n’est active que temporairement
  • le CSS réagit à l’utilisateur

2.2.3 Pseudo-éléments et contenu généré

Les pseudo-éléments ciblent une partie spécifique d’un élément.

Exemple :

p::first-letter {
  font-size: 24px;
}

Ils permettent :

  • un ciblage très précis
  • sans modifier le HTML

Exercice 2.2.3 — Cibler une partie d’un élément

Consignes

  • Créez un paragraphe avec un texte suffisamment long

Consignes CSS

  • Utilisez un pseudo-élément
  • Modifiez uniquement la première lettre du paragraphe

Correction

HTML :

<p>Lorem ipsum dolor sit amet.</p>

CSS :

p::first-letter {
  font-size: 24px;
}

Explications

  • seule une partie de l’élément est ciblée
  • le HTML reste inchangé
  • le CSS est très expressif

2.3 Combinaison des sélecteurs

Les sélecteurs peuvent être combinés pour cibler des éléments selon leur position dans la structure HTML.

2.3.1 Sélecteurs descendants

div p {
  color: blue;
}

Cible :

  • tous les paragraphes
  • contenus dans un div

2.3.2 Sélecteurs d’enfants directs

div > p {
  color: blue;
}

Cible :

  • uniquement les paragraphes enfants directs

2.3.3 Sélecteurs de frères adjacents et généraux

h2 + p {
  color: blue;
}

Cible :

  • le paragraphe immédiatement après le titre

Exercice 2.3 — Comprendre la structure HTML et le ciblage

Consignes

  • Créez :
    • un titre
    • plusieurs paragraphes
  • Placez un paragraphe juste après le titre

Consignes CSS

  • Utilisez un sélecteur de frères adjacents
  • Ciblez uniquement le paragraphe suivant le titre

Correction

HTML :

<h2>Section</h2>
<p>Introduction</p>
<p>Contenu</p>

CSS :

h2 + p {
  color: blue;
}

2.4 Cascade et héritage

2.4.1 Principe de la cascade CSS

Lorsque plusieurs règles s’appliquent à un même élément, le navigateur doit choisir.

C’est le principe de la cascade.

2.4.2 Notion d’héritage des propriétés

Certaines propriétés sont héritées automatiquement par les éléments enfants.

Exemple :

body {
  color: black;
}

Les paragraphes héritent de cette couleur.

2.4.3 Propriétés héritables et non héritables

  • héritables : color, font-family
  • non héritables : margin, border

2.5 Spécificité et résolution des conflits

2.5.1 Calcul de la spécificité des sélecteurs

Ordre de spécificité :

  • balise
  • classe
  • identifiant

2.5.2 Conflits entre règles CSS

p {
  color: blue;
}

.important {
  color: red;
}

La classe l’emporte.

2.5.3 Bonnes pratiques pour éviter les conflits

  • privilégier les classes
  • éviter les identifiants pour le style
  • garder des sélecteurs simples
  • structurer le CSS

Exercices récap —
Partie 1 à 2

Exercice 1 — Affiche de festival “Mini Line-up”

Consignes détaillées

Objectif : structurer une “affiche” simple et la styliser avec balises, classes, id, et survol.

  • Créez une page HTML complète (doctype, html lang="fr", head avec meta charset, title).
  • Reliez une feuille externe style.css (obligatoire).
  • Dans le body, créez :
    • un titre principal (h1) contenant le nom du festival, et ajoutez-lui un id festival.
    • un paragraphe d’intro (p) avec une phrase de contexte.
    • une section “Artistes” :
      • un titre de section (h2)
      • une liste non ordonnée (ul) de 4 artistes (li)
      • ajoutez la classe headliner à un seul artiste (le plus important)
    • une section “Infos” :
      • un titre (h2)
      • deux paragraphes : date / lieu
    • un lien (a) “Billetterie” en bas de page (href au choix, même #).
  • CSS à réaliser :
    • stylisez tous les h2 (sélecteur par balise) avec une couleur.
    • stylisez le h1 via l’id #festival (couleur différente).
    • stylisez l’artiste “headliner” via sa classe .headliner (couleur différente + taille de texte plus grande).
    • appliquez un style au lien au survol (:hover) (couleur différente).
    • ajoutez au moins un commentaire CSS pour délimiter une section.

Livrables

  • index.html
  • style.css

Correction (HTML + CSS)

index.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Mini Line-up</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1 id="festival">Festival Mirage</h1>
  <p>Une soirée unique, entre synthés et guitares, au bord de l’eau.</p>

  <h2>Artistes</h2>
  <ul>
    <li class="headliner">Nova Atlas</li>
    <li>Blue Tape</li>
    <li>Signal & Co</li>
    <li>Weekend Static</li>
  </ul>

  <h2>Infos</h2>
  <p>Date : samedi 22 juin</p>
  <p>Lieu : Scène du Phare</p>

  <a href="#">Billetterie</a>
</body>
</html>

style.css

/* Titres de section */
h2 {
  color: #2b2b2b;
}

/* Titre principal (unique) */
#festival {
  color: #0b3d91;
}

/* Artiste mis en avant (réutilisable si besoin) */
.headliner {
  color: #b00020;
  font-size: 20px;
}

/* Interaction utilisateur */
a:hover {
  color: #b00020;
}

Explications

  • h2 par balise : pratique pour des styles globaux.
  • #festival : id = unicité, parfait pour un titre principal unique.
  • .headliner : classe = réutilisable, idéale pour “marquer” un élément.
  • a:hover : pseudo-classe d’état utilisateur, visible uniquement au survol.
  • La feuille externe impose une organisation propre et maintenable.

Exercice 2 — Formulaire “Inscription à la mission”

Consignes détaillées

Objectif : utiliser sélecteurs d’attributs pour cibler des champs sans classe, + pseudo-classes d’état.

  • Créez une page complète liée à style.css.
  • Dans le body :
    • un titre h1 “Inscription à la mission”
    • un formulaire (form) contenant :
      • un champ texte (input type="text") pour “Nom de code”
      • un champ email (input type="email") pour “Contact”
      • un champ mot de passe (input type="password") pour “Accès”
      • un bouton (button) “Valider”
  • Ajoutez un paragraphe sous le formulaire : “Vos données restent confidentielles.”
  • CSS à réaliser (sans ajouter de classes aux inputs) :
    • ciblez uniquement le champ texte par attribut et changez son arrière-plan.
    • ciblez uniquement le champ password par attribut et mettez une bordure.
    • au survol du bouton (:hover), changez sa couleur de texte.
    • faites en sorte que le paragraphe final hérite d’une couleur définie sur body.

Livrables

  • index.html
  • style.css

Correction

index.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Inscription à la mission</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Inscription à la mission</h1>

  <form>
    <input type="text" placeholder="Nom de code">
    <input type="email" placeholder="Contact">
    <input type="password" placeholder="Accès">
    <button type="submit">Valider</button>
  </form>

  <p>Vos données restent confidentielles.</p>
</body>
</html>

style.css

body {
  color: #333;
}

/* Sélecteurs d’attributs */
input[type="text"] {
  background-color: #f0f0f0;
}

input[type="password"] {
  border: 2px solid #333;
}

/* État utilisateur */
button:hover {
  color: #b00020;
}

Explications

  • Les sélecteurs d’attributs permettent un ciblage précis sans classes.
  • body { color: ... } : la propriété color est héritée par les éléments enfants (dont p).
  • border n’est pas héritée : si vous mettiez border sur body, les inputs ne la recevraient pas automatiquement.
  • button:hover : interaction simple et directement visible.

Exercice 3 — “Carte d’articles” et sélecteurs combinés

Consignes détaillées

Objectif : pratiquer descendant vs enfant direct + organisation HTML propre.

  • Page complète + lien vers style.css.
  • Dans le body :
    • un h1 “Actus express”
    • un conteneur principal div avec la classe feed
    • à l’intérieur de .feed, créez deux articles (balise article)
      • chaque article contient :
        • un h2 (titre)
        • un p (résumé)
        • un div avec la classe meta contenant un paragraphe (p) “Publié il y a …”
  • CSS à réaliser :
    • avec un sélecteur descendant, stylisez les p contenus dans .feed (couleur).
    • avec un sélecteur enfant direct, stylisez uniquement les h2 enfants directs des article (couleur).
    • stylisez le paragraphe .meta p avec une autre couleur (descendant).
    • ajoutez au moins un commentaire CSS.

Livrables

  • index.html
  • style.css

Correction

index.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Actus express</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Actus express</h1>

  <div class="feed">
    <article>
      <h2>Un nouveau studio ouvre</h2>
      <p>Un espace dédié à la création visuelle vient d’ouvrir en centre-ville.</p>
      <div class="meta">
        <p>Publié il y a 2 heures</p>
      </div>
    </article>

    <article>
      <h2>Exposition nocturne</h2>
      <p>Une exposition éphémère propose un parcours lumineux en extérieur.</p>
      <div class="meta">
        <p>Publié il y a 1 jour</p>
      </div>
    </article>
  </div>
</body>
</html>

style.css

/* Tous les paragraphes dans le flux */
.feed p {
  color: #333;
}

/* Uniquement les titres h2 directement sous article */
article > h2 {
  color: #0b3d91;
}

/* Métadonnées (plus discrètes) */
.meta p {
  color: #666;
}

Explications

  • .feed p (descendant) cible tous les paragraphes dans .feed, y compris ceux de .meta.
  • article > h2 (enfant direct) évite d’attraper des h2 qui seraient plus profondément imbriqués.
  • .meta p permet de redonner un style spécifique aux paragraphes de métadonnées.

Exercice 4 — “Bande-annonce” et frères adjacents

Consignes détaillées (HTML à produire par les élèves)

Objectif : utiliser le sélecteur de frères adjacents +.

  • Page complète + style.css.
  • Dans le body :
    • un h1 “Bande-annonce”
    • une section contenant :
      • un h2 “Synopsis”
      • un paragraphe (p) qui doit être juste après ce h2 (c’est important)
      • un second paragraphe après le premier
  • CSS :
    • via h2 + p, stylisez uniquement le paragraphe placé immédiatement après le h2 (couleur ou taille).
    • vérifiez que le second paragraphe n’est pas affecté.

Livrables

  • index.html
  • style.css

Correction

index.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Bande-annonce</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Bande-annonce</h1>

  <section>
    <h2>Synopsis</h2>
    <p>Un message arrive trop tard, et tout bascule en une nuit.</p>
    <p>Entre fuite et révélations, le héros comprend que tout était déjà écrit.</p>
  </section>
</body>
</html>

style.css

h2 + p {
  color: #b00020;
  font-size: 18px;
}

Explications

  • h2 + p cible uniquement le paragraphe immédiatement adjacent.
  • C’est un sélecteur structurel : si vous insérez un autre élément entre h2 et p, le style ne s’appliquera plus.
  • Très utile pour mettre en valeur “la première phrase” d’une section sans ajouter de classe.

Exercice 5 — “Capsule typographique” avec pseudo-élément

Consignes détaillées

Objectif : appliquer ::first-letter et comprendre le ciblage partiel.

  • Page complète + style.css.
  • Dans le body :
    • un h1 “Capsule”
    • un paragraphe avec un texte d’au moins 2 lignes (pas juste une phrase courte)
  • CSS :
    • modifiez la taille de la première lettre du paragraphe avec p::first-letter.
    • ajoutez une règle body { color: ... } et observez que le paragraphe hérite de cette couleur.

Livrables

  • index.html
  • style.css

Correction

index.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Capsule</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Capsule</h1>
  <p>
    Dans la ville, les enseignes s’éteignent une à une. Un dernier bruit de pas résonne,
    puis le silence reprend sa place.
  </p>
</body>
</html>

style.css

body {
  color: #333;
}

p::first-letter {
  font-size: 28px;
}

Explications

  • p::first-letter cible une partie de l’élément, sans toucher au HTML.
  • color définie sur body est héritée par le paragraphe (héritage).
  • Le pseudo-élément n’empêche pas l’héritage : la lettre reste dans le paragraphe.

Exercice 6 — “Conflit de styles” (cascade + spécificité)

Consignes détaillées

Objectif : provoquer volontairement des conflits et apprendre à les résoudre proprement.

  • Page complète + style.css.
  • Dans le body :
    • un h1 “Conflit de styles”
    • un paragraphe de base (p) “Message normal”
    • un second paragraphe “Message important”
      • ajoutez-lui la classe important
    • un troisième paragraphe “Message unique”
      • ajoutez-lui un id unique
      • ajoutez-lui aussi la classe important
  • CSS (à écrire dans cet ordre) :
    • une règle par balise p { color: ... }
    • une règle de classe .important { color: ... }
    • une règle d’id #unique { color: ... }
  • Observez le rendu : chaque paragraphe doit avoir la bonne couleur attendue.

Livrables

  • index.html
  • style.css

Correction

index.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Conflit de styles</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Conflit de styles</h1>

  <p>Message normal</p>
  <p class="important">Message important</p>
  <p id="unique" class="important">Message unique</p>
</body>
</html>

style.css

p {
  color: #333;
}

.important {
  color: #b00020;
}

#unique {
  color: #0b3d91;
}

Explications

  • Les trois règles ciblent parfois le même élément (conflit volontaire).
  • Le navigateur résout via la spécificité :
    • p (balise) est moins spécifique que .important (classe)
    • .important est moins spécifique que #unique (id)
  • Ainsi :
    • paragraphe 1 : seulement p → couleur #333
    • paragraphe 2 : p + .important → la classe l’emporte
    • paragraphe 3 : p + .important + #unique → l’id l’emporte
  • Bonnes pratiques : privilégier les classes pour le style, réserver l’id aux éléments vraiment uniques (et éviter de l’utiliser comme “outil de force” au quotidien).

3. Texte, typographie et unités de mesure

Le texte est l’élément le plus présent dans une interface web.
Avant même les images, les animations ou les effets visuels, c’est le texte qui transmet l’information, guide l’utilisateur et structure la lecture.

La typographie joue donc un rôle central dans :

  • la compréhension d’une interface
  • la hiérarchisation de l’information
  • le confort de lecture
  • la crédibilité d’un site

Cette partie vise à comprendre comment le CSS permet de contrôler précisément l’apparence du texte, à la fois d’un point de vue esthétique et fonctionnel.

3.1 Rôle de la typographie dans une interface

La typographie ne se limite pas au choix d’une police.
Elle participe activement à l’expérience utilisateur et à la clarté d’une interface.

3.1.1 Lisibilité et hiérarchie visuelle

La lisibilité désigne la facilité avec laquelle un texte peut être lu et compris.
Elle dépend de plusieurs paramètres combinés :

  • taille du texte
  • espacement entre les lignes
  • contraste avec l’arrière-plan
  • longueur des lignes
  • alignement du texte

La hiérarchie visuelle permet à l’utilisateur de comprendre immédiatement :

  • ce qui est le plus important
  • ce qui est secondaire
  • l’ordre naturel de lecture

En CSS, cette hiérarchie est construite principalement grâce à :

  • font-size
  • font-weight
  • line-height
  • les espacements verticaux

3.1.2 Impact du choix typographique

Le choix typographique influence fortement la perception d’un site :

  • une police peut sembler sérieuse ou ludique
  • institutionnelle ou créative
  • neutre ou expressive

Même avec un HTML identique, deux choix de polices différents peuvent produire des expériences très différentes.

Le CSS permet :

  • de définir une police principale
  • de prévoir des alternatives
  • d’assurer une cohérence sur différents appareils

3.2 Propriétés liées aux polices

Le CSS propose plusieurs propriétés permettant de contrôler l’apparence des caractères.

3.2.1 Définition des familles de polices

La propriété font-family définit la police utilisée pour le texte.

body {
  font-family: Arial, Helvetica, sans-serif;
}

Fonctionnement :

  • le navigateur tente d’utiliser la première police
  • si elle n’est pas disponible, il passe à la suivante
  • la dernière valeur est une famille générique

Familles génériques courantes :

  • serif
  • sans-serif
  • monospace

Bonne pratique :

  • toujours définir plusieurs polices
  • terminer par une famille générique

Exercice 3.2.1 — Définir une famille de police cohérente

Consignes (HTML à produire par les élèves)

  • Créez une page HTML complète.
  • Dans le body :
    • ajoutez un titre principal
    • ajoutez deux paragraphes de texte.
  • Reliez une feuille de style externe style.css.

Consignes CSS

  • Définissez une police principale pour toute la page via body.
  • Ajoutez au moins une police de secours.
  • Terminez par une famille générique.

Correction

HTML :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Typographie</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Manifeste</h1>
  <p>Le texte structure l’interface.</p>
  <p>La typographie guide la lecture.</p>
</body>
</html>

CSS :

body {
  font-family: "Helvetica Neue", Arial, sans-serif;
}

Explications

  • la police est définie globalement
  • les éléments héritent automatiquement de cette propriété
  • la famille générique garantit un rendu minimal sur tous les systèmes

3.2.2 Gestion de la taille du texte

La taille du texte est contrôlée par la propriété font-size.

p {
  font-size: 16px;
}

La taille du texte :

  • influence directement la lisibilité
  • participe à la hiérarchie visuelle
  • interagit avec les unités de mesure

Exercice 3.2.2 — Créer une hiérarchie par la taille

Consignes (HTML à produire par les élèves)

  • Créez une page HTML avec :
    • un h1
    • un h2
    • un paragraphe.
  • Reliez un fichier CSS.

Consignes CSS

  • Définissez une taille différente pour chaque élément.
  • La hiérarchie doit être lisible sans utiliser de couleur.

Correction

CSS :

h1 {
  font-size: 32px;
}

h2 {
  font-size: 24px;
}

p {
  font-size: 16px;
}

Explications

  • la hiérarchie repose uniquement sur la taille
  • les écarts sont suffisamment marqués
  • la structure est immédiatement perceptible

3.2.3 Gestion de l’épaisseur et du style

Deux propriétés principales :

  • font-weight : épaisseur du texte
  • font-style : style du texte
strong {
  font-weight: bold;
}

em {
  font-style: italic;
}

3.3 Mise en forme du texte

La mise en forme concerne la façon dont le texte est disposé et présenté dans l’espace.

3.3.1 Alignement du texte

La propriété text-align permet de contrôler l’alignement horizontal du texte.

Valeurs courantes :

  • left
  • center
  • right
  • justify

Exercice 3.3.1 — Centrer le texte dans un bloc

Consignes (HTML à produire par les élèves)

  • Créez une page HTML complète.
  • Dans le body :
    • une div représentant une carte
    • dans cette div : un h2 et un paragraphe.
  • Aucun style inline.

Consignes CSS

  • Donnez une largeur fixe à la div.
  • Centrez le H2 et le paragraphe à l’intérieur de la div.

Correction

HTML :

<div class="card">
  <h2>Carte éditoriale</h2>
  <p>Ce texte est centré à l’intérieur de son conteneur.</p>
</div>

CSS :

.card {
  width: 300px;
  text-align: center;
}

Explications

  • text-align agit sur le contenu textuel
  • il ne centre pas le bloc lui-même
  • confusion très fréquente chez les débutants

3.3.2 Décoration et transformation du texte

Deux propriétés essentielles :

  • text-decoration
  • text-transform

text-decoration

Ajouter une décoration visuelle au texte :

  • souligner

  • barrer

  • ligne au-dessus

  • enlever le soulignement des liens

  • personnaliser le style du soulignement

text-transform

Changer automatiquement la casse du texte :

  • tout en majuscules

  • tout en minuscules

  • première lettre en majuscule

  • capitaliser les mots

Exercice 3.3.2 — Décorer et transformer un texte

Consignes (HTML à produire par les élèves)

  • Créez :
    • un paragraphe
    • un lien hypertexte.
  • Reliez un fichier CSS.

Consignes CSS

  • Supprimez la décoration du lien.
  • Transformez le texte du paragraphe en majuscules.

Correction

CSS :

a {
  text-decoration: none;
}

p {
  text-transform: uppercase;
}

Explications

  • text-decoration contrôle les éléments décoratifs
  • text-transform agit sur l’affichage, pas sur le contenu HTML
  • le texte reste identique dans le code source

3.3.3 Gestion des espacements

Les espacements influencent fortement la lisibilité.

Propriétés clés :

  • line-height espace entre les lignes
  • letter-spacing -> espace entre les lettres
  • word-spacing -> espace entre les mots

Exercice 3.3.3 — Améliorer la lisibilité d’un paragraphe

Consignes (HTML à produire par les élèves)

  • Créez un paragraphe avec un texte relativement long.
  • Reliez un fichier CSS.

Consignes CSS

  • Utilisez line-height pour améliorez la lisibilité du texte.
  • Jouez avec letter-spacing et word-spacing pour vous entrainer avec les propriétés.

Correction

CSS :

p {
  line-height: 1.6;
  letter-spacing: 0.5px;
}

Explications

  • line-height améliore immédiatement le confort de lecture
  • letter-spacing doit rester subtil
  • des valeurs excessives nuisent à la lisibilité

3.4 Effets typographiques

Les effets doivent toujours rester au service du contenu.

3.4.1 Ajout d’ombres portées sur le texte

La propriété text-shadow permet d’ajouter une ombre.

h1 {
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}

Exercice 3.4.1 — Ajouter une ombre portée à un titre

Consignes (HTML à produire par les élèves)

  • Créez une page avec un h1.
  • Reliez un fichier CSS.

Consignes CSS

  • Ajoutez une ombre portée au titre.
  • L’ombre doit rester lisible et discrète.

Correction

CSS :

h1 {
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}

Explications

  • l’ombre améliore le contraste
  • un excès nuit à la lisibilité
  • l’effet doit rester secondaire

3.4.2 Impact visuel et lisibilité

Une ombre :

  • peut renforcer un titre
  • peut améliorer la lisibilité sur fond clair
  • devient problématique si elle est trop marquée

3.5 Unités de mesure en CSS

3.5.1 Différence entre unités absolues et relatives

En CSS, une unité de mesure indique comment le navigateur doit interpréter une valeur : taille d’un texte, marge, largeur, etc.

Unités absolues : valeur fixe

Une unité absolue correspond à une valeur que le navigateur traite comme stable et indépendante du contexte.

  • px (pixel) est l’unité absolue la plus utilisée en CSS.

Exemple :

p {
  font-size: 16px;
}

Ici, le texte est défini à 16 px, quel que soit :

  • le parent de l’élément
  • le reste de la page

Avantage : prévisible, facile pour débuter
Limite : moins flexible si on veut une interface qui s’adapte bien aux contextes (changements de taille de base, cohérence globale).

Unités relatives : valeur dépendante d’un repère

Une unité relative est calculée à partir de quelque chose : taille du parent, taille de base du document, taille du viewport, etc.

Exemples d’unités relatives :

  • % → par rapport au parent. Ex: width:50%; l’élément prend 50% de la largeur de son parent.

  • em → par rapport à la taille du texte locale: Dimension basée sur la taille de police de l’élément courant.

  • rem → par rapport au texte racine. Dimension basée sur la taille du texte du document (html).

  • vw → par rapport à la largeur de l’écran. Dimension basée sur la largeur de l’écran. Ex: 1vw = 1% largeur fenêtre.

  • vh → par rapport à la hauteur de l’écran. Dimension basée sur la hauteur de l’écran. Ex: 1vh = 1% hauteur fenêtre

Le principe : la valeur s’adapte selon le contexte.

3.5.2 Utilisation des unités px, %, em et rem

Cette sous-partie est centrale : c’est ici que vous comprenez ce que “relatif” veut dire en pratique.

px : taille fixe (simple, local)

px est souvent utilisé pour :

  • des réglages précis
  • des cas simples
  • des premières maquettes CSS
h1 {
  font-size: 32px;
}

Vous obtenez une taille stable.
Mais si vous souhaitez augmenter la taille globale de tout le site, il faudra modifier beaucoup de règles.

% : relatif à une valeur du parent

% correspond à une valeur proportionnelle à celle du parent.

Exemple :

.container {
  font-size: 20px;
}

.container p {
  font-size: 80%;
}

Ici :

  • .container définit une base à 20px
  • le paragraphe fait 80% de 20px, soit 16px

Le point clé : % dépend du parent.

em : relatif à la taille de police du contexte

em est basé sur la taille de police utilisée comme référence dans le contexte courant (souvent le parent).

Exemple :

.container {
  font-size: 20px;
}

.container p {
  font-size: 1em;
}

Ici :

  • 1em vaut 20px

Autre exemple :

.container p {
  font-size: 0.8em;
}

Le paragraphe fait alors 16px.

À quoi sert em ?

  • à créer des tailles proportionnelles
  • à faire évoluer un composant dans son ensemble

Piège classique

.container {
  font-size: 20px;
}

.container p {
  font-size: 1em;
}

.container p span {
  font-size: 1.2em;
}

Les tailles peuvent s’accumuler si l’imbrication devient profonde.

rem : relatif à la racine du document

rem signifie root em.
Il est toujours basé sur la taille définie sur html.

Exemple :

html {
  font-size: 16px;
}

p {
  font-size: 1rem;
}

Ici :

  • 1rem vaut toujours 16px
p {
  font-size: 1.25rem;
}

Donne 20px.

À quoi sert rem ?

  • définir une échelle typographique globale
  • modifier facilement la taille générale du site
  • éviter l’effet cumulatif de em

Règle pratique

  • rem pour les tailles globales
  • em pour des ajustements locaux

Exercice 3.5.2 — Visualiser la différence entre em et rem

Consignes (HTML à produire par les élèves)

  • Créez une page HTML complète reliée à style.css.
  • Dans le body :
    • une div avec la classe container
    • dans cette div :
      • un h2
      • un paragraphe
      • un span autour d’un mot dans le paragraphe
  • Aucun style inline.

Consignes CSS

  • Fixez une taille de base sur html.
  • Fixez une taille différente sur .container.
  • Définissez la taille du paragraphe en em.
  • Définissez la taille du h2 en rem.
  • Définissez la taille du span en em.
  • Modifiez successivement html puis .container pour observer les différences.

Correction

HTML :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>em vs rem</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <h2>Titre du bloc</h2>
    <p>Ce paragraphe contient un mot <span>accentué</span> pour comparer les unités.</p>
  </div>
</body>
</html>

CSS :

html {
  font-size: 16px;
}

.container {
  font-size: 20px;
}

.container p {
  font-size: 1em;
}

.container p span {
  font-size: 1.2em;
}

.container h2 {
  font-size: 1.5rem;
}

Explications

  • p dépend de .container → 20px
  • span dépend du p → 24px
  • h2 dépend de html → 24px
  • Modifier .container change p et span, pas h2
  • Modifier html change h2, pas p ni span

3.5.3 Introduction aux unités liées au viewport

Le viewport correspond à la zone visible du navigateur.

  • vw : 1% de la largeur du viewport
  • vh : 1% de la hauteur du viewport

Exemple :

h1 {
  font-size: 5vw;
}

La taille varie selon la largeur de l’écran.

À quoi ça sert ?

  • titres adaptatifs
  • effets typographiques fluides
  • mises en page réactives simples

Point d’attention Une utilisation excessive peut nuire à la lisibilité.

Exercice 3.5.3 — Créer un titre responsive au viewport

Consignes (HTML à produire par les élèves)

  • Créez une page HTML complète reliée à style.css.
  • Ajoutez :
    • un h1
    • un paragraphe
  • Aucun style inline.

Consignes CSS

  • Définissez la taille du h1 avec vw.
  • Définissez la taille du paragraphe avec une unité classique.
  • Redimensionnez la fenêtre pour observer le comportement.

Correction

CSS :

h1 {
  font-size: 6vw;
}

p {
  font-size: 16px;
}

Explications

  • le h1 s’adapte à la largeur de l’écran
  • le paragraphe reste stable
  • usage typique : titres principaux ou sections “hero”

3.6.1 Principe des polices web

Les polices web permettent d’utiliser une police non installée sur l’appareil de l’utilisateur.
Le navigateur télécharge la police au chargement de la page.

3.6 Polices web

3.6.2 Chargement de polices avec @font-face

@font-face {
  font-family: "MaPolice";
  src: url("mapolice.woff2");
}

body {
  font-family: "MaPolice", sans-serif;
}

Bonnes pratiques :

  • toujours prévoir une police de secours
  • limiter le nombre de variantes
  • utiliser des noms explicites

Exercices récap —
​Partie 1 à 3

Exercice 1 — Le message secret

Consignes HTML

Contexte
Vous devez créer une carte contenant un message confidentiel.

  • Créez une page HTML complète reliée à style.css.
  • Dans le body :
    • une div avec la classe secret-card
    • dans cette div :
      • un h2 (titre de la carte)
      • un paragraphe principal (le message)
      • un second paragraphe plus discret (signature ou note)
  • Ajoutez une classe important sur un seul mot du message principal.
  • Aucun style inline.

Consignes CSS

  • Définissez une police globale.
  • Donnez à .secret-card :
    • une largeur fixe
    • un alignement de texte centré
  • Créez une hiérarchie claire entre titre, message et note.
  • Donnez un style distinct au mot .important.
  • Au survol de la carte, modifiez légèrement l’apparence du texte.

Correction

HTML :

<div class="secret-card">
  <h2>Message confidentiel</h2>
  <p>Le rendez-vous aura lieu à <span class="important">minuit</span>.</p>
  <p>— Source inconnue</p>
</div>

CSS :

body {
  font-family: Arial, sans-serif;
}

.secret-card {
  width: 320px;
  text-align: center;
  line-height: 1.6;
}

.important {
  font-weight: bold;
  color: #b00020;
}

.secret-card:hover {
  opacity: 0.9;
}

Explications

  • la carte est un composant logique structuré
  • la hiérarchie repose uniquement sur la typographie
  • :hover ajoute une interaction simple et maîtrisée

Exercice 2 — La fiche de personnage mystérieux

Consignes HTML

Contexte
Vous créez une fiche de présentation pour un personnage fictif.

  • Créez une page HTML complète.
  • Dans le body :
    • une div avec la classe profile
    • à l’intérieur :
      • un h2 (nom du personnage)
      • un paragraphe de description
      • un paragraphe “Statut : …”
  • Ajoutez une classe alias sur le nom du personnage.
  • Aucun style inline.

Consignes CSS

  • Définissez une police globale.
  • Créez une hiérarchie visuelle claire.
  • Donnez au nom (.alias) :
    • une taille en rem
    • une légère ombre portée
  • Améliorez la lisibilité du texte.

Correction

HTML :

<div class="profile">
  <h2 class="alias">Le Veilleur</h2>
  <p>Personnage discret, rarement aperçu, toujours présent.</p>
  <p>Statut : actif</p>
</div>

CSS :

body {
  font-family: Arial, sans-serif;
}

.profile {
  width: 300px;
  line-height: 1.6;
}

.alias {
  font-size: 1.6rem;
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
}

Explications

  • rem garantit une taille cohérente à l’échelle du document
  • l’ombre portée renforce l’identité sans nuire à la lisibilité
  • la structure reste simple et réutilisable

Exercice 3 — Le manifeste d’un groupe secret

Consignes HTML

Contexte
Vous devez mettre en forme un court manifeste idéologique.

  • Créez une page HTML complète.
  • Dans le body :
    • un h1 (nom du groupe)
    • un paragraphe manifeste
    • un paragraphe de devise
  • Ajoutez une classe emphasis sur une phrase entière du manifeste.

Consignes CSS

  • Définissez une police globale.
  • Créez une hiérarchie très marquée entre h1 et paragraphes.
  • Stylisez .emphasis différemment (taille ou style).
  • Utilisez em pour un ajustement local.

Correction

CSS :

h1 {
  font-size: 2rem;
}

p {
  font-size: 1rem;
  line-height: 1.6;
}

.emphasis {
  font-size: 1.2em;
  font-style: italic;
}

Explications

  • em dépend du contexte du paragraphe
  • l’effet reste local et maîtrisé
  • la hiérarchie ne dépend pas de la couleur

Exercice 4 — L’avertissement d’une interface futuriste

Consignes HTML

Contexte
Vous concevez un message d’alerte pour une interface futuriste.

  • Créez :
    • une div avec la classe warning
    • un h2
    • un paragraphe explicatif
  • Ajoutez une classe critical sur un mot clé.

Consignes CSS

  • Donnez à .warning une largeur fixe.
  • Centrez le texte.
  • Utilisez text-transform sur le h2.
  • Accentuez le mot .critical.
  • Ajoutez un effet discret au survol.

Correction

CSS :

.warning {
  width: 320px;
  text-align: center;
}

h2 {
  text-transform: uppercase;
}

.critical {
  font-weight: bold;
  font-size: 1.2em;
}

.warning:hover {
  opacity: 0.85;
}

Explications

  • text-transform agit uniquement sur l’affichage
  • l’effet de survol renforce la tension narrative
  • la lisibilité reste prioritaire

Exercice 5 — L’affiche de prophétie

Consignes HTML

Contexte
Vous créez une affiche textuelle annonçant une prophétie.

  • Créez :
    • un h1 (phrase courte)
    • un paragraphe d’explication
  • Ajoutez une classe vision sur le h1.

Consignes CSS

  • Définissez une police globale.
  • Utilisez une unité liée au viewport pour le h1.
  • Ajoutez une ombre portée au titre.
  • Gardez le paragraphe lisible et stable.

Correction

CSS :

h1.vision {
  font-size: 6vw;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}

p {
  font-size: 1rem;
  line-height: 1.6;
}

Explications

  • vw rend le titre adaptatif à la taille de l’écran
  • le paragraphe reste stable pour le confort de lecture
  • contraste clair entre effet visuel et lisibilité

4. Couleurs et arrière-plans

La couleur est l’un des leviers les plus puissants du CSS.
Elle influence immédiatement :

  • la lisibilité
  • la hiérarchie visuelle
  • l’émotion transmise
  • l’accessibilité d’une interface

Contrairement à ce que l’on pourrait penser, la couleur en CSS n’est pas qu’un choix esthétique :
c’est un outil fonctionnel, soumis à des règles précises.

4.1 Comprendre les systèmes de couleurs

Avant d’appliquer des couleurs, il est essentiel de comprendre comment le navigateur les interprète.
Le CSS propose plusieurs systèmes de notation, qui ne servent pas tous aux mêmes usages.

4.1.1 Noms de couleurs prédéfinis

Le CSS fournit une liste de noms de couleurs prédéfinis, comme :

p {
  color: red;
}

Exemples courants :

  • black
  • white
  • red
  • blue
  • green
  • gray

À quoi ça sert

  • prototypage rapide
  • démonstration pédagogique
  • tests visuels simples

Limites

  • palette très limitée
  • manque de précision
  • peu utilisée en production réelle

👉 En pratique, ces couleurs sont surtout utiles pour apprendre ou tester, pas pour construire une identité graphique.

4.1.2 Notation hexadécimale

La notation hexadécimale est l’une des plus utilisées sur le web.

Exemple :

p {
  color: #ff0000;
}

Fonctionnement :

  • #RRGGBB
  • chaque paire représente l’intensité du rouge, du vert et du bleu
  • valeurs de 00 à FF

Exemples :

  • #000000 → noir
  • #ffffff → blanc
  • #ff0000 → rouge

Version raccourcie possible :

p {
  color: #f00;
}

À quoi ça sert

  • grande précision
  • très répandu dans les outils de design
  • facile à partager entre designers et développeurs

Exercice 4.1.2 — Explorer la notation hexadécimale

Consignes HTML

  • Créez une page HTML complète reliée à style.css.
  • Ajoutez trois paragraphes avec des textes différents.
  • Aucun style inline.

Consignes CSS

  • Appliquez une couleur différente à chaque paragraphe.
  • Utilisez uniquement des couleurs hexadécimales.
  • Testez une couleur en version longue et une en version courte.

Correction (exemple)

p:first-of-type {
  color: #ff0000;
}

p:nth-of-type(2) {
  color: #00ff00;
}

p:nth-of-type(3) {
  color: #00f;
}

Explications

  • la notation hexadécimale permet un contrôle précis
  • la version courte est une écriture simplifiée, pas une autre couleur
  • ces valeurs sont indépendantes du contexte

4.1.3 Modèles RGB et RGBA

Le modèle RGB représente les couleurs par leurs composantes :

  • Red
  • Green
  • Blue

Exemple :

p {
  color: rgb(255, 0, 0);
}

Chaque valeur va de 0 à 255.

RGBA ajoute un canal alpha, qui gère la transparence :

p {
  color: rgba(255, 0, 0, 0.5);
}

Le dernier paramètre :

  • varie de 0 (transparent)
  • à 1 (opaque)

À quoi ça sert

  • gérer la transparence localement
  • superposer des éléments
  • créer des effets visuels subtils

Exercice 4.1.3 — Texte semi-transparent

Consignes

  • Créez un paragraphe de texte.
  • Appliquez une couleur en rgba.
  • Testez plusieurs valeurs d’opacité.

Correction

p {
  color: rgba(0, 0, 0, 0.6);
}

Explications

  • seule la couleur du texte est affectée
  • le reste de l’élément reste intact
  • différence fondamentale avec opacity (vue plus tard)

4.1.4 Modèles HSL et HSLA

HSL signifie :

  • Hue (teinte)
  • Saturation
  • Lightness (luminosité)

Exemple :

p {
  color: hsl(0, 100%, 50%);
}

HSLA ajoute la transparence :

p {
  color: hsla(0, 100%, 50%, 0.7);
}

À quoi ça sert

  • ajuster une couleur de façon intuitive
  • créer des variantes cohérentes
  • travailler par teinte plutôt que par valeur technique

👉 HSL est souvent plus lisible mentalement que RGB.

4.2.1 Application de la couleur au texte

La propriété principale est color.

p {
  color: #333333;
}

Elle s’applique à :

  • tout élément contenant du texte
  • et est héritée par défaut

Cela signifie que :

  • définir color sur body impacte tout le texte
  • sauf si une règle plus spécifique la surcharge

4.2 Couleur du texte

4.2.2 Gestion de la lisibilité et du contraste

Une couleur n’est jamais isolée :
elle est toujours perçue par rapport à son arrière-plan.

Bonnes pratiques :

  • éviter les contrastes faibles
  • préférer des couleurs foncées sur fond clair
  • éviter le texte clair sur fond clair (et inversement)

Exercice 4.2 — Lisibilité et contraste

Consignes

  • Créez une div contenant un paragraphe.
  • Appliquez une couleur de texte.
  • Appliquez une couleur d’arrière-plan.
  • Ajustez les couleurs pour garantir une bonne lisibilité.

Correction

div {
  background-color: #f5f5f5;
}

p {
  color: #333333;
}

Explications

  • le contraste facilite la lecture prolongée
  • une couleur “jolie” n’est pas forcément lisible
  • la lisibilité prime sur l’esthétique

4.3.1 Couleur d’arrière-plan

La propriété background-color définit la couleur derrière un élément.

div {
  background-color: #eeeeee;
}

Elle s’applique :

  • aux éléments blocs
  • aux éléments inline (partiellement)

4.3 Arrière-plans

4.3.2 Images d’arrière-plan

Une image peut être utilisée comme arrière-plan :

div {
  background-image: url("image.jpg");
}

À quoi ça sert

  • décor visuel
  • texture
  • image illustrative sans impact sur le HTML

Exercice 4.3 — Carte avec image de fond

Consignes

  • Créez une div contenant du texte.
  • Appliquez une image en arrière-plan.
  • Ajoutez une couleur de texte lisible.

Correction

.card {
  background-image: url("background.jpg");
  color: white;
}

Explications

  • l’image ne fait pas partie du contenu
  • le texte doit rester lisible par-dessus

4.4.1 Répétition des images

Par défaut, une image d’arrière-plan se répète.

div {
  background-repeat: no-repeat;
}

Valeurs courantes :

  • repeat
  • no-repeat
  • repeat-x
  • repeat-y

4.4 Propriétés avancées d’arrière-plan

4.4.2 Positionnement et dimensionnement

Position :

div {
  background-position: center;
}

Dimensionnement :

div {
  background-size: cover;
}
  • cover remplit l’espace
  • contain conserve l’image entière

Exercice 4.4 — Maîtriser une image de fond

Consignes

  • Créez une div avec une image d’arrière-plan.
  • Empêchez la répétition.
  • Centrez l’image.
  • Adaptez-la à la taille du bloc.

Correction

div {
  background-image: url("background.jpg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

4.4.3 Combinaison de plusieurs arrière-plans

Le CSS permet plusieurs arrière-plans sur un même élément :

div {
  background-image: 
    linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)),
    url("image.jpg");
}

Les arrière-plans sont empilés du haut vers le bas.

4.5.1 Dégradés linéaires

div {
  background: linear-gradient(to right, #000, #fff);
}

4.5 Dégradés et transparence

4.5.2 Dégradés radiaux

div {
  background: radial-gradient(circle, #fff, #000);
}

4.5.3 Différence entre opacité globale et transparence

opacity affecte tout l’élément :

div {
  opacity: 0.5;
}

RGBA / HSLA affecte uniquement la couleur :

div {
  background-color: rgba(0,0,0,0.5);
}

👉 C’est une différence fondamentale à comprendre.

Exercice 4.5 — Opacité vs transparence

Consignes

  • Créez deux divs avec du texte.
  • Sur la première, utilisez opacity.
  • Sur la seconde, utilisez une couleur RGBA.
  • Comparez le rendu du texte.

Correction

.box-opacity {
  background-color: black;
  opacity: 0.5;
}

.box-rgba {
  background-color: rgba(0, 0, 0, 0.5);
}

Explications

  • opacity rend aussi le texte transparent
  • RGBA conserve la lisibilité
  • choix essentiel dans les interfaces réelles

Partie 5 — Le modèle de boîte (Box Model)

Le Box Model est un concept central en CSS. Chaque élément HTML est représenté comme une boîte rectangulaire avec contenu, padding, bordure et marge.

Maîtriser le Box Model est indispensable pour :

  • contrôler la taille réelle d’un élément,
  • gérer les espacements internes et externes,
  • créer des interfaces claires et lisibles,
  • éviter les débordements et collisions dans les mises en page.

5.1 Structure du Box Model

Chaque boîte CSS est composée de quatre zones imbriquées :

  • Content (contenu)
  • Padding (espacement interne)
  • Border (bordure)
  • Margin (marge externe)
+---------------------------+
|        margin             |
|   +-------------------+   |
|   |     border        |   |
|   |  +-------------+  |   |
|   |  |   padding   |  |   |
|   |  |  +-------+  |  |   |
|   |  |  |content|  |  |   |
|   |  |  +-------+  |  |   |
|   |  +-------------+  |   |
|   +-------------------+   |
+---------------------------+

Chaque zone a son rôle :

  • Content : texte ou éléments enfants
  • Padding : espace entre le contenu et la bordure
  • Border : contour visible autour du padding
  • Margin : espace entre l’élément et les autres éléments

5.1.1 Zone de contenu (Content)

La zone de contenu correspond à l’espace où s’affiche le texte ou les éléments enfants.

Exemple :

div.content-box {
  width: 300px;             /* largeur du contenu */
  height: 150px;            /* hauteur du contenu */
  background-color: #f0f0f0; /* couleur de fond pour visualiser le content */
}

💡 Explications :

  • La largeur et la hauteur définissent uniquement le contenu.
  • Padding, border et margin s’ajoutent autour et augmentent la taille réelle si box-sizing: content-box.
  • Observer le rendu permet de comprendre l’espace réservé au contenu par rapport aux autres zones.

Exercice 5.1.1 — Observer la zone de contenu

Consignes HTML

  • Créez un fichier index.html.
  • Dans le <body>, ajoutez une div avec la classe content-box.
  • Ajoutez deux paragraphes de texte à l’intérieur.
  • Aucun style inline.

Consignes CSS

  • Donnez une largeur et une hauteur fixes à .content-box.
  • Ajoutez une couleur de fond.
  • Ajoutez une bordure fine pour visualiser la boîte.
  • Observez et notez la zone correspondant au contenu.

5.1.2 Padding (espacement interne)

Le padding est l’espace entre le contenu et la bordure.

div.padding-box {
  padding: 20px;             /* même padding pour tous les côtés */
  background-color: #d0e6f0;
  border: 2px solid #333;
}

💡 Explications :

  • Le padding fait partie de la boîte visuelle et prend la couleur de fond.
  • On peut définir chaque côté individuellement :
padding-top: 10px;
padding-right: 15px;
padding-bottom: 20px;
padding-left: 25px;
  • On peut aussi utiliser le shorthand pour définir les 4 côtés en une seule ligne :
padding: 10px 15px 20px 25px; /* top right bottom left */
padding: 10px 20px;           /* top/bottom, right/left */
padding: 10px;                /* tous les côtés identiques */

Exemple concret :

div.example {
  width: 200px;
  height: 100px;
  padding: 10px 20px;  /* 10px haut et bas, 20px droite et gauche */
  border: 3px solid black;
  background-color: #f7e6f0;
}

💡 Pièges fréquents :

  • Le padding augmente la taille totale de l’élément.
  • Nécessaire de calculer la largeur réelle si vous utilisez width fixe.

Exercice 5.1.2 — Expérimenter le padding

Consignes HTML

  • Une div avec la classe padding-box.
  • Ajoutez un titre et un paragraphe.

Consignes CSS

  • Appliquez un padding différent pour chaque côté (shorthand et propriétés individuelles).
  • Ajoutez une bordure et une couleur de fond.
  • Observez comment le padding modifie la distance entre le texte et la bordure.
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Exercice Padding</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="padding-box">
        <h2>Mon titre</h2>
        <p>Voici un paragraphe pour observer l'effet du padding sur la distance entre le texte et la bordure.</p>
    </div>
</body>
</html>
.padding-box {
    /* Padding individuel pour chaque côté */
    padding-top: 20px;
    padding-right: 40px;
    padding-bottom: 30px;
    padding-left: 10px;

    /* Ou en shorthand (alternative) */
    /* padding: 20px 40px 30px 10px; */

    /* Bordure et couleur de fond */
    border: 2px solid #333;
    background-color: #f0f0f0;

    /* Pour mieux visualiser le contenu */
    max-width: 400px;
    margin: 20px auto;
}

5.1.3 Border (bordure)

La bordure entoure le padding et délimite visuellement l’élément.

div.border-box {
  padding: 15px;
  border: 4px dashed #ff6600;
  background-color: #fff3e6;
}

💡 Explications :

  • La bordure ajoute de la taille à la boîte.
  • Propriétés importantes : border-width, border-style, border-color.
  • Peut être définie pour chaque côté :
border-top: 3px solid red;
border-right: 5px dotted blue;
border-bottom: 2px dashed green;
border-left: 4px solid black;
  • Le shorthand permet tout définir en une seule ligne :
border: 2px solid black;

Exercice 5.1.3 — Tester les bordures

Consignes HTML

  • Une div avec la classe border-box.
  • Ajoutez un titre et un paragraphe.

Consignes CSS

  • Testez différentes épaisseurs et styles de bordure.
  • Changez la couleur.
  • Observez comment la bordure influence la taille totale et l’apparence.
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Exercice Border</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="border-box">
        <h2>Mon titre</h2>
        <p>Voici un paragraphe pour observer l'effet des bordures sur la taille et l'apparence de la div.</p>
    </div>
</body
.border-box {
    /* Padding pour que le texte ne touche pas la bordure */
    padding: 15px;

    /* Bordure : différentes propriétés */
    border-width: 4px;          /* épaisseur de la bordure */
    border-style: dashed;       /* style : solid, dashed, dotted, double, groove... */
    border-color: #ff6600;      /* couleur de la bordure */

    /* Option : couleur de fond pour mieux voir l'effet */
    background-color: #ffeedd;

    /* Taille et centrage pour visualisation */
    width: 300px;
    margin: 20px auto;
    box-sizing: border-box; /* inclut la bordure et le padding dans la largeur totale */
}

5.1.4 Margin (marges externes)

La marge crée un espace autour de l’élément, séparant les boîtes entre elles.

div.margin-box {
  padding: 10px;
  border: 2px solid #333;
  margin: 20px;              /* espace externe */
  background-color: #e6f7d0;
}

💡 Explications :

  • La margin n’a pas de couleur.
  • Peut être définie individuellement :
margin-top: 10px;
margin-right: 15px;
margin-bottom: 20px;
margin-left: 25px;
  • Shorthand pour tous les côtés :
margin: 10px 15px 20px 25px; /* top right bottom left */
margin: 10px 20px;           /* top/bottom, right/left */
margin: 10px;                /* tous les côtés identiques */

Exercice 5.1.4 — Observer les marges

Consignes HTML

  • Créez deux div successives avec la classe margin-box.
  • Ajoutez un titre ou un paragraphe dans chaque boîte.

Consignes CSS

  • Appliquez des marges différentes pour chaque div.
  • Observez comment l’espace entre les boîtes varie.
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Exercice Margin</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="margin-box">
        <h2>Boîte 1</h2>
        <p>Voici le contenu de la première boîte pour tester les marges.</p>
    </div>

    <div class="margin-box">
        <h2>Boîte 2</h2>
        <p>Voici le contenu de la deuxième boîte pour observer l’espace entre les deux.</p>
    </div>
</body>
</html>
.margin-box {
    /* Padding interne pour que le texte ne touche pas les bords */
    padding: 15px;

    /* Bordure et fond pour visualiser la boîte */
    border: 2px solid #444;
    background-color: #ddeeff;

    /* Largeur et centrage */
    width: 300px;
    margin-left: auto;
    margin-right: auto;
}

/* Marges différentes pour chaque boîte */
.margin-box:nth-child(1) {
    margin-top: 20px;
    margin-bottom: 40px;
}

.margin-box:nth-child(2) {
    margin-top: 10px;
    margin-bottom: 30px;
}

5.2 Dimensionnement des éléments

5.2.1 Width et Height

Définissent la taille du contenu uniquement.

div.dimension-box {
  width: 200px;
  height: 100px;
  padding: 20px;
  border: 5px solid #333;
  background-color: #f2e6ff;
}

💡 Explications :

  • Largeur réelle = content + padding + border
  • Important de calculer si la mise en page est stricte.

Exercice 5.2.1 — Largeur réelle vs déclarée

Consignes HTML

  • Créez deux div avec les classes box1 et box2.
  • Ajoutez du texte dans chaque boîte.

Consignes CSS

  • Donnez la même largeur et le même padding/border aux deux boîtes.
  • Pour box2 seulement, utilisez box-sizing: border-box.
  • Observez la différence de rendu.

5.2.2 Contraintes min et max

Permettent de limiter les dimensions :

div.limit-box {
  width: 50%;
  max-width: 600px;
  min-width: 200px;
}
  • Empêche l’élément de devenir trop petit ou trop grand.
  • Utile pour les interfaces adaptatives.

Exercice 5.2.2 — Tester min/max

Consignes HTML

  • Une div contenant plusieurs paragraphes.

Consignes CSS

  • Donnez une largeur en pourcentage.
  • Ajoutez min-width et max-width.
  • Testez le redimensionnement de la fenêtre et observez quand la boîte s’arrête de rétrécir ou d’agrandir.

5.3 Box-sizing

Par défaut, width et height ne prennent pas en compte le padding et la bordure (content-box).

div.content-box {
  box-sizing: content-box;
}

Avec border-box, le padding et la border sont inclus dans la largeur totale :

div.border-box {
  box-sizing: border-box;
}

Exercice 5.3 — Comparer box-sizing

Consignes HTML

  • Deux div avec texte identique.
  • Une div content-box, l’autre border-box.

Consignes CSS

  • Donnez la même largeur, padding et border.
  • Observez l’impact sur la taille réelle.

5.4 Effets visuels liés au Box Model

5.4.1 Box-shadow

div.shadow-box {
  padding: 20px;
  border: 2px solid #333;
  box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
  background-color: #fffacd;
}

💡 Explications :

  • Décalage horizontal et vertical, flou et couleur.
  • Ajoute de la profondeur et un effet de relief.

Exercice 5.4.1 — Ajouter une ombre portée

Consignes HTML

  • Une div avec la classe shadow-box.
  • Ajoutez un titre et un paragraphe.

Consignes CSS

  • Appliquez padding, bordure et couleur de fond.
  • Expérimentez avec les valeurs du box-shadow.

5.4.2 Impact visuel

  • Padding, margin, border et box-shadow améliorent la lisibilité,
  • Renforcent la hiérarchie visuelle,
  • Créent une sensation d’espace et de profondeur.

Synthèse

Le Box Model est essentiel pour créer des interfaces CSS propres et maîtrisées :

  • Content : texte et éléments enfants.
  • Padding : espace intérieur et influence couleur de fond.
  • Border : contour visuel et impact sur la taille totale.
  • Margin : espace externe et séparation des éléments.
  • Width/Height : dimension du contenu.
  • Box-sizing : contrôle la manière dont la taille totale est calculée.
  • Box-shadow : effet de profondeur.

Maîtriser ces notions permet de prévoir la taille réelle d’un élément, de gérer les espacements, et de produire des interfaces lisibles et harmonieuses.

Exercices de fin de partie — Synthèse (parties 1 à 5)

Exercice 1 — Dossier d’espion : “Fiche confidentielle”

Consignes HTML

Objectif : produire une carte “dossier secret” structurée et sémantiquement simple.

Créez un fichier index.html complet comprenant :

  • <!DOCTYPE html>
  • <html lang="fr">
  • <head> avec :
    • <meta charset="UTF-8">
    • un <title> cohérent
    • un lien vers une feuille externe style.css
  • <body>

Dans le <body>, créez un seul conteneur principal : une div avec la classe dossier.

À l’intérieur de .dossier, ajoutez dans cet ordre :

  • un h1 contenant un nom de code (ex : “NOM DE CODE : …”) et donnez-lui l’id code-name (cet id doit être unique).
  • un paragraphe p d’introduction (2 phrases minimum).
  • un h2 avec le texte “Détails”.
  • une liste ul contenant exactement 3 éléments li (ex : Niveau, Zone, Statut).
  • un lien a en bas avec le texte “Déclassifier” et un href="#".

Dans la liste, ajoutez la classe flag à un seul li (celui qui doit ressortir visuellement).

Consignes CSS

Objectif : hiérarchie typographique + box model + dégradé + interaction.

Sur body :

  • Définissez une police globale.
  • Définissez une couleur de texte globale (et laissez l’héritage faire son travail).

Sur .dossier :

  • fixez une largeur en px
  • appliquez une margin
  • appliquez un padding
  • appliquez une border
  • appliquez un arrière-plan en linear-gradient() comportant au moins deux couleurs

Créez une hiérarchie :

  • #code-name doit être plus grand que h2 et les paragraphes (tailles en rem)
  • h2 doit être clairement identifié (taille, marge verticale)

Mettez en valeur .flag :

  • Couleur différente
  • Texte en gras

Style du lien :

  • supprimez le soulignement
  • définissez une couleur
  • au :hover, changez la couleur du lien

Ajoutez au moins un commentaire CSS pour structurer le fichier.

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Dossier d’espion</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="dossier">
    <h1 id="code-name">NOM DE CODE : ORION</h1>
    <p>Document interne. Diffusion restreinte. Toute reproduction est interdite.</p>

    <h2>Détails</h2>
    <ul>
      <li>Niveau : 3</li>
      <li class="flag">Statut : prioritaire</li>
      <li>Zone : secteur nord</li>
    </ul>

    <a href="#">Déclassifier</a>
  </div>
</body>
</html>

Correction CSS

/* Base globale : typographie et héritage */
body {
  font-family: Arial, sans-serif;
  color: #222;
}

/* Carte principale : box model */
.dossier {
  width: 360px;
  margin: 24px;
  padding: 18px;
  border: 2px solid #222;
  background: linear-gradient(to bottom, #f5f5f5, #e6e6e6);
  line-height: 1.6;
}

/* Hiérarchie typographique */
#code-name {
  font-size: 1.6rem;
  text-transform: uppercase;
  margin: 0 0 10px 0;
}

h2 {
  font-size: 1.2rem;
  margin: 14px 0 8px 0;
}

.flag {
  color: #b00020;
  font-weight: bold;
}

/* Lien */
a {
  text-decoration: none;
  color: #0b3d91;
}

a:hover {
  color: #b00020;
}

Explications

  • L’héritage permet d’éviter de répéter la couleur et la police.
  • Le box model structure visuellement le composant.
  • Le dégradé crée une ambiance sans image externe.
  • Les tailles en rem assurent une cohérence d’échelle.
  • :hover introduit une interaction simple.
  • Erreur fréquente : confondre margin (extérieur) et padding (intérieur).

Exercice 2 — Grimoire : “Sortilège du jour”

Consignes HTML

  • Créez une page HTML complète + style.css externe.
  • Dans le body :
    • une div class grimoire
    • un h1 (titre du sort)
    • un p long (2–3 lignes minimum)
    • un p court “Ingrédients : …”
  • Ajoutez une classe incantation au paragraphe long.
  • Aucun style inline.

Consignes CSS

  • Définissez la police globale sur body.
  • .grimoire doit avoir :
    • une largeur en px
    • une margin, padding, border visibles (box model)
    • un background-color en HSL ou HSLA (obligatoire)
  • Sur h1 :
    • taille en rem
    • text-shadow discret
  • Sur .incantation::first-letter :
    • rendre la première lettre visuellement dominante (taille en em)
    • ajouter une graisse (bold)
  • Travail d’unités :
    • au moins une taille en rem
    • au moins une taille en em

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Grimoire</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="grimoire">
    <h1>Sortilège : Brume Légère</h1>
    <p class="incantation">
      Lorsque la nuit tombe, prononcez ces mots à voix basse. La brume couvre les traces, et le silence protège votre passage.
    </p>
    <p>Ingrédients : feuille sèche, eau, souffle.</p>
  </div>
</body>
</html>

Correction CSS

body {
  font-family: Arial, sans-serif;
  color: #1f1f1f;
}

.grimoire {
  width: 380px;
  margin: 24px;
  padding: 18px;
  border: 2px solid #1f1f1f;

  background-color: hsl(45, 40%, 92%);
  line-height: 1.6;
}

h1 {
  font-size: 1.7rem;
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.25);
  margin: 0 0 12px 0;
}

.incantation {
  font-size: 1rem;
  margin: 0 0 10px 0;
}

.incantation::first-letter {
  font-size: 2em;
  font-weight: bold;
}

p {
  margin: 0 0 10px 0;
}

Explications

  • HSL : utile pour créer des variations cohérentes (ici, un “papier” chaud), plus intuitif que RGB.
  • Pseudo-élément ::first-letter : permet de styliser une partie d’un texte sans modifier le HTML (effet “manuscrit”).
  • rem vs em :
    • rem sert aux tailles globales (titre)
    • em sert à des ajustements locaux (la première lettre par rapport au paragraphe)
  • Pièges :
    • confondre opacity (rend tout transparent) et une couleur HSLA (ne rend transparent que la couleur)
    • mettre une ombre trop forte et perdre en lisibilité

Exercice 3 — Arcade : “Ticket de score”

Consignes HTML

But : créer un ticket typographique, avec contraste, hover, unités viewport.

  • Page HTML complète + feuille externe.
  • Dans le body :
    • une div class ticket
    • un h1 class game-title
    • un h2 “Score”
    • un p contenant un nombre
    • un lien a “Rejouer”
  • Ajoutez un span class bonus autour d’un mot dans le paragraphe (ex : “bonus”).
  • Aucun style inline.

Consignes CSS

  • h1 en unité vw (titre qui s’adapte à l’écran).
  • .ticket : width fixe + padding + border + background-color + margin.
  • Contraste texte/fond solide.
  • .bonus en em (plus grand que le texte autour).
  • a:hover (couleur).
  • Au moins une couleur en hex et une en rgba.

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Arcade</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="ticket">
    <h1 class="game-title">NEON RUN</h1>
    <h2>Score</h2>
    <p>Vous avez obtenu 12800 points <span class="bonus">bonus</span>.</p>
    <a href="#">Rejouer</a>
  </div>
</body>
</html>

Correction CSS

body {
  font-family: Arial, sans-serif;
  color: #111;
}

.ticket {
  width: 360px;
  margin: 24px;
  padding: 18px;
  border: 2px dashed #111;

  background-color: rgba(240, 240, 240, 1);
  line-height: 1.6;
}

.game-title {
  font-size: 6vw;
  margin: 0 0 8px 0;
  color: #0b3d91;
}

h2 {
  font-size: 1.2rem;
  margin: 10px 0 6px 0;
}

p {
  margin: 0 0 12px 0;
}

.bonus {
  font-size: 1.2em;
  font-weight: bold;
  color: #b00020;
}

a {
  text-decoration: none;
  color: #0b3d91;
}

a:hover {
  color: #b00020;
}

Explications

  • vw : rend le titre fluide et adaptatif.
  • rgba : permet un fond potentiellement semi-transparent sans toucher au texte.
  • em sur .bonus : le bonus grossit proportionnellement au texte du paragraphe.
  • Border dashed : rend l’objet “ticket” crédible visuellement sans layout compliqué.
  • Erreurs fréquentes :
    • vw trop grand → titre énorme sur grands écrans
    • oublier de retirer le soulignement du lien
    • mettre font-size du paragraphe en px partout → perte de cohérence globale

Exercice 4 — Atelier d’alchimiste : “Étiquettes de potions”

Consignes HTML

But : manipuler sélecteurs d’attributs + couleurs + box model.

  • Page HTML complète + CSS externe.
  • Dans le body, créez une div class shelf contenant 3 “étiquettes” :
    • chaque étiquette est une div class label
    • chaque .label contient un h2 + un p
  • Sur chaque .label, ajoutez un attribut data-type avec une valeur différente :
    • data-type="heal", data-type="poison", data-type="mana"
  • Aucun style inline.

Consignes CSS

  • .shelf : width fixe + padding + background-color.
  • .label : border + margin-bottom + padding.
  • Utilisez uniquement des sélecteurs d’attributs pour donner une couleur différente à chaque potion.
  • Ajoutez un text-transform sur les titres.
  • Ajoutez un :hover sur .label (léger changement de fond ou d’opacité).

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Potions</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="shelf">
    <div class="label" data-type="heal">
      <h2>Potion de soin</h2>
      <p>Usage : régénération lente.</p>
    </div>

    <div class="label" data-type="poison">
      <h2>Potion toxique</h2>
      <p>Usage : à manipuler avec prudence.</p>
    </div>

    <div class="label" data-type="mana">
      <h2>Potion de mana</h2>
      <p>Usage : concentration accrue.</p>
    </div>
  </div>
</body>
</html>

Correction CSS

body {
  font-family: Arial, sans-serif;
  color: #222;
}

.shelf {
  width: 420px;
  margin: 24px;
  padding: 18px;
  background-color: #f5f5f5;
  border: 2px solid #222;
}

.label {
  padding: 14px;
  border: 2px solid #222;
  margin-bottom: 12px;
  line-height: 1.6;
}

.label:hover {
  opacity: 0.92;
}

.label h2 {
  text-transform: uppercase;
  margin: 0 0 6px 0;
  font-size: 1.2rem;
}

/* Couleurs par attribut */
.label[data-type="heal"] {
  background-color: rgba(0, 160, 80, 0.12);
}

.label[data-type="poison"] {
  background-color: rgba(176, 0, 32, 0.12);
}

.label[data-type="mana"] {
  background-color: rgba(11, 61, 145, 0.12);
}

Explications

  • Sélecteurs d’attributs : permettent de styliser selon data-type sans rajouter des classes “heal/poison/mana”.
  • rgba : parfait pour des fonds “teintés” légers, tout en gardant le texte lisible.
  • Hover sur .label : renforce l’interactivité sans introduire de concepts avancés.
  • Erreurs fréquentes :
    • utiliser des classes supplémentaires au lieu de l’attribut (ce qui contourne l’objectif)
    • mettre une opacité globale sur .label (cela affecterait aussi le texte)
    • oublier de différencier visuellement les 3 types

Exercice 5 — Affiche de film : “Nuit Blanche”

Consignes HTML

But : cumuler image de fond + réglages background + contraste + overlay sans opacity globale.

  • Page HTML complète + CSS externe.
  • Dans le body :
    • une div class poster
    • dans .poster : un h1, un p (pitch), un lien “Réserver”
  • Aucun style inline.

Consignes CSS

  • .poster : width fixe + padding + border + margin.
  • Fond : utilisez background-image avec 2 couches :
    • un linear-gradient semi-transparent
    • une url(...) (image au choix)
  • Empêchez la répétition, centrez l’image, utilisez cover.
  • Assurez un texte lisible (couleur).
  • h1 : letter-spacing léger + ombre portée.
  • Lien : text-decoration: none et :hover couleur.

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Nuit Blanche</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="poster">
    <h1>Nuit Blanche</h1>
    <p>Une ville s’endort. Un message s’allume. Tout change en une heure.</p>
    <a href="#">Réserver</a>
  </div>
</body>
</html>

Correction CSS

body {
  font-family: Arial, sans-serif;
}

.poster {
  width: 440px;
  margin: 24px;
  padding: 18px;
  border: 2px solid #111;

  color: #ffffff;
  line-height: 1.6;

  background-image:
    linear-gradient(rgba(0,0,0,0.55), rgba(0,0,0,0.55)),
    url("background.jpg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

.poster h1 {
  margin: 0 0 8px 0;
  font-size: 2rem;
  letter-spacing: 1px;
  text-shadow: 2px 2px 4px rgba(0,0,0,0.35);
}

.poster p {
  margin: 0 0 12px 0;
  font-size: 1rem;
}

.poster a {
  text-decoration: none;
  color: #ffffff;
}

.poster a:hover {
  color: #ffcc00;
}

Explications

  • Double background : le dégradé sert d’“overlay” pour améliorer la lisibilité du texte sur l’image.
  • Pourquoi pas opacity : opacity aurait rendu le texte transparent aussi, ce qu’on évite.
  • background-size: cover : l’image remplit le bloc, même si elle est recadrée.
  • text-shadow : améliore le contraste sur fonds visuels.
  • Erreurs fréquentes :
    • oublier no-repeat et voir l’image se répéter
    • mettre un overlay trop faible et perdre le contraste
    • oublier text-decoration: none sur le lien

Exercice 6 — Laboratoire : “Test opacité vs transparence”

Consignes HTML

  • Page HTML complète + style.css.
  • Dans le body, créez une div class lab.
  • Dans .lab, créez 2 blocs :
    • une div class case case-opacity
    • une div class case case-rgba
  • Dans chaque .case, mettez :
    • un h2
    • un p (même contenu dans les deux, pour comparer)
    • un a (lien “Détails”)
  • Aucun style inline.

Consignes CSS

  • .lab : width fixe + padding + border + background-color + margin.
  • .case : padding + border + margin-bottom.
  • Dans .case-opacity, utilisez opacity: ....
  • Dans .case-rgba, utilisez un fond rgba(...) (pas d’opacité globale).
  • Le texte doit rester nettement plus lisible dans la version RGBA que dans la version opacity.
  • Ajoutez a:hover + suppression du soulignement.
  • Ajoutez une taille en rem pour les titres.

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Laboratoire</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="lab">
    <div class="case case-opacity">
      <h2>Version opacity</h2>
      <p>Comparez la lisibilité : ici, tout l’élément devient transparent.</p>
      <a href="#">Détails</a>
    </div>

    <div class="case case-rgba">
      <h2>Version RGBA</h2>
      <p>Comparez la lisibilité : ici, seul le fond est semi-transparent.</p>
      <a href="#">Détails</a>
    </div>
  </div>
</body>
</html>

Correction CSS

body {
  font-family: Arial, sans-serif;
  color: #111;
}

.lab {
  width: 460px;
  margin: 24px;
  padding: 18px;
  border: 2px solid #111;
  background-color: #f5f5f5;
}

.case {
  padding: 14px;
  border: 2px solid #111;
  margin-bottom: 12px;
  line-height: 1.6;
}

.case h2 {
  font-size: 1.3rem;
  margin: 0 0 6px 0;
}

.case a {
  text-decoration: none;
  color: #0b3d91;
}

.case a:hover {
  color: #b00020;
}

.case-opacity {
  background-color: #0b3d91;
  color: #ffffff;
  opacity: 0.6;
}

.case-rgba {
  background-color: rgba(11, 61, 145, 0.6);
  color: #ffffff;
}

Explications

  • opacity : rend tout le bloc transparent, y compris le texte. Résultat : la lisibilité diminue.
  • rgba : ne rend semi-transparent que le fond. Le texte reste opaque → lisible.
  • Comparaison contrôlée : même structure HTML, même contenu, seule la technique change : c’est idéal pour comprendre.
  • Erreurs fréquentes :
    • oublier que opacity s’applique à tous les enfants
    • utiliser rgba sur le texte au lieu du fond (et perdre en lisibilité)
    • ne pas différencier assez les deux rendus

6. Mise en page : flux du document et positionnement

Ce module est l'un des piliers fondamentaux du CSS. Comprendre comment le navigateur organise les éléments sur une page, comment les positionner avec précision et comment contrôler leur superposition vous donnera la maîtrise totale de la mise en page web.

Sans cette compréhension, les comportements du navigateur semblent souvent mystérieux, voire contradictoires. Avec elle, vous serez capable de prévoir et de reproduire n'importe quelle mise en page.

6.1 Flux normal du document

Avant de parler de positionnement, de flottants ou de Flexbox, il est indispensable de comprendre ce que le navigateur fait par défaut lorsqu'il affiche une page HTML. Ce comportement par défaut s'appelle le flux normal du document, ou plus simplement le flux.

Le flux est la mécanique invisible qui gouverne chaque page web que vous avez jamais vue. Chaque développeur web, qu'il soit débutant ou expert, doit maîtriser ce concept avant toute chose. C'est la fondation sur laquelle repose tout le reste.

6.1.1 Organisation naturelle des éléments HTML

Lorsqu'un navigateur reçoit un fichier HTML, il le lit de haut en bas, dans l'ordre du code source, et place chaque élément les uns à la suite des autres. Ce mécanisme obéit à deux règles simples et immuables :

  • Les éléments de type bloc se placent les uns en dessous des autres, chacun occupant toute la largeur disponible.
  • Les éléments de type en ligne se placent les uns à côté des autres, de gauche à droite, et passent à la ligne uniquement quand l'espace horizontal est épuisé.

Considérez ce HTML très simple, sans aucune ligne de CSS :

<h1>Titre principal</h1>
<p>Un premier paragraphe de texte.</p>
<p>Un second paragraphe. Le mot <strong>important</strong> est en ligne.</p>
<div>Une division bloc.</div>
<span>Un span</span>
<span>Un autre span</span>
<span>Encore un span</span>

Le navigateur va placer h1, p et div empilés verticalement (éléments blocs), et les trois span côte à côte sur la même ligne (éléments en ligne). Le mot strong s'insère dans le texte sans créer de saut de ligne.

Point clé : le flux normal est gratuit et automatique. Il ne nécessite zéro CSS. C'est le comportement de base du navigateur, et c'est sur cette base que vous allez construire toutes vos mises en page.

Il est essentiel de retenir que le flux garantit un ordre de lecture identique à l'ordre du code HTML. Cet ordre est fondamental pour l'accessibilité (les lecteurs d'écran lisent le DOM dans cet ordre) et pour le référencement naturel.

6.1.2 Impact du flux sur la mise en page

Le flux normal a cinq conséquences directes sur la manière dont vous écrivez votre HTML et votre CSS. Comprendre ces conséquences vous évitera de nombreuses erreurs et frustrations.

Conséquence 1 : la largeur automatique des blocs. Par défaut, un élément bloc occupe 100% de la largeur de son conteneur. Un div enfant d'un body de 1200px fera lui aussi 1200px, sans que vous ayez besoin de le préciser. Cette largeur est calculée automatiquement.

div {
  /* Pas besoin d'écrire width: 100%; c'est déjà le cas par défaut */
  background-color: lightblue;
  padding: 20px;
}

/* Si on veut le réduire, on fixe une valeur explicite */
div.reduit {
  width: 60%;
}

Conséquence 2 : la hauteur automatique. Un élément bloc s'étend verticalement pour englober tout son contenu. Si vous avez un div contenant trois paragraphes, le div sera aussi haut que leur somme. La hauteur n'a pas besoin d'être fixée manuellement. Si vous la fixez et que le contenu dépasse, gérez cela avec overflow.

div.hauteur-fixe {
  height: 100px;
  overflow: hidden;  /* Le contenu qui dépasse est masqué */
  /* ou : overflow: scroll; pour ajouter une barre de défilement */
}

Conséquence 3 : la fusion des marges (margin collapsing). Lorsque deux éléments blocs adjacents ont chacun une marge verticale, ces marges ne s'additionnent pas — elles fusionnent : seule la plus grande est appliquée. C'est un comportement propre au flux normal et il ne concerne que les marges verticales. Les marges horizontales ne fusionnent jamais.

p.premier {
  margin-bottom: 30px;
}
p.second {
  margin-top: 20px;
}
/*
  L'espace entre les deux paragraphes n'est PAS de 50px.
  Il est de 30px : les marges ont fusionné, la plus grande gagne.
  Pour le constater : DevTools (F12) > sélectionner p.premier > Box Model.
*/

Attention, piège classique : on s'attend à ce que les marges s'additionnent. Vérifiez toujours dans le Box Model des DevTools lorsqu'un espacement ne correspond pas à ce que vous attendez.

Conséquence 4 : les éléments en ligne ignorent width et height. Si vous écrivez width: 200px sur un span, rien ne se passera. Les éléments en ligne prennent uniquement la place nécessaire à leur contenu. Les paddings et marges verticaux sont également sans effet sur le flux.

span {
  width: 300px;        /* Ignoré */
  height: 100px;       /* Ignoré */
  padding-left: 8px;   /* OK, les paddings horizontaux fonctionnent */
  padding-right: 8px;  /* OK */
}

Conséquence 5 : sortir du flux. Les propriétés float, position: absolute et position: fixed retirent un élément du flux normal. Les autres éléments se comportent alors comme s'il n'existait plus, et peuvent se placer sous lui ou le recouvrir. C'est puissant, mais source de nombreux bugs si mal compris. Nous détaillerons tout cela dans les sections 6.4 et 6.6.

Exercice 1 — La carte de visite en flux naturel

Objectif : créer une carte de visite simple en utilisant uniquement le flux normal, sans aucune propriété de positionnement. L'exercice vous amène à observer concrètement le comportement des éléments blocs et en ligne, et à constater la fusion des marges en direct dans les DevTools.

Consignes :

  • Créez un fichier carte.html avec le HTML fourni ci-dessous.
  • Créez un fichier style.css lié. Propriétés autorisées : background-color, color, padding, margin, font-size, font-weight, border, max-width, font-family. Aucune propriété de positionnement.
  • Stylisez la carte pour qu'elle soit visuellement soignée et lisible.
  • Ajoutez margin-bottom: 30px sur h1 et margin-top: 20px sur .poste. Observez dans les DevTools que l'espace réel est de 30px, pas 50px.
  • Quels éléments sont côte à côte ? Lesquels sont empilés ? Pourquoi ?
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
  <title>Carte de visite</title>
</head>
<body>
  <div class="carte">
    <h1>Marie Dupont</h1>
    <p class="poste">Développeuse Web Front-End</p>
    <hr>
    <p>
      Email : <span class="info">marie@exemple.fr</span>
      &nbsp;|&nbsp;
      Tél : <span class="info">06 12 34 56 78</span>
    </p>
    <p class="bio">
      Passionnée par le CSS et l'UX Design.
      <strong>Disponible</strong> pour des missions freelance.
    </p>
  </div>
</body>
</html>

Correction détaillée — Exercice 1

/* On retire les marges par défaut du navigateur */
body {
  margin: 0;
  padding: 40px;
  background-color: #f0f4f8;
  font-family: Arial, sans-serif;
}

/*
  La carte est un élément bloc : elle occupe 100% de la largeur du body.
  max-width limite cette largeur. margin: 0 auto centre horizontalement.
*/
.carte {
  background-color: #ffffff;
  border: 1px solid #cccccc;
  border-top: 4px solid #2E6DA4;
  padding: 30px 40px;
  max-width: 520px;
  margin: 0 auto;
}

/*
  h1 est un élément bloc : il prend toute la ligne.
  margin-bottom: 30px fusionnera avec margin-top: 20px de .poste.
  Résultat visible dans les DevTools : 30px d'espace, pas 50px.
*/
h1 {
  font-size: 28px;
  color: #1A3A5C;
  margin-top: 0;
  margin-bottom: 30px;
}

/*
  .poste est un <p> (bloc) : il passe à la ligne sous h1.
  margin-top: 20px fusionne avec margin-bottom de h1.
  Seule la plus grande valeur (30px) s'applique.
*/
.poste {
  font-size: 16px;
  color: #2E6DA4;
  font-weight: bold;
  margin-top: 20px;
  margin-bottom: 0;
}

hr {
  border: none;
  border-top: 1px solid #cccccc;
  margin: 20px 0;
}

/*
  Les .info sont des <span> : éléments en ligne.
  Ils restent dans la phrase. width et height n'auraient aucun effet ici.
*/
.info {
  color: #C0392B;
  font-weight: bold;
  background-color: #FEF9F9;
  padding: 2px 6px;
  border-radius: 3px;
}

.bio {
  font-size: 14px;
  color: #555555;
  line-height: 1.7;
  margin-bottom: 0;
}

/* <strong> est en ligne : il s'insère dans le texte sans saut de ligne */
.bio strong {
  color: #1A3A5C;
}

Explications et points de vigilance

Quels éléments sont empilés ? h1, p.poste, hr, p, p.bio — tous des blocs — s'empilent de haut en bas automatiquement, sans aucune instruction CSS de positionnement. C'est le flux normal à l'œuvre.

Quels éléments sont côte à côte ? Les span.info restent dans le texte du p qui les contient. strong s'insère dans le texte de .bio. Ce sont des éléments en ligne : ils ne forcent jamais de saut de ligne.

Fusion des marges : dans les DevTools, cliquez sur h1 puis observez le Box Model. L'espace sous h1 affiche 30px, et non 50px. Le margin-bottom: 30px de h1 et le margin-top: 20px de .poste ont fusionné : seule la valeur la plus grande a été conservée.

Erreur fréquente : tenter de placer deux blocs côte à côte en jouant uniquement sur les marges. Dans le flux normal pur, deux div ne peuvent jamais être côte à côte. Il faut modifier leur type d'affichage — ce que nous allons voir dans la section 6.2.

6.2 Types d'affichage

Chaque élément HTML a un comportement d'affichage par défaut. Ce comportement est défini par la propriété CSS display, mais il est déjà appliqué par le navigateur via sa feuille de style interne, appelée la user-agent stylesheet. Comprendre les trois grandes catégories d'affichage est indispensable avant de chercher à les modifier.

6.2.1 Éléments block

Un élément block possède quatre caractéristiques fondamentales, toutes liées les unes aux autres :

  • Il commence toujours sur une nouvelle ligne. Même s'il y avait de la place sur la ligne précédente, un élément bloc force un saut de ligne avant lui et après lui.
  • Il occupe toute la largeur disponible de son conteneur parent, de bord gauche à bord droit.
  • Sa hauteur s'adapte automatiquement à son contenu.
  • Il accepte toutes les propriétés du modèle de boîte : width, height, margin, padding, border dans toutes les directions.

Les éléments HTML qui sont bloc par défaut sont très nombreux : div, p, h1 à h6, ul, ol, li, article, section, header, footer, nav, main, aside, blockquote, pre, form, table, hr, figure, figcaption...

/* Illustration des propriétés d'un élément bloc */
div {
  width: 60%;           /* On peut réduire la largeur par défaut (100%) */
  height: 150px;        /* On peut fixer la hauteur */
  margin: 20px auto;    /* Les marges auto centrent horizontalement */
  padding: 20px;
  background-color: lightblue;
  border: 2px solid navy;
}

/* Un second div sera toujours EN DESSOUS du premier,
   même si le premier ne fait que 60% de large. */
div + div {
  background-color: lightyellow;
}

6.2.2 Éléments inline

Un élément inline (ou en ligne) a un comportement radicalement différent d'un élément bloc. Ses caractéristiques :

  • Il ne force pas de saut de ligne. Il s'insère dans le flux du texte, à côté des autres éléments en ligne, de gauche à droite.
  • Il ne prend que la place nécessaire à son contenu, ni plus, ni moins.
  • Il ignore les propriétés width et height. Vous pouvez les écrire, elles n'auront aucun effet.
  • Il accepte les paddings et borders horizontaux (gauche/droite), mais les paddings et borders verticaux ne participent pas au flux : ils peuvent déborder sur les lignes adjacentes sans les décaler.
  • Les marges verticales (margin-top et margin-bottom) sont ignorées sur les éléments inline.

Les éléments HTML inline par défaut : span, a, strong, em, b, i, u, abbr, code, cite, small, sub, sup, label...

/* Illustration : les propriétés ignorées sur les éléments inline */
span {
  background-color: yellow;

  padding-left: 10px;   /* OK : fonctionne */
  padding-right: 10px;  /* OK : fonctionne */
  border-left: 2px solid red;   /* OK */
  border-right: 2px solid red;  /* OK */

  width: 300px;        /* IGNORÉ */
  height: 100px;       /* IGNORÉ */
  margin-top: 50px;    /* IGNORÉ dans le flux */
  margin-bottom: 50px; /* IGNORÉ dans le flux */
}

6.2.3 Éléments inline-block

Le type inline-block est un hybride qui combine les avantages des deux modes précédents. C'est une valeur extrêmement utile en pratique.

  • Comme un élément inline : il ne force pas de saut de ligne. Il se place côte à côte avec les autres éléments inline et inline-block.
  • Comme un élément bloc : il accepte width, height, margin (y compris vertical), padding et border dans toutes les directions.

Aucun élément HTML n'est inline-block par défaut. On obtient ce comportement en appliquant display: inline-block en CSS.

/* Exemple concret : des boutons côte à côte */
.bouton {
  display: inline-block; /* La clé : en ligne mais se comporte comme un bloc */
  width: 150px;
  height: 40px;
  padding: 8px 16px;
  margin: 5px;
  background-color: #2E6DA4;
  color: white;
  text-align: center;
  line-height: 24px; /* Centrage vertical du texte sur une ligne */
  border-radius: 4px;
}

Comportement à connaître : les espaces blancs dans le HTML (sauts de ligne entre les balises) créent de petits espaces visuels entre les éléments inline-block. Pour les éliminer, la solution la plus propre est d'appliquer font-size: 0 sur le conteneur parent, puis de restaurer la taille de police sur les enfants.

.conteneur {
  font-size: 0; /* Supprime l'espace blanc parasite entre les inline-block */
}

.bouton {
  display: inline-block;
  font-size: 16px; /* On restaure la taille de police sur les enfants */
}

Exercice 2 — Le podium des langages

Objectif : construire un podium visuel affichant trois langages de programmation côte à côte, avec des hauteurs différentes selon leur rang (1er, 2e, 3e place). L'exercice travaille spécifiquement la différence entre block, inline et inline-block, et vous force à choisir le bon type d'affichage pour chaque élément.

Consignes HTML :

  • Créez un fichier podium.html.
  • Dans le body, créez une div avec la classe podium.
  • À l'intérieur, créez trois div avec la classe marche. Chacune aura également une classe spécifique : premier, deuxieme, troisieme.
  • Dans chaque .marche, placez :
    • Un élément span avec la classe rang contenant respectivement 1, 2, 3.
    • Un élément p avec la classe langage contenant le nom du langage : CSS, HTML, JavaScript.
  • Liez un fichier podium.css.

Consignes CSS :

  • Le .podium doit aligner les trois marches côte à côte et en bas (vertical-align: bottom sera utile).
  • Chaque .marche doit utiliser display: inline-block pour être côté à côté.
  • Toutes les marches ont la même largeur (150px). Les hauteurs diffèrent : .premier → 180px, .deuxieme → 130px, .troisieme → 100px.
  • Le .rang doit être un élément en ligne mais visuellement mis en valeur (grande taille de police, gras).
  • Le .langage est un bloc : il doit être centré horizontalement dans la marche.
  • Choisissez des couleurs distinctes pour chaque marche.

Correction détaillée — Exercice 2

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="podium.css">
  <title>Podium des langages</title>
</head>
<body>

  <h1>Podium des langages web</h1>

  <div class="podium">

    <div class="marche deuxieme">
      <span class="rang">2</span>
      <p class="langage">HTML</p>
    </div>

    <div class="marche premier">
      <span class="rang">1</span>
      <p class="langage">CSS</p>
    </div>

    <div class="marche troisieme">
      <span class="rang">3</span>
      <p class="langage">JavaScript</p>
    </div>

  </div>

</body>
</html>

Correction CSS

/* podium.css — Correction exercice 2 */

body {
  margin: 0;
  padding: 40px;
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
}

h1 {
  text-align: center;
  color: #1A3A5C;
  margin-bottom: 40px;
}

/*
  font-size: 0 sur le conteneur pour supprimer
  les espaces blancs parasites entre les inline-block.
  vertical-align: bottom aligne les marches par le bas,
  ce qui donne l'effet podium avec des hauteurs différentes.
*/
.podium {
  font-size: 0;
  text-align: center;
}

/*
  Chaque marche utilise display: inline-block pour être
  côte à côte tout en acceptant width et height.
  vertical-align: bottom assure l'alignement par le bas.
*/
.marche {
  display: inline-block;
  width: 150px;
  vertical-align: bottom;
  margin: 0 4px;
  border-radius: 6px 6px 0 0;
  padding-top: 16px;

  /* On restaure font-size à 0 annulé sur .podium */
  font-size: 16px;
}

/* Hauteurs spécifiques selon le rang */
.premier    { height: 180px; background-color: #F0C040; }
.deuxieme   { height: 130px; background-color: #B0B8C1; }
.troisieme  { height: 100px; background-color: #CD7F32; }

/*
  .rang est un <span> : élément inline par défaut.
  On ne change pas son display, mais on le stylise fortement.
  Il s'insère dans le flux du texte à l'intérieur de .marche.
*/
.rang {
  font-size: 48px;
  font-weight: bold;
  color: white;
  display: block; /* On le passe en bloc pour le centrer facilement */
  text-align: center;
}

/*
  .langage est un <p> : élément bloc par défaut.
  Il prend naturellement toute la largeur de .marche.
  text-align: center suffit à centrer son contenu.
*/
.langage {
  font-size: 14px;
  font-weight: bold;
  color: white;
  text-align: center;
  margin: 8px 0 0 0;
  text-transform: uppercase;
  letter-spacing: 1px;
}

Explications et points de vigilance

Pourquoi display: inline-block sur .marche ? Parce que les div sont des blocs par défaut : ils s'empileraient verticalement. inline-block leur permet d'être côte à côte tout en conservant la possibilité de définir width et height. Avec display: inline, ils ne pourraient pas avoir de hauteur définie.

Pourquoi vertical-align: bottom ? Sans cette propriété, les éléments inline-block s'alignent par leur ligne de base (baseline), ce qui donne un résultat désorganisé quand les hauteurs diffèrent. bottom aligne tous les blocs par leur bord inférieur, ce qui crée l'effet podium attendu.

Pourquoi font-size: 0 sur .podium ? Les espaces et sauts de ligne entre les balises div dans le HTML se traduisent par un petit espace visuel entre les marches. En passant font-size: 0 sur le parent, ces espaces blancs — qui sont traités comme du texte — disparaissent. Il faut ensuite restaurer font-size sur les enfants.

L'ordre dans le HTML : notez que la marche du 2e est placée en premier dans le HTML, avant celle du 1er. C'est intentionnel pour reproduire l'ordre visuel d'un vrai podium (2e à gauche, 1er au centre, 3e à droite). Le flux lit le HTML de gauche à droite, donc l'ordre du code détermine l'ordre à l'écran.

Erreur fréquente : utiliser display: block sur .marche en pensant que cela suffit. Deux blocs ne peuvent pas être côte à côte dans le flux normal — ils s'empileraient immédiatement.

Exercice 3 — La galerie de badges de compétences

Objectif : créer une galerie de badges de compétences qui s'affichent côte à côte et passent automatiquement à la ligne quand l'espace manque. Chaque badge affiche une icône textuelle (emoji ou initiale), un nom de compétence et un niveau (débutant, intermédiaire, expert). L'exercice vous oblige à combiner intelligemment éléments blocs, inline et inline-block au sein d'une même structure.

Consignes HTML :

  • Créez un fichier badges.html.
  • Dans le body, créez un titre h1 avec le texte Mes compétences.
  • Créez une div avec la classe galerie.
  • À l'intérieur, créez six div avec la classe badge. Chaque badge contiendra :
    • Un élément span avec la classe icone contenant une initiale ou un symbole au choix (par exemple : H, C, JS, PY, SQL, GIT).
    • Un élément p avec la classe nom contenant le nom de la compétence (HTML, CSS, JavaScript, Python, SQL, Git).
    • Un élément span avec la classe niveau contenant le niveau. Attribuez les classes supplémentaires expert, intermediaire ou debutant selon le niveau choisi.
  • Liez un fichier badges.css.

Consignes CSS :

  • Chaque .badge doit être en inline-block pour que les badges se placent côte à côte et passent naturellement à la ligne suivante quand la fenêtre est trop étroite.
  • Chaque .badge a une largeur fixe de 140px, un padding de 20px, des coins arrondis et une ombre légère (box-shadow).
  • L'.icone doit être convertie en élément bloc pour occuper toute la largeur du badge et être centrée. Donnez-lui une grande taille de police (36px), un fond coloré et une hauteur fixe de 60px avec line-height: 60px pour centrer verticalement le texte.
  • Le .nom est déjà un bloc : centrez son texte, mettez-le en gras.
  • Le .niveau doit être converti en bloc pour passer sous le nom. Sa couleur de texte doit changer selon la classe : .expert en vert foncé, .intermediaire en orange, .debutant en gris.
  • Au survol d'un .badge, ajoutez un léger effet de remontée avec transform: translateY(-4px) et une transition fluide.

Correction détaillée — Exercice 3

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="badges.css">
  <title>Galerie de badges</title>
</head>
<body>

  <h1>Mes compétences</h1>

  <div class="galerie">

    <div class="badge">
      <span class="icone">H</span>
      <p class="nom">HTML</p>
      <span class="niveau expert">Expert</span>
    </div>

    <div class="badge">
      <span class="icone">C</span>
      <p class="nom">CSS</p>
      <span class="niveau expert">Expert</span>
    </div>

    <div class="badge">
      <span class="icone">JS</span>
      <p class="nom">JavaScript</p>
      <span class="niveau intermediaire">Intermédiaire</span>
    </div>

    <div class="badge">
      <span class="icone">PY</span>
      <p class="nom">Python</p>
      <span class="niveau intermediaire">Intermédiaire</span>
    </div>

    <div class="badge">
      <span class="icone">SQL</span>
      <p class="nom">SQL</p>
      <span class="niveau debutant">Débutant</span>
    </div>

    <div class="badge">
      <span class="icone">GIT</span>
      <p class="nom">Git</p>
      <span class="niveau debutant">Débutant</span>
    </div>

  </div>

</body>
</html>

Correction CSS

/* badges.css — Correction exercice 3 */

body {
  margin: 0;
  padding: 40px;
  font-family: Arial, sans-serif;
  background-color: #f4f6f9;
}

h1 {
  color: #1A3A5C;
  margin-bottom: 30px;
}

/*
  La galerie est un bloc (div par défaut) qui occupe toute la largeur.
  Pas besoin de modifier son display : ses enfants .badge en inline-block
  vont naturellement se placer côte à côte et revenir à la ligne
  automatiquement quand l'espace est insuffisant.
*/
.galerie {
  /* font-size: 0 pour supprimer les espaces blancs entre les badges */
  font-size: 0;
}

/*
  Chaque badge est inline-block : côte à côte, mais avec width/height/padding.
  vertical-align: top pour les aligner par le haut (comportement attendu
  pour une galerie de cartes de même hauteur).
*/
.badge {
  display: inline-block;
  vertical-align: top;
  width: 140px;
  padding: 0 0 16px 0;
  margin: 10px;
  background-color: #ffffff;
  border-radius: 10px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  overflow: hidden; /* Pour que l'icône respecte le border-radius en haut */

  /* On restaure la font-size annulée par le parent */
  font-size: 14px;

  /* Transition pour l'effet de survol */
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.badge:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}

/*
  .icone est un <span> : inline par défaut.
  On le passe en display: block pour qu'il occupe toute la largeur
  du badge et qu'on puisse lui donner une hauteur et le centrer.
*/
.icone {
  display: block;
  height: 60px;
  line-height: 60px;   /* Centrage vertical du texte */
  text-align: center;  /* Centrage horizontal */
  font-size: 24px;
  font-weight: bold;
  color: white;
  background-color: #2E6DA4;
  letter-spacing: 1px;
}

/*
  .nom est un <p> : déjà bloc par défaut.
  Il prend toute la largeur du badge naturellement.
*/
.nom {
  text-align: center;
  font-weight: bold;
  color: #1A3A5C;
  margin: 12px 0 6px 0;
  font-size: 15px;
}

/*
  .niveau est un <span> : inline par défaut.
  On le passe en display: block pour le placer sous .nom
  et pouvoir le centrer facilement.
*/
.niveau {
  display: block;
  text-align: center;
  font-size: 12px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* Couleurs selon le niveau */
.expert       { color: #1E6B2E; }
.intermediaire { color: #B05A00; }
.debutant     { color: #888888; }

Explications et points de vigilance

Pourquoi display: block sur .icone et .niveau ? Ces deux éléments sont des span : inline par défaut. En les laissant inline, ils resteraient sur la même ligne que le contenu adjacent et ne pourraient pas être centrés avec text-align. En les passant en block, ils prennent toute la largeur de leur parent et se placent chacun sur leur propre ligne, ce qui est exactement le comportement attendu pour une carte structurée verticalement.

Le retour à la ligne automatique des inline-block : c'est l'un des grands avantages de ce type d'affichage. Contrairement à Flexbox ou Grid (que vous verrez plus tard), les éléments inline-block se comportent exactement comme des mots dans un texte : ils passent à la ligne suivante quand l'espace horizontal est épuisé. Cela rend la galerie naturellement responsive sans aucune media query.

vertical-align: top vs bottom : dans l'exercice précédent (podium), on utilisait bottom pour aligner par le bas. Ici, top est le bon choix : on veut que les badges s'alignent par leur bord supérieur, comme des cartes posées côte à côte sur une table.

overflow: hidden sur .badge : cette propriété force les coins arrondis (border-radius) à s'appliquer aussi à l'élément .icone qui se trouve en haut du badge. Sans elle, le fond coloré de l'icône dépasserait des coins arrondis de la carte.

Erreur fréquente : oublier de restaurer font-size sur .badge après avoir mis font-size: 0 sur .galerie. Sans cette restauration, tout le texte à l'intérieur des badges disparaît.

6.3 Propriété display

La propriété CSS display est l'une des plus importantes de tout le langage CSS. Elle permet de modifier le comportement d'affichage natif de n'importe quel élément HTML. C'est elle qui détermine comment un élément participe au flux et comment il interagit avec ses voisins. Vous venez de voir les trois grandes catégories d'affichage — il est maintenant temps de comprendre comment les manipuler activement et dans quels contextes précis cela s'avère indispensable.

6.3.1 Modifier le comportement d'affichage

La propriété display accepte de nombreuses valeurs. Dans le cadre de ce module, nous nous concentrons sur les valeurs fondamentales qui agissent sur le flux du document. Les valeurs flex et grid seront traitées dans des modules dédiés.

element { display: block; }
/* => L'élément se comporte comme un bloc : nouvelle ligne, 100% de largeur,
      accepte width / height / margin / padding dans toutes les directions. */

element { display: inline; }
/* => L'élément se comporte comme du texte en ligne : pas de saut de ligne,
      ignore width et height, marges verticales ignorées. */

element { display: inline-block; }
/* => Hybride : en ligne mais accepte width / height / margin vertical. */

element { display: none; }
/* => L'élément est complètement retiré du flux ET invisible.
      Il ne prend plus aucune place dans la page. */

element { display: list-item; }
/* => Se comporte comme un <li>, avec puce ou numéro automatique. */

element { display: table; }
/* => Se comporte comme un <table> HTML, avec ses règles d'alignement. */

La différence entre display: none et visibility: hidden est fondamentale et source d'erreurs fréquentes. Ces deux propriétés cachent un élément, mais de manière radicalement différente :

/* display: none retire COMPLÈTEMENT l'élément du flux */
.cache-sans-espace {
  display: none;
}
/* L'élément n'est plus là visuellement ET physiquement.
   Les éléments suivants remontent pour combler le vide. */

/* visibility: hidden cache l'élément mais conserve son espace dans le flux */
.cache-avec-espace {
  visibility: hidden;
}
/* L'élément est invisible mais son espace est préservé.
   Les éléments voisins ne bougent pas. */

/* opacity: 0 rend l'élément transparent mais garde espace ET interactivité */
.transparent {
  opacity: 0;
}
/* Invisible, espace préservé, ET il réagit toujours au clic et au hover. */

À retenir : display: none = l'élément n'existe plus visuellement ni physiquement. visibility: hidden = l'élément existe encore dans la mise en page, il est juste invisible. opacity: 0 = l'élément existe, est invisible, et reste interactif.

6.3.2 Cas d'usage courants

Modifier la propriété display est une technique quotidienne en CSS. Voici les scénarios les plus fréquents que vous rencontrerez en pratique.

Cas 1 : transformer un lien en bloc cliquable. Par défaut, un <a> est inline : seul le texte est cliquable. En le passant en display: block, toute la surface rectangulaire devient cliquable. C'est indispensable pour les menus où l'on veut une grande zone de clic confortable.

.menu-lateral a {
  display: block;       /* Toute la largeur du menu devient cliquable */
  padding: 12px 20px;
  color: #ffffff;
  text-decoration: none;
  background-color: #1A3A5C;
  border-bottom: 1px solid #2E6DA4;
  transition: background-color 0.2s ease;
}

.menu-lateral a:hover {
  background-color: #2E6DA4;
}

Cas 2 : afficher des éléments de liste côte à côte. Les <li> sont des blocs par défaut. Pour construire une navigation horizontale à partir d'une liste <ul>, on les passe en inline-block. La liste conserve sa sémantique HTML tout en s'affichant horizontalement.

nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0; /* Suppression des espaces blancs parasites */
}

nav li {
  display: inline-block;
  font-size: 16px; /* On restaure la taille de police */
}

nav li a {
  display: block; /* Le lien remplit tout le li : zone de clic maximale */
  padding: 15px 20px;
  color: white;
  text-decoration: none;
}

Cas 3 : réduire un bloc à la taille de son contenu. Un <div> est bloc par défaut : il prend toute la largeur disponible. Pour créer un badge ou une étiquette à partir d'un <div>, on le passe en display: inline-block afin qu'il ne prenne que la place de son contenu.

/* Sans modification : le div s'étire sur toute la ligne */
.badge { background-color: #2E6DA4; padding: 4px 12px; }

/* Avec inline-block : le div rétrécit à la taille du texte */
.badge {
  display: inline-block;
  background-color: #2E6DA4;
  color: white;
  padding: 4px 12px;
  border-radius: 20px;
}

Cas 4 : masquer un élément selon la taille d'écran. Combiné aux media queries, display: none permet d'afficher ou masquer des éléments selon le contexte. C'est une technique fondamentale du design responsive.

.menu-hamburger {
  display: none; /* Masqué sur grand écran */
}

@media (max-width: 768px) {
  .menu-hamburger {
    display: block; /* Visible sur mobile */
  }
  .nav-bureau {
    display: none; /* Masquée sur mobile */
  }
}

Exercice 4 — La barre de navigation d'un blog

Objectif : construire la barre de navigation horizontale d'un blog à partir d'une liste HTML sémantique. L'exercice cible précisément les deux conversions de display les plus courantes en navigation : les <li> de blocs à inline-block, et les <a> d'inline à block.

Consignes HTML :

  • Créez un fichier blog-nav.html.
  • Créez un <header> contenant un <h1> avec le nom du blog de votre choix.
  • Créez une <nav> contenant une <ul> avec quatre <li>. Chaque <li> contient un <a href="#"> avec les rubriques : Accueil, Articles, À propos, Contact.
  • Sous la <nav>, ajoutez un <p> avec un court texte de bienvenue.
  • Liez un fichier blog-nav.css.

Consignes CSS :

  • Le <header> a un fond sombre, du padding et le <h1> est blanc et sans marge.
  • La <nav> a un fond d'une couleur légèrement différente du header.
  • Sur la <ul> : retirez les puces et marges par défaut, appliquez font-size: 0.
  • Sur les <li> : passez-les en display: inline-block et restaurez font-size: 15px.
  • Sur les <a> : passez-les en display: block, ajoutez du padding, retirez le soulignement, colorez le texte en blanc. Ajoutez un changement de couleur de fond au survol.
  • Le <p> sous la nav doit avoir du padding et une taille de police lisible.

Correction détaillée — Exercice 4

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="blog-nav.css">
  <title>Mon Blog</title>
</head>
<body>

  <header>
    <h1>Le Blog du Dev</h1>
  </header>

  <nav>
    <ul>
      <li><a href="#">Accueil</a></li>
      <li><a href="#">Articles</a></li>
      <li><a href="#">À propos</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
  </nav>

  <p class="intro">Bienvenue sur ce blog dédié au développement web. Retrouvez ici des articles, des tutoriels et des ressources pour progresser en HTML, CSS et JavaScript.</p>

</body>
</html>

Correction CSS

/* blog-nav.css — Correction exercice 4 */

body {
  margin: 0;
  font-family: Arial, sans-serif;
}

header {
  background-color: #1A3A5C;
  padding: 24px 30px;
}

header h1 {
  color: #ffffff;
  margin: 0;
  font-size: 26px;
}

nav {
  background-color: #2E6DA4;
}

nav ul {
  list-style: none; /* Supprime les puces par défaut */
  margin: 0;
  padding: 0 20px;
  font-size: 0;     /* Supprime les espaces blancs entre les li inline-block */
}

/*
  Les <li> sont des blocs par défaut : ils s'empileraient verticalement.
  display: inline-block les place côte à côte.
*/
nav li {
  display: inline-block;
  font-size: 15px; /* Restauration après le font-size: 0 du parent */
}

/*
  Les <a> sont inline par défaut : seul le texte est cliquable.
  display: block étend la zone cliquable à tout le padding.
*/
nav li a {
  display: block;
  padding: 14px 18px;
  color: #ffffff;
  text-decoration: none;
  transition: background-color 0.2s ease;
}

nav li a:hover {
  background-color: #1A3A5C;
}

.intro {
  padding: 24px 30px;
  font-size: 16px;
  line-height: 1.7;
  color: #333333;
}

Explications et points de vigilance

display: inline-block sur les <li> : c'est l'unique instruction qui transforme la liste verticale en barre de navigation horizontale. Sans elle, chaque <li> est un bloc et s'empile sous le précédent.

display: block sur les <a> : un lien inline n'est cliquable que sur son texte. En le passant en bloc, il remplit tout son <li> parent, padding compris. La zone de clic devient confortable. C'est une bonne pratique systématique pour tous les liens de navigation.

font-size: 0 sur <ul> puis restauration sur <li> : sans font-size: 0, un espace de quelques pixels apparaît entre chaque lien à cause des sauts de ligne dans le HTML. Cet espace est traité comme un caractère de texte. En annulant la taille de police sur le parent, ces espaces disparaissent. Il faut impérativement la restaurer sur les <li> sinon le texte des liens est invisible.

Erreur fréquente : appliquer display: inline-block sur les <a> au lieu des <li>. Cela fonctionne partiellement mais le <li> reste bloc et enveloppe le lien sans prendre la bonne taille — le résultat est visuellement incorrect.

Exercice 5 — Les étiquettes de catégories

Objectif : créer une liste de catégories d'articles affichées sous forme d'étiquettes colorées. L'exercice travaille spécifiquement la conversion d'un <div> en inline-block pour le réduire à la taille de son contenu (comportement de badge), et la mise en évidence visuelle d'une catégorie active avec display et des classes CSS.

Consignes HTML :

  • Créez un fichier categories.html.
  • Créez un <h2> avec le texte Filtrer par catégorie.
  • Créez une <div> avec la classe liste-categories contenant six <div> avec la classe categorie. Donnez à chacun une classe supplémentaire parmi : cat-tech, cat-design, cat-code, cat-actu, cat-video, cat-podcast. Les textes : Tech, Design, Code, Actualité, Vidéo, Podcast.
  • Ajoutez la classe active sur la catégorie Tech pour simuler la catégorie sélectionnée.
  • Sous la liste, ajoutez un <p class="info-masquee"> avec le texte Aucune catégorie sélectionnée.
  • Liez un fichier categories.css.

Consignes CSS :

  • Sur .liste-categories : ajoutez un peu de padding vertical.
  • Sur .categorie : passez-les en display: inline-block pour qu'ils se comportent comme des badges. Donnez-leur un padding, un border-radius, une border, une couleur de texte et un fond neutre. Ajoutez un margin pour les espacer et un effet de survol.
  • Donnez une couleur de fond et de texte distincte à chaque catégorie via sa classe spécifique (cat-tech, cat-design, etc.) uniquement au survol ou à l'état .active.
  • La classe .active doit appliquer un fond coloré et du texte blanc, sans bordure.
  • Le <p class="info-masquee"> doit être masqué avec visibility: hidden. Observez qu'il laisse un espace vide sous les étiquettes — c'est la différence avec display: none.

Correction détaillée — Exercice 5

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="categories.css">
  <title>Catégories</title>
</head>
<body>

  <h2>Filtrer par catégorie</h2>

  <div class="liste-categories">
    <div class="categorie cat-tech active">Tech</div>
    <div class="categorie cat-design">Design</div>
    <div class="categorie cat-code">Code</div>
    <div class="categorie cat-actu">Actualité</div>
    <div class="categorie cat-video">Vidéo</div>
    <div class="categorie cat-podcast">Podcast</div>
  </div>

  <p class="info-masquee">Aucune catégorie sélectionnée.</p>

</body>
</html>

Correction CSS

/* categories.css — Correction exercice 5 */

body {
  margin: 0;
  padding: 40px;
  font-family: Arial, sans-serif;
  background-color: #f5f5f5;
}

h2 {
  color: #1A3A5C;
  margin-bottom: 16px;
}

.liste-categories {
  padding: 10px 0;
}

/*
  Les .categorie sont des <div> : blocs par défaut.
  Sans display: inline-block, chaque catégorie s'étirerait
  sur toute la largeur de la page — comportement inadapté pour un badge.
  inline-block les réduit à la taille de leur contenu.
*/
.categorie {
  display: inline-block;
  padding: 8px 18px;
  margin: 4px;
  border-radius: 20px;
  border: 2px solid #cccccc;
  color: #555555;
  background-color: #ffffff;
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

/* Survol générique */
.categorie:hover {
  border-color: #2E6DA4;
  color: #2E6DA4;
}

/* Couleurs spécifiques au survol par catégorie */
.cat-tech:hover    { border-color: #2E6DA4; color: #2E6DA4; }
.cat-design:hover  { border-color: #8E44AD; color: #8E44AD; }
.cat-code:hover    { border-color: #1E6B2E; color: #1E6B2E; }
.cat-actu:hover    { border-color: #C0392B; color: #C0392B; }
.cat-video:hover   { border-color: #E67E22; color: #E67E22; }
.cat-podcast:hover { border-color: #16A085; color: #16A085; }

/* État actif : fond coloré, texte blanc, pas de bordure visible */
.cat-tech.active    { background-color: #2E6DA4; color: #ffffff; border-color: #2E6DA4; }
.cat-design.active  { background-color: #8E44AD; color: #ffffff; border-color: #8E44AD; }
.cat-code.active    { background-color: #1E6B2E; color: #ffffff; border-color: #1E6B2E; }
.cat-actu.active    { background-color: #C0392B; color: #ffffff; border-color: #C0392B; }
.cat-video.active   { background-color: #E67E22; color: #ffffff; border-color: #E67E22; }
.cat-podcast.active { background-color: #16A085; color: #ffffff; border-color: #16A085; }

/*
  visibility: hidden masque l'élément mais préserve son espace dans le flux.
  Comparez avec display: none : en passant à display: none,
  le paragraphe disparaît ET les étiquettes remontent.
  Avec visibility: hidden, l'espace est réservé et rien ne bouge.
*/
.info-masquee {
  visibility: hidden;
  font-size: 14px;
  color: #888888;
  margin-top: 12px;
}

Explications et points de vigilance

display: inline-block sur .categorie : c'est le cœur de l'exercice. Un <div> est un bloc — il prendrait toute la largeur de la page. inline-block le contraint à la taille de son texte, exactement comme se comporte un <span>, mais en lui conservant la capacité d'avoir padding, border et border-radius qui s'appliquent proprement dans toutes les directions.

visibility: hidden sur .info-masquee : observez attentivement dans le navigateur : sous les étiquettes, un espace vide subsiste là où se trouverait le paragraphe s'il était visible. C'est précisément la différence avec display: none. Essayez de remplacer visibility: hidden par display: none : les étiquettes descendent légèrement car l'espace réservé disparaît. Ce comportement est important à connaître pour les interfaces où la stabilité visuelle de la mise en page est cruciale.

Les classes combinées .cat-tech.active : cette notation CSS cible un élément qui possède à la fois la classe cat-tech ET la classe active. Pas d'espace entre les deux noms de classe — un espace signifierait un descendant, ce qui serait une sélection totalement différente.

Erreur fréquente : laisser display: block sur .categorie et s'étonner que les badges s'empilent verticalement. Le changement de display est ici la seule instruction qui transforme la mise en page de verticale en horizontale.

6.4 Positionnement CSS

La propriété position est celle qui vous donne un contrôle précis sur l'emplacement d'un élément dans la page, indépendamment du flux normal. Jusqu'ici, les éléments se plaçaient selon les règles du flux — les uns sous les autres ou côte à côte. Avec position, vous pouvez placer un élément exactement où vous le souhaitez, le fixer à l'écran, ou le décaler par rapport à sa position d'origine.

position fonctionne toujours en tandem avec quatre propriétés de décalage : top, right, bottom et left. Ces propriétés n'ont aucun effet si position vaut static (la valeur par défaut). Dès que position prend une autre valeur, elles entrent en jeu pour indiquer où placer l'élément.

element {
  position: static;    /* Valeur par défaut : l'élément suit le flux normal */
  position: relative;  /* Décalé par rapport à sa position d'origine dans le flux */
  position: absolute;  /* Positionné par rapport à son ancêtre positionné le plus proche */
  position: fixed;     /* Positionné par rapport à la fenêtre du navigateur */
  position: sticky;    /* Suit le défilement jusqu'à un seuil, puis se fixe */
}

6.4.1 Positionnement statique

position: static est la valeur par défaut de tous les éléments HTML. Vous n'avez jamais besoin de l'écrire explicitement, sauf pour annuler un positionnement défini ailleurs (par exemple dans une feuille de style tierce que vous souhaitez neutraliser).

/* Ces deux règles sont strictement équivalentes */
div { }
div { position: static; }

/* Cas d'usage réel : neutraliser un positionnement hérité */
.remise-a-zero {
  position: static; /* Annule un position: relative ou absolute défini ailleurs */
}

Un élément static ignore complètement les propriétés top, right, bottom et left. Si vous les écrivez, elles n'auront aucun effet. C'est le comportement le plus courant, celui que vous avez utilisé dans tous les exercices précédents sans le savoir.

6.4.2 Positionnement relatif

position: relative est le premier type de positionnement qui sort de l'ordinaire. Il a deux effets distincts qu'il est important de bien comprendre séparément.

Premier effet : décaler l'élément visuellement. L'élément est déplacé par rapport à la position qu'il aurait occupée normalement dans le flux. Les propriétés top, right, bottom et left définissent ce décalage.

.boite {
  position: relative;
  top: 20px;   /* Décale de 20px vers le bas par rapport à sa position naturelle */
  left: 30px;  /* Décale de 30px vers la droite */
}

Second effet — et c'est le plus important en pratique : créer un contexte de positionnement. Un élément en position: relative devient le point de référence pour tous ses descendants en position: absolute. C'est l'usage le plus fréquent du positionnement relatif, et nous y reviendrons en détail dans la section suivante.

Point crucial : contrairement à float ou position: absolute, un élément en position: relative reste dans le flux normal. L'espace qu'il occupait originellement est préservé. Les autres éléments ne le voient pas se déplacer — ils ignorent son décalage visuel.

/* Illustration : l'espace original est conservé */
.paragraphe-decale {
  position: relative;
  top: 40px;
  background-color: lightblue;
}

/*
  Visuellement, .paragraphe-decale descend de 40px.
  Mais le paragraphe suivant se positionne comme si
  .paragraphe-decale n'avait pas bougé.
  Un espace vide apparaît là où il était originellement.
*/

6.4.3 Positionnement absolu

position: absolute retire complètement l'élément du flux normal. Les autres éléments se comportent comme s'il n'existait plus, et comblent l'espace qu'il aurait occupé.

L'élément est ensuite positionné par rapport à son ancêtre positionné le plus proche, c'est-à-dire le premier ancêtre qui a une valeur de position différente de static. Si aucun ancêtre n'est positionné, l'élément se positionne par rapport au <body>.

/* Schéma classique : conteneur relatif + enfant absolu */

.conteneur {
  position: relative; /* Devient le point de référence pour les enfants absolus */
  width: 400px;
  height: 300px;
  background-color: #e8f0fe;
  border: 2px solid #2E6DA4;
}

.etiquette {
  position: absolute; /* Sort du flux, se positionne par rapport à .conteneur */
  top: 10px;          /* À 10px du bord supérieur de .conteneur */
  right: 10px;        /* À 10px du bord droit de .conteneur */
  background-color: #C0392B;
  color: white;
  padding: 4px 10px;
  font-size: 12px;
  border-radius: 3px;
}
<!-- Structure HTML correspondante -->
<div class="conteneur">
  <span class="etiquette">NOUVEAU</span>
  <p>Le contenu de la carte. L'étiquette "NOUVEAU" est positionnée
     en haut à droite de la carte, sans perturber ce texte.</p>
</div>

Ce schéma position: relative sur le parent + position: absolute sur l'enfant est l'un des patterns les plus utilisés en CSS. Il sert à créer des badges, des tooltips, des overlays, des icônes superposées à des images, etc.

Attention au piège de l'ancêtre : si vous oubliez de mettre position: relative sur le conteneur parent, l'élément absolu se positionnera par rapport au <body> entier, ce qui donnera presque toujours un résultat inattendu.

/* Positionnement aux quatre coins d'un conteneur */
.coin-haut-gauche  { top: 0;    left: 0;    }
.coin-haut-droit   { top: 0;    right: 0;   }
.coin-bas-gauche   { bottom: 0; left: 0;    }
.coin-bas-droit    { bottom: 0; right: 0;   }

/* Couvrir entièrement le conteneur parent (overlay) */
.overlay {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background-color: rgba(0, 0, 0, 0.5);
}

6.4.4 Positionnement fixe

position: fixed retire également l'élément du flux normal, mais le positionne par rapport à la fenêtre du navigateur (le viewport), et non par rapport à un ancêtre. L'élément reste donc visible à la même position même lorsque l'utilisateur fait défiler la page.

/* Barre de navigation fixe en haut de page */
.navbar-fixe {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;          /* S'étend sur toute la largeur de la fenêtre */
  background-color: #1A3A5C;
  color: white;
  padding: 15px 30px;
  z-index: 100;         /* Passe au-dessus des autres éléments */
}

/*
  Problème classique avec position: fixed :
  la navbar sort du flux et se superpose au contenu.
  Il faut compenser en ajoutant un padding-top au body
  égal à la hauteur de la navbar.
*/
body {
  padding-top: 60px; /* Valeur égale à la hauteur de la navbar */
}
/* Bouton "retour en haut" fixe en bas à droite */
.btn-retour-haut {
  position: fixed;
  bottom: 30px;
  right: 30px;
  width: 44px;
  height: 44px;
  background-color: #2E6DA4;
  color: white;
  text-align: center;
  line-height: 44px;
  border-radius: 50%;
  text-decoration: none;
  font-size: 20px;
}

Les usages typiques de position: fixed sont : barres de navigation persistantes, boutons flottants, bandeaux de cookies, chatbots, panier d'achat flottant dans un e-commerce.

6.4.5 Positionnement sticky

position: sticky est un hybride entre relative et fixed. L'élément se comporte comme un élément relatif — il suit le défilement normal de la page — jusqu'à ce qu'il atteigne un seuil défini par top, bottom, left ou right. À partir de ce seuil, il se comporte comme un élément fixe et reste visible à cette position pendant le défilement.

/* En-tête de tableau qui reste visible au défilement */
thead th {
  position: sticky;
  top: 0;               /* Se colle au bord supérieur de la fenêtre */
  background-color: #1A3A5C;
  color: white;
  padding: 12px 16px;
  z-index: 10;
}
/* Barre latérale qui suit le défilement */
.sidebar {
  position: sticky;
  top: 20px; /* La sidebar se colle à 20px du haut de la fenêtre */
  height: fit-content;
}

Condition indispensable : pour que position: sticky fonctionne, l'élément doit avoir de l'espace pour défiler à l'intérieur de son conteneur parent. Si le parent n'est pas plus haut que l'élément sticky lui-même, l'effet ne se produit pas. De plus, le parent ne doit pas avoir overflow: hidden ou overflow: auto, ce qui annulerait le comportement sticky.

Contrairement à position: fixed, un élément sticky reste dans le flux normal. Il n'y a pas besoin de compenser son espace avec du padding sur le body.

Exercice 6 — La carte produit avec badge

Objectif : construire une carte produit avec un badge "Nouveauté" positionné en haut à droite de la carte. L'exercice travaille le pattern fondamental position: relative sur le parent + position: absolute sur l'enfant, qui est le schéma de positionnement le plus utilisé en CSS au quotidien.

Consignes HTML :

  • Créez un fichier carte-produit.html.
  • Créez une <div> avec la classe carte. À l'intérieur, placez :
    • Un <span> avec la classe badge et le texte Nouveauté.
    • Une <div> avec la classe image-produit (simulera l'image).
    • Un <h2> avec un nom de produit.
    • Un <p> avec la classe prix (ex : 49,00 €).
    • Un <p> avec une courte description.
  • Liez un fichier carte-produit.css.

Consignes CSS :

  • La .carte est centrée sur la page (max-width: 320px, margin: 40px auto). Donnez-lui un fond blanc, une ombre légère (box-shadow) et des coins arrondis. Elle doit avoir position: relative pour servir de référence au badge.
  • L'.image-produit est un bloc avec height: 200px et un fond coloré.
  • Le .badge doit être positionné en absolute, ancré en haut à droite de la carte (top: 12px, right: 12px). Stylisez-le avec un fond rouge, du texte blanc, du padding et un border-radius.
  • Le h2 et le .prix ont simplement du padding horizontal et une couleur.

Correction détaillée — Exercice 6

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="carte-produit.css">
  <title>Carte produit</title>
</head>
<body>

  <div class="carte">
    <span class="badge">Nouveauté</span>
    <div class="image-produit"></div>
    <h2>Sac en cuir véritable</h2>
    <p class="prix">49,00 €</p>
    <p class="description">Fabriqué à la main, disponible en trois coloris. Livraison offerte dès 60€ d'achat.</p>
  </div>

</body>
</html>

Correction CSS

/* carte-produit.css — Correction exercice 6 */

body {
  margin: 0;
  background-color: #f0f4f8;
  font-family: Arial, sans-serif;
}

/*
  position: relative sur .carte est indispensable.
  Sans elle, le badge se positionnerait par rapport au <body>
  et non par rapport à la carte. C'est l'erreur la plus fréquente.
*/
.carte {
  position: relative;
  max-width: 320px;
  margin: 40px auto;
  background-color: #ffffff;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

.image-produit {
  width: 100%;
  height: 200px;
  background-color: #c8daf0;
}

/*
  position: absolute retire le badge du flux.
  Il se positionne par rapport à .carte (son ancêtre positionné).
  top et right définissent sa distance aux bords de .carte.
*/
.badge {
  position: absolute;
  top: 12px;
  right: 12px;
  background-color: #C0392B;
  color: #ffffff;
  font-size: 12px;
  font-weight: bold;
  padding: 5px 12px;
  border-radius: 20px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

h2 {
  padding: 16px 20px 4px;
  font-size: 18px;
  color: #1A3A5C;
  margin: 0;
}

.prix {
  padding: 0 20px;
  font-size: 20px;
  font-weight: bold;
  color: #C0392B;
  margin: 6px 0;
}

.description {
  padding: 0 20px 20px;
  font-size: 13px;
  color: #666666;
  line-height: 1.6;
  margin: 0;
}

Explications et points de vigilance

Pourquoi position: relative sur .carte ? C'est le point le plus important de l'exercice. Sans cette déclaration, le navigateur cherche l'ancêtre positionné le plus proche du badge — et ne trouvant rien, il remonte jusqu'au <body>. Le badge se placerait alors à 12px du coin supérieur droit de la page entière, pas de la carte. C'est l'erreur numéro un avec position: absolute.

Le badge sort du flux : le <span> avec position: absolute n'occupe plus d'espace dans le flux. Le <h2> qui le suit dans le HTML se positionne comme si le badge n'existait pas. C'est pourquoi l'image et le titre ne sont pas décalés par la présence du badge.

overflow: hidden sur .carte : cette propriété fait que l'image respecte les coins arrondis de la carte. Sans elle, le fond coloré de .image-produit déborderait des coins arrondis dans les angles supérieurs.

Erreur fréquente : mettre position: absolute sur .carte au lieu de position: relative. La carte sortirait alors du flux et se positionnerait par rapport au body — la mise en page serait complètement cassée.

Exercice 7 — Le bandeau de navigation fixe

Objectif : construire une page avec une barre de navigation fixe qui reste visible lors du défilement. L'exercice travaille position: fixed et la compensation du chevauchement avec le contenu — un problème concret que tout développeur rencontre dès la première utilisation de ce type de positionnement.

Consignes HTML :

  • Créez un fichier nav-fixe.html.
  • Créez une <nav> avec la classe navbar contenant un <span> avec la classe logo (texte : MonSite) et une <ul> avec trois <li> / <a> : Accueil, Services, Contact.
  • Créez un <main> avec la classe contenu contenant trois <section>, chacune avec un <h2> et deux <p> de texte fictif suffisamment long pour forcer le défilement de la page.
  • Liez un fichier nav-fixe.css.

Consignes CSS :

  • La .navbar doit être en position: fixed, collée en haut de la fenêtre (top: 0, left: 0), avec width: 100%. Donnez-lui un fond sombre, du padding, et un z-index: 100.
  • Sur la .navbar, affichez le .logo et la <ul> côte à côte : le logo à gauche, la liste à droite. Utilisez display: inline-block et float: right sur la liste (ou la technique de votre choix avec ce que vous avez vu jusqu'ici).
  • Les <li> de la navbar sont en inline-block, les <a> en block. Retirez puces et soulignements.
  • Le .contenu doit avoir un padding-top égal à la hauteur de la navbar (environ 60px) pour que le premier titre ne soit pas masqué derrière elle.
  • Chaque <section> a du padding et une bordure basse pour la séparer visuellement.

Correction détaillée — Exercice 7

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="nav-fixe.css">
  <title>Navigation fixe</title>
</head>
<body>

  <nav class="navbar">
    <span class="logo">MonSite</span>
    <ul>
      <li><a href="#">Accueil</a></li>
      <li><a href="#">Services</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
  </nav>

  <main class="contenu">

    <section>
      <h2>Bienvenue</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
      <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    </section>

    <section>
      <h2>Nos services</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p>
      <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident deserunt mollit anim id est laborum.</p>
    </section>

    <section>
      <h2>Contactez-nous</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.</p>
      <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt.</p>
    </section>

  </main>

</body>
</html>

Correction CSS

/* nav-fixe.css — Correction exercice 7 */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: Arial, sans-serif;
  color: #2c2c2c;
}

/*
  position: fixed sort la navbar du flux et la colle à la fenêtre.
  Elle reste visible même quand l'utilisateur fait défiler la page.
  width: 100% est nécessaire car un élément fixe ne prend pas
  automatiquement toute la largeur — il faut le préciser.
*/
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  background-color: #1A3A5C;
  padding: 0 30px;
  height: 60px;
  line-height: 60px;
  z-index: 100;
  font-size: 0; /* Supprime l'espace blanc entre .logo et ul */
}

.logo {
  display: inline-block;
  font-size: 20px;
  font-weight: bold;
  color: #ffffff;
  letter-spacing: 1px;
}

.navbar ul {
  display: inline-block;
  float: right;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0;
}

.navbar li {
  display: inline-block;
  font-size: 14px;
}

.navbar li a {
  display: block;
  padding: 0 16px;
  color: #ffffff;
  text-decoration: none;
  transition: background-color 0.2s ease;
}

.navbar li a:hover {
  background-color: #2E6DA4;
}

/*
  Sans ce padding-top, le premier <h2> du contenu se placerait
  DERRIÈRE la navbar fixe et serait partiellement masqué.
  La valeur doit être égale ou légèrement supérieure à la hauteur
  de la navbar (ici 60px).
*/
.contenu {
  padding-top: 60px;
}

section {
  padding: 40px 30px;
  border-bottom: 1px solid #e0e0e0;
  max-width: 800px;
  margin: 0 auto;
}

section h2 {
  color: #1A3A5C;
  margin-top: 0;
}

section p {
  line-height: 1.8;
  color: #444444;
}

Explications et points de vigilance

position: fixed sort la navbar du flux : la navbar n'occupe plus d'espace dans la page. Sans le padding-top: 60px sur .contenu, le premier titre serait masqué derrière la navbar. C'est le problème le plus fréquent rencontré avec position: fixed : on fixe la nav, on défile, et on constate que le début du contenu est inaccessible.

width: 100% est obligatoire : un élément fixe ou absolu perd sa largeur automatique de 100%. Il prend uniquement la place de son contenu. Si vous oubliez width: 100%, la navbar ne couvrira que la largeur de son texte.

z-index: 100 : sans cette propriété, certains éléments de la page pourraient passer par-dessus la navbar lors du défilement. Le z-index garantit que la navbar reste toujours au premier plan. Nous détaillerons le fonctionnement du z-index dans la section 6.5.

position: sticky vs position: fixed : si vous remplacez fixed par sticky avec top: 0 dans cet exercice, vous observerez que la navbar suit le défilement jusqu'à atteindre le haut de la fenêtre, puis se colle. La différence majeure : avec sticky, le padding-top sur .contenu n'est plus nécessaire car l'élément reste dans le flux.

6.5 Empilement et superposition

Dès que vous commencez à utiliser position: absolute, position: fixed ou position: relative avec des décalages, des éléments peuvent se retrouver à se superposer. Le navigateur doit alors décider lequel s'affiche par-dessus l'autre. Cette décision n'est pas arbitraire : elle obéit à des règles précises que vous allez maintenant maîtriser.

6.5.1 Principe du z-index

Par défaut, quand deux éléments se superposent, c'est celui qui apparaît en dernier dans le code HTML qui s'affiche par-dessus. C'est la règle de l'ordre du document : le dernier arrivé est au premier plan.

<!-- Sans z-index : .rouge s'affiche PAR-DESSUS .bleu car il est après dans le HTML -->
<div class="bleu">Bleu</div>
<div class="rouge">Rouge</div>
.bleu {
  position: absolute;
  top: 20px; left: 20px;
  width: 100px; height: 100px;
  background-color: #2E6DA4;
}

.rouge {
  position: absolute;
  top: 50px; left: 50px;
  width: 100px; height: 100px;
  background-color: #C0392B;
  /* Rouge est après Bleu dans le HTML => Rouge est au-dessus */
}

La propriété z-index vous permet de contrôler explicitement cet ordre d'empilement. Elle accepte des valeurs entières, positives ou négatives. Plus la valeur est élevée, plus l'élément est au premier plan.

.bleu {
  position: absolute;
  z-index: 10; /* Bleu passe devant Rouge malgré son ordre dans le HTML */
}

.rouge {
  position: absolute;
  z-index: 5;  /* Rouge est maintenant derrière Bleu */
}

Condition indispensable : z-index ne fonctionne que sur les éléments dont la propriété position est différente de static. Sur un élément static (la valeur par défaut), z-index est simplement ignoré. C'est l'erreur la plus fréquente : écrire z-index: 999 sur un élément sans lui donner de position, et se demander pourquoi rien ne change.

/* z-index ignoré : position est static par défaut */
.element {
  z-index: 999; /* Aucun effet */
}

/* z-index actif : l'élément a une position différente de static */
.element {
  position: relative; /* ou absolute, fixed, sticky */
  z-index: 999;       /* Fonctionne */
}

Les valeurs négatives sont également possibles. Elles permettent de placer un élément derrière le flux normal du document, par exemple pour créer un effet de fond décoratif.

.decoration-arriere-plan {
  position: absolute;
  z-index: -1; /* Passe derrière les éléments du flux normal */
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-color: rgba(46, 109, 164, 0.1);
}

6.5.2 Contextes d'empilement

Le concept de contexte d'empilement est plus avancé, mais il est essentiel pour comprendre pourquoi z-index produit parfois des résultats inattendus.

Un contexte d'empilement est une zone indépendante dans laquelle les éléments sont empilés les uns par rapport aux autres. Chaque contexte d'empilement forme une sorte de groupe hermétique : les éléments à l'intérieur ne peuvent jamais passer devant ou derrière des éléments extérieurs à leur contexte, quelle que soit leur valeur de z-index.

Un contexte d'empilement est créé dans plusieurs situations :

/* 1. Un élément positionné avec un z-index autre que auto */
.nouveau-contexte {
  position: relative;
  z-index: 1; /* Crée un nouveau contexte d'empilement */
}

/* 2. Un élément avec opacity inférieur à 1 */
.nouveau-contexte {
  opacity: 0.99; /* Crée un contexte d'empilement, même sans position */
}

/* 3. Un élément avec transform */
.nouveau-contexte {
  transform: translateX(0); /* Crée un contexte d'empilement */
}

Voici l'exemple classique qui déroute les développeurs :

<div class="parent-a">   <!-- z-index: 1 => contexte A -->
  <div class="enfant-a"> <!-- z-index: 999, mais DANS le contexte A -->
  </div>
</div>

<div class="parent-b">   <!-- z-index: 2 => contexte B, AU-DESSUS de A -->
  <div class="enfant-b"> <!-- z-index: 1, mais dans le contexte B -->
  </div>
</div>
.parent-a {
  position: relative;
  z-index: 1; /* Contexte A : z-index 1 par rapport au document */
}

.enfant-a {
  position: relative;
  z-index: 999;
  /* Malgré son z-index énorme, .enfant-a reste dans le contexte A.
     Il ne peut jamais passer devant .parent-b ni .enfant-b,
     car le contexte A (z-index: 1) est sous le contexte B (z-index: 2). */
}

.parent-b {
  position: relative;
  z-index: 2; /* Contexte B est au-dessus du contexte A */
}

.enfant-b {
  position: relative;
  z-index: 1;
  /* .enfant-b passe devant .enfant-a malgré son z-index de 1 vs 999,
     car le contexte B (z-index: 2) est au-dessus du contexte A (z-index: 1). */
}

À retenir : le z-index d'un élément n'est comparé qu'avec les éléments du même contexte d'empilement. Pour comparer des éléments de contextes différents, c'est le z-index de leurs contextes respectifs qui compte, pas le leur propre.

En pratique, pour éviter les surprises avec les contextes d'empilement, il vaut mieux :

  • Utiliser des valeurs de z-index espacées (10, 20, 30...) plutôt que consécutives (1, 2, 3), pour pouvoir en insérer facilement une entre deux.
  • Éviter de multiplier les contextes d'empilement inutilement (notamment via opacity et transform sur des conteneurs).
  • Garder les éléments qui doivent se superposer dans le même contexte d'empilement autant que possible.

Exercice 8 — La carte avec overlay au survol

Objectif : construire une carte d'article avec une image (simulée par un bloc coloré) sur laquelle apparaît un overlay semi-transparent au survol, avec un texte centré par-dessus. L'exercice travaille position: absolute pour l'overlay, z-index pour contrôler qui passe devant qui, et :hover pour déclencher l'apparition.

Consignes HTML :

  • Créez un fichier overlay.html.
  • Créez une <div> avec la classe carte-article. À l'intérieur :
    • Une <div> avec la classe image-wrapper contenant :
      • Une <div> avec la classe image (simulera l'image).
      • Une <div> avec la classe overlay contenant un <p> avec le texte Lire l'article.
    • Un <h2> avec un titre d'article.
    • Un <p> avec la classe resume et un court texte.
  • Liez un fichier overlay.css.

Consignes CSS :

  • La .carte-article est centrée (max-width: 360px, margin: 60px auto), avec fond blanc et ombre.
  • L'.image-wrapper a une position relative et overflow: hidden. Il a une hauteur de 220px.
  • L'.image remplit entièrement son parent (toute la largeur, toute la hauteur) avec un fond coloré.
  • L'.overlay est en position absolute, couvre entièrement l'.image-wrapper (top: 0, left: 0, width: 100%, height: 100%). Son fond est rgba(0, 0, 0, 0.55). Il est masqué par défaut avec une opacité de 0 et une transition.
  • Le texte Lire l'article dans l'overlay doit être centré horizontalement et verticalement (utilisez text-align: center et line-height égale à la hauteur de l'overlay). Il est blanc et en gras.
  • Au survol de .carte-article, l'overlay doit apparaître (opacity: 1).

Correction détaillée — Exercice 8

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="overlay.css">
  <title>Carte avec overlay</title>
</head>
<body>

  <div class="carte-article">

    <div class="image-wrapper">
      <div class="image"></div>
      <div class="overlay">
        <p>Lire l'article</p>
      </div>
    </div>

    <h2>Les secrets du positionnement CSS</h2>
    <p class="resume">Découvrez comment maîtriser position, z-index et les contextes d'empilement pour créer des interfaces précises et robustes.</p>

  </div>

</body>
</html>

Correction CSS

/* overlay.css — Correction exercice 8 */

body {
  margin: 0;
  background-color: #f0f4f8;
  font-family: Arial, sans-serif;
}

.carte-article {
  max-width: 360px;
  margin: 60px auto;
  background-color: #ffffff;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  overflow: hidden;
}

/*
  position: relative transforme .image-wrapper en point de référence
  pour l'overlay en position: absolute.
  overflow: hidden s'assure que l'overlay ne déborde pas
  en dehors des coins arrondis de la carte.
*/
.image-wrapper {
  position: relative;
  height: 220px;
  overflow: hidden;
}

/* L'image remplit tout le wrapper */
.image {
  width: 100%;
  height: 100%;
  background-color: #a8c4e0;
}

/*
  L'overlay est en position: absolute : il sort du flux et se
  positionne par rapport à .image-wrapper (son parent positionné).
  top/left/width/height à 100% le font couvrir entièrement l'image.
  opacity: 0 le rend invisible par défaut sans le retirer du flux.
  La transition rend l'apparition progressive et fluide.
*/
.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.overlay p {
  color: #ffffff;
  font-weight: bold;
  font-size: 18px;
  text-align: center;
  /* line-height égale à la hauteur du parent pour centrer verticalement */
  line-height: 220px;
  margin: 0;
  letter-spacing: 1px;
  text-transform: uppercase;
}

/*
  Au survol de la carte entière, l'overlay devient visible.
  On cible .carte-article:hover pour que le survol sur le texte
  sous l'image ne fasse pas disparaître l'overlay.
*/
.carte-article:hover .overlay {
  opacity: 1;
}

.carte-article h2 {
  padding: 16px 20px 6px;
  font-size: 17px;
  color: #1A3A5C;
  margin: 0;
}

.resume {
  padding: 0 20px 20px;
  font-size: 13px;
  color: #666666;
  line-height: 1.6;
  margin: 0;
}

Explications et points de vigilance

position: relative sur .image-wrapper et position: absolute sur .overlay : c'est à nouveau le pattern fondamental. L'overlay doit se superposer précisément à l'image — pas à la carte entière, pas à la page. En mettant position: relative sur .image-wrapper, on garantit que l'overlay absolu se positionne par rapport à lui, et uniquement lui.

opacity: 0 pour masquer plutôt que display: none : si l'on utilisait display: none pour masquer l'overlay et display: block pour l'afficher, la propriété transition n'aurait aucun effet — on ne peut pas faire de transition sur display. opacity, en revanche, est une propriété numérique qui se prête parfaitement aux transitions fluides.

.carte-article:hover .overlay : cette règle CSS signifie "l'overlay qui se trouve à l'intérieur d'une .carte-article survolée". En ciblant le survol sur le conteneur parent plutôt que sur l'overlay lui-même, on évite l'effet de scintillement qui apparaîtrait si l'on ciblait .overlay:hover — l'overlay apparaîtrait et disparaîtrait en boucle au passage de la souris.

Erreur fréquente : oublier position: relative sur .image-wrapper. L'overlay remonterait alors jusqu'au premier ancêtre positionné, voire jusqu'au <body>, et couvrirait toute la page au lieu de l'image uniquement.

Exercice 9 — Les calques superposés

Objectif : créer une composition graphique de trois calques colorés qui se superposent, et contrôler leur ordre d'affichage exclusivement avec z-index. L'exercice est volontairement ciblé sur un seul concept : observer et manipuler l'ordre d'empilement, et comprendre pourquoi z-index ne fonctionne que sur les éléments positionnés.

Consignes HTML :

  • Créez un fichier calques.html.
  • Créez une <div> avec la classe scene.
  • À l'intérieur, créez trois <div> avec respectivement les classes calque calque-bleu, calque calque-rouge, calque calque-jaune. Chaque <div> contient un <span> avec son nom : Calque Bleu, Calque Rouge, Calque Jaune.
  • Liez un fichier calques.css.

Consignes CSS :

  • La .scene a position: relative, une largeur de 400px, une hauteur de 300px et un fond gris clair. Elle est centrée sur la page.
  • Tous les .calque sont en position: absolute, mesurent 200px par 200px et ont un border-radius: 8px et un padding: 12px.
  • Positionnez les calques pour qu'ils se chevauchent partiellement :
    • .calque-bleu : top: 20px, left: 20px
    • .calque-rouge : top: 60px, left: 80px
    • .calque-jaune : top: 100px, left: 140px
  • Sans z-index, observez l'ordre d'affichage par défaut (le dernier dans le HTML est au premier plan).
  • Ajoutez ensuite des z-index pour que l'ordre soit : Bleu au-dessus, puis Jaune, puis Rouge en dessous. Ce résultat va à l'encontre de l'ordre du HTML — c'est l'objectif.
  • Tentez d'ajouter z-index: 999 sur .calque-rouge sans lui donner de position et observez que rien ne change.

Correction détaillée — Exercice 9

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="calques.css">
  <title>Calques superposés</title>
</head>
<body>

  <div class="scene">
    <div class="calque calque-bleu">
      <span>Calque Bleu</span>
    </div>
    <div class="calque calque-rouge">
      <span>Calque Rouge</span>
    </div>
    <div class="calque calque-jaune">
      <span>Calque Jaune</span>
    </div>
  </div>

</body>
</html>

Correction CSS

/* calques.css — Correction exercice 9 */

body {
  margin: 0;
  padding: 60px;
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
}

.scene {
  position: relative; /* Contexte de référence pour les calques absolus */
  width: 400px;
  height: 300px;
  background-color: #e8e8e8;
  border: 2px dashed #aaaaaa;
  border-radius: 8px;
}

.calque {
  position: absolute; /* Tous les calques sortent du flux */
  width: 200px;
  height: 200px;
  border-radius: 8px;
  padding: 12px;
  font-weight: bold;
  font-size: 14px;
  color: #ffffff;
}

/* Positionnement des calques pour qu'ils se chevauchent */
.calque-bleu  { top: 20px;  left: 20px;  background-color: #2E6DA4; }
.calque-rouge { top: 60px;  left: 80px;  background-color: #C0392B; }
.calque-jaune { top: 100px; left: 140px; background-color: #E0A800; color: #333; }

/*
  Sans z-index : l'ordre d'affichage suit l'ordre du HTML.
  Jaune est au-dessus (dernier dans le HTML), puis Rouge, puis Bleu.

  Avec z-index : on inverse cet ordre.
  Bleu au-dessus (z-index le plus élevé), puis Jaune, puis Rouge.

  Note : z-index fonctionne car tous les .calque ont position: absolute.
  Sur un élément static, z-index serait totalement ignoré.
*/
.calque-bleu  { z-index: 3; } /* Premier plan */
.calque-jaune { z-index: 2; } /* Deuxième plan */
.calque-rouge { z-index: 1; } /* Arrière-plan */

Explications et points de vigilance

L'ordre par défaut sans z-index : avant d'ajouter les z-index, l'ordre d'empilement suit strictement l'ordre du HTML. Jaune, étant le dernier <div> dans le code, est au premier plan. C'est la règle de base : le dernier arrivé gagne.

z-index renverse l'ordre : en donnant à Bleu le z-index le plus élevé, il passe devant Rouge et Jaune, même s'il apparaît en premier dans le HTML. C'est exactement le pouvoir de z-index : découpler l'ordre visuel de l'ordre du code.

z-index sans position = aucun effet : si vous retirez position: absolute de .calque-rouge et que vous lui mettez z-index: 999, rien ne change. Le navigateur ignore complètement le z-index sur les éléments statiques. C'est la règle absolue à mémoriser.

Valeurs espacées : notez que les z-index utilisés sont 1, 2, 3. En production, on préférera 10, 20, 30 ou même 100, 200, 300 — pour pouvoir facilement insérer une nouvelle valeur entre deux existantes sans avoir à tout renuméroter.

6.6 Flottants et héritage historique

Avant l'arrivée de Flexbox et CSS Grid, les développeurs web n'avaient pas de véritable outil natif pour créer des mises en page en colonnes. La propriété float a été détournée de son usage original pour remplir ce rôle pendant plus d'une décennie. Aujourd'hui, float est revenu à sa vocation première, mais vous la croiserez inévitablement dans du code existant. Il est donc indispensable de la comprendre en profondeur.

6.6.1 Origine des flottants

La propriété float a été conçue à l'origine pour un usage typographique : permettre à une image de flotter sur le côté d'un bloc de texte, exactement comme dans la mise en page d'un journal ou d'un magazine. Le texte environnant devait alors se répartir naturellement autour de l'image.

/* Usage original et toujours pertinent de float */
.illustration {
  float: left;         /* L'image flotte à gauche */
  margin-right: 20px;  /* Espace entre l'image et le texte */
  margin-bottom: 10px;
  width: 200px;
}

/*
  Le texte contenu dans le même bloc parent va automatiquement
  s'enrouler autour de l'image flottante, à sa droite et en dessous.
  C'est le comportement natif et voulu de float.
*/
<div class="article">
  <img class="illustration" src="photo.jpg" alt="Illustration">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
     Le texte se répartit naturellement autour de l'image flottante,
     exactement comme dans une mise en page de magazine...</p>
</div>

C'est dans les années 2000, face à l'absence d'alternatives, que les développeurs ont commencé à utiliser float pour créer des mises en page entières en colonnes. Cette pratique a fonctionné, mais elle a introduit des comportements complexes et souvent contre-intuitifs que les développeurs ont dû apprendre à gérer à la main. Flexbox (2012) puis CSS Grid (2017) ont mis fin à cette pratique, mais des millions de lignes de code utilisant float pour la mise en page existent encore aujourd'hui.

6.6.2 Utilisation de float

La propriété float accepte trois valeurs principales : left, right et none (la valeur par défaut).

element { float: left;  } /* L'élément flotte à gauche, le contenu s'enroule à droite */
element { float: right; } /* L'élément flotte à droite, le contenu s'enroule à gauche */
element { float: none;  } /* Comportement par défaut, annule un float hérité */

Quand un élément reçoit float: left ou float: right, plusieurs choses se produisent simultanément qu'il est crucial de bien comprendre :

Premièrement : l'élément est partiellement retiré du flux normal. Il sort du flux mais reste dans son contexte de formatage. Les éléments blocs qui le suivent se comportent comme s'il n'existait pas et remontent pour occuper l'espace. En revanche, le contenu inline (texte, images inline) de ces blocs s'enroule autour du flottant.

.photo {
  float: left;
  width: 250px;
  margin-right: 20px;
}
<div class="conteneur">
  <div class="photo">[ image ]</div>
  <p>Ce texte s'enroule autour de la photo flottante.
     Le div <p> lui-même remonte comme si la photo n'existait pas,
     mais son contenu texte s'enroule autour du flottant.
     C'est le comportement hybride caractéristique de float.</p>
</div>

Deuxièmement : l'élément flottant prend la largeur de son contenu (ou la largeur que vous lui définissez explicitement). Contrairement à un bloc normal, il ne s'étire plus sur 100% de la largeur disponible.

Troisièmement : le conteneur parent ne voit plus le flottant pour le calcul de sa hauteur. C'est le problème le plus connu de float : si un conteneur ne contient que des éléments flottants, sa hauteur calculée est zéro. Il s'effondre sur lui-même.

/* Problème classique : le conteneur s'effondre */
.conteneur {
  background-color: lightblue;
  /* Si tous les enfants sont en float, la hauteur du conteneur est 0.
     Le fond bleu ne sera pas visible. */
}

.colonne-gauche {
  float: left;
  width: 45%;
}

.colonne-droite {
  float: right;
  width: 45%;
}

Voici un exemple complet d'une mise en page deux colonnes avec float, telle qu'elle était couramment écrite avant Flexbox :

/* Mise en page deux colonnes à l'ancienne */

.conteneur {
  width: 900px;
  margin: 0 auto;
}

.colonne-principale {
  float: left;
  width: 620px;
}

.colonne-secondaire {
  float: right;
  width: 240px;
}

/* Sans clearfix, .conteneur aurait une hauteur de 0 */
/* (voir section 6.6.3 pour la solution) */

6.6.3 Dégagement avec clear

La propriété clear est la réponse directe aux problèmes causés par float. Elle indique à un élément qu'il ne doit pas se placer à côté d'un flottant, mais passer en dessous de lui.

element { clear: left;  }
/* L'élément passe sous tous les flottants à gauche */

element { clear: right; }
/* L'élément passe sous tous les flottants à droite */

element { clear: both;  }
/* L'élément passe sous tous les flottants, quel que soit leur côté */
/* C'est la valeur la plus utilisée en pratique */

element { clear: none;  }
/* Valeur par défaut : l'élément ne tient pas compte des flottants */

Le problème du conteneur effondré et ses solutions.

Quand un conteneur ne contient que des éléments flottants, il s'effondre à une hauteur nulle. Plusieurs solutions existent pour forcer le conteneur à englober ses enfants flottants.

Solution 1 : l'élément vide avec clear: both (ancienne méthode, déconseillée).

<div class="conteneur">
  <div class="colonne-gauche">...</div>
  <div class="colonne-droite">...</div>
  <div class="clearfix-element"></div> <!-- Élément vide ajouté -->
</div>
.clearfix-element {
  clear: both; /* Force le conteneur à s'étendre sous les flottants */
}
/* Inconvénient : pollue le HTML avec un élément purement décoratif */

Solution 2 : overflow: hidden sur le conteneur (méthode simple).

.conteneur {
  overflow: hidden;
  /* ou overflow: auto; */
  /* Crée un nouveau contexte de formatage de bloc qui englobe les flottants */
}
/* Inconvénient : peut masquer du contenu qui déborde intentionnellement */

Solution 3 : le clearfix avec pseudo-élément (méthode recommandée).

C'est la solution la plus propre et la plus utilisée. Elle consiste à utiliser le pseudo-élément ::after pour insérer automatiquement un élément de dégagement après le conteneur, sans toucher au HTML.

/* La classe clearfix à appliquer sur tout conteneur de flottants */
.clearfix::after {
  content: "";        /* Le pseudo-élément doit avoir un contenu, même vide */
  display: block;     /* Doit être un bloc pour que clear fonctionne */
  clear: both;        /* Passe sous tous les flottants */
}
<!-- En HTML : on ajoute simplement la classe clearfix au conteneur -->
<div class="conteneur clearfix">
  <div class="colonne-gauche">...</div>
  <div class="colonne-droite">...</div>
  <!-- Plus besoin d'élément vide dans le HTML -->
</div>

Bonne pratique : définissez la classe .clearfix une seule fois dans votre CSS et appliquez-la à tous les conteneurs de flottants. C'est une règle que l'on retrouve dans quasiment toutes les feuilles de style des projets utilisant float pour la mise en page.

Voici un exemple complet et fonctionnel d'une mise en page avec float et clearfix :

/* Exemple complet : float + clearfix */

.conteneur {
  width: 860px;
  margin: 0 auto;
  background-color: #f0f4f8;
  padding: 20px;
}

/* Le clearfix empêche le conteneur de s'effondrer */
.conteneur::after {
  content: "";
  display: block;
  clear: both;
}

.principale {
  float: left;
  width: 580px;
  background-color: #ffffff;
  padding: 20px;
}

.secondaire {
  float: right;
  width: 220px;
  background-color: #e8f0fe;
  padding: 20px;
}

/* Le footer passe sous les deux colonnes flottantes */
.pied-de-page {
  clear: both;
  background-color: #1A3A5C;
  color: white;
  padding: 20px;
  text-align: center;
}

Exercice 10 — L'article de magazine avec image flottante

Objectif : reproduire la mise en page typographique originelle de float : une image qui flotte à gauche d'un article, avec le texte qui s'enroule naturellement autour d'elle. C'est l'usage le plus légitime et le plus simple de float, celui pour lequel il a été conçu.

Consignes HTML :

  • Créez un fichier magazine.html.
  • Créez une <article> avec la classe article-magazine.
  • À l'intérieur, placez dans cet ordre :
    • Un <div> avec la classe photo (simulera la photo de l'article).
    • Un <h2> avec un titre d'article.
    • Trois <p> avec du texte fictif suffisamment long (au moins 4-5 lignes chacun) pour que le texte s'enroule bien autour de la photo.
    • Un <div> avec la classe auteur contenant un <span> avec le nom d'un auteur fictif et un <span> avec la classe date contenant une date.
  • Liez un fichier magazine.css.

Consignes CSS :

  • L'article-magazine a un max-width de 720px, est centré, avec un fond blanc, du padding et une ombre légère.
  • La .photo flotte à gauche, mesure 240px de large et 180px de haut, avec un fond coloré, un border-radius, et une marge à droite et en bas pour l'espacer du texte.
  • Le <h2> a une couleur sombre et une marge basse réduite.
  • Les <p> ont une taille de police de 15px et un interligne confortable.
  • La .auteur doit passer sous la photo et le texte grâce à clear: both. Elle a une bordure haute, du padding et les deux <span> sont stylisés différemment (nom en gras, date en italique et plus clair).

Correction détaillée — Exercice 10

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="magazine.css">
  <title>Article de magazine</title>
</head>
<body>

  <article class="article-magazine">

    <div class="photo"></div>

    <h2>La typographie web à l'ère du CSS moderne</h2>

    <p>Depuis les débuts du web, la typographie a toujours été au cœur des préoccupations des designers. Les premières pages HTML ne disposaient que d'un nombre très limité de polices système, et la mise en forme était rudimentaire. C'est l'arrivée progressive du CSS qui a permis d'affiner considérablement le contrôle typographique.</p>

    <p>Aujourd'hui, grâce aux polices web et aux services comme Google Fonts, les designers disposent de milliers de familles typographiques. Les propriétés CSS permettent de contrôler finement l'interlignage, l'espacement entre les lettres, la casse, la graisse et bien d'autres paramètres. La lisibilité est devenue une discipline à part entière dans la conception d'interfaces.</p>

    <p>L'interlignage optimal se situe généralement entre 1.5 et 1.8 fois la taille de la police pour le corps de texte. Une largeur de colonne de 60 à 75 caractères par ligne est considérée comme idéale pour la lecture. Ces règles, issues de la typographie traditionnelle, s'appliquent tout aussi bien sur écran que sur papier.</p>

    <div class="auteur">
      <span class="nom-auteur">Sophie Marchand</span>
      <span class="date">14 novembre 2024</span>
    </div>

  </article>

</body>
</html>

Correction CSS

/* magazine.css — Correction exercice 10 */

body {
  margin: 0;
  padding: 40px;
  background-color: #f5f5f0;
  font-family: Georgia, serif;
  color: #2c2c2c;
}

.article-magazine {
  max-width: 720px;
  margin: 0 auto;
  background-color: #ffffff;
  padding: 40px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

/*
  float: left sort la photo du flux et la colle à gauche.
  Le texte (inline) des paragraphes suivants s'enroule autour d'elle.
  margin-right et margin-bottom créent l'espace entre la photo et le texte.
*/
.photo {
  float: left;
  width: 240px;
  height: 180px;
  background-color: #c8daf0;
  border-radius: 4px;
  margin-right: 24px;
  margin-bottom: 12px;
}

h2 {
  font-size: 22px;
  color: #1A3A5C;
  margin-top: 0;
  margin-bottom: 14px;
  line-height: 1.3;
}

p {
  font-size: 15px;
  line-height: 1.8;
  margin-top: 0;
  margin-bottom: 14px;
}

/*
  clear: both force .auteur à se placer sous tous les flottants.
  Sans cette propriété, .auteur remonterait à côté de la photo
  si elle est plus haute que le texte, ou s'afficherait de manière
  imprévue selon la quantité de texte.
  border-top crée une séparation visuelle nette.
*/
.auteur {
  clear: both;
  border-top: 1px solid #e0e0e0;
  padding-top: 16px;
  margin-top: 8px;
  font-size: 13px;
}

.nom-auteur {
  font-weight: bold;
  color: #1A3A5C;
  margin-right: 16px;
}

.date {
  font-style: italic;
  color: #888888;
}

Explications et points de vigilance

float: left sur .photo : la photo sort partiellement du flux. Les blocs <p> qui suivent remontent comme si la photo n'existait pas, mais leur contenu texte s'enroule autour d'elle à droite. C'est exactement le comportement typographique original pour lequel float a été créé.

margin-right et margin-bottom sur .photo : sans ces marges, le texte collerait directement contre la photo. Ce sont les marges du flottant lui-même qui créent l'espace autour de lui — pas le padding ou les marges des paragraphes.

clear: both sur .auteur : sans cette propriété, si la photo est plus haute que les trois paragraphes, .auteur remonterait partiellement à côté de la photo. clear: both garantit qu'il se place toujours sous le flottant, quelle que soit la quantité de texte. Essayez de retirer cette propriété et de réduire le texte pour observer l'effet.

Erreur fréquente : mettre float sur la photo mais oublier de lui donner une largeur explicite. Sans width, un flottant prend la largeur de son contenu — ce qui est imprévisible pour un <div> vide ou une vraie image non contrainte.

Exercice 11 — La mise en page deux colonnes avec clearfix

Objectif : construire une mise en page classique avec une colonne principale et une colonne secondaire côte à côte grâce à float, un pied de page qui passe sous les deux colonnes avec clear, et un conteneur qui ne s'effondre pas grâce au clearfix. C'est la mise en page historique par excellence avec float.

Consignes HTML :

  • Créez un fichier deux-colonnes.html.
  • Créez un <header> avec un <h1> et un <p class="slogan">.
  • Créez une <div> avec les classes conteneur et clearfix contenant :
    • Une <div> avec la classe col-principale contenant un <h2>, deux <p> de texte.
    • Une <aside> avec la classe col-secondaire contenant un <h3> et une petite liste <ul> de trois liens.
  • Créez un <footer> avec un <p> de copyright.
  • Liez un fichier deux-colonnes.css.

Consignes CSS :

  • Le <header> a un fond sombre, du padding et le texte est centré en blanc.
  • Le .conteneur a un max-width de 900px, est centré et a du padding.
  • Appliquez le clearfix sur .clearfix::after pour éviter l'effondrement du conteneur.
  • .col-principale flotte à gauche et fait 62% de large. Elle a du padding et un fond blanc.
  • .col-secondaire flotte à droite et fait 30% de large. Elle a du padding et un fond légèrement coloré.
  • Le <footer> a clear: both pour passer sous les deux colonnes, un fond sombre et le texte centré en blanc.

Correction détaillée — Exercice 11

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="deux-colonnes.css">
  <title>Mise en page deux colonnes</title>
</head>
<body>

  <header>
    <h1>Le Journal du Dev</h1>
    <p class="slogan">Toute l'actualité du développement web</p>
  </header>

  <div class="conteneur clearfix">

    <div class="col-principale">
      <h2>CSS Grid : la révolution de la mise en page</h2>
      <p>Depuis son adoption par l'ensemble des navigateurs modernes, CSS Grid a profondément transformé la manière dont les développeurs conçoivent leurs mises en page. Fini le recours aux flottants détournés ou aux tableaux de présentation — Grid offre un système bidimensionnel puissant et intuitif.</p>
      <p>Les colonnes et les lignes se définissent directement sur le conteneur, et les éléments enfants se placent automatiquement ou manuellement dans la grille. La syntaxe, bien que dense au premier abord, devient rapidement naturelle et expressive.</p>
    </div>

    <aside class="col-secondaire">
      <h3>À lire aussi</h3>
      <ul>
        <li><a href="#">Introduction à Flexbox</a></li>
        <li><a href="#">Les media queries en 2024</a></li>
        <li><a href="#">Variables CSS natives</a></li>
      </ul>
    </aside>

  </div>

  <footer>
    <p>&copy; 2024 Le Journal du Dev. Tous droits réservés.</p>
  </footer>

</body>
</html>

Correction CSS

/* deux-colonnes.css — Correction exercice 11 */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: Arial, sans-serif;
  background-color: #f4f4f4;
  color: #2c2c2c;
}

header {
  background-color: #1A3A5C;
  padding: 24px 30px;
  text-align: center;
}

header h1 {
  color: #ffffff;
  margin: 0 0 6px 0;
  font-size: 28px;
}

.slogan {
  color: #a8c4e0;
  margin: 0;
  font-style: italic;
  font-size: 14px;
}

.conteneur {
  max-width: 900px;
  margin: 0 auto;
  padding: 24px;
}

/*
  Le clearfix empêche .conteneur de s'effondrer à hauteur zéro
  quand tous ses enfants sont en float.
  ::after insère un élément invisible après le contenu du conteneur,
  lequel force le conteneur à s'étendre sous ses enfants flottants.
*/
.clearfix::after {
  content: "";
  display: block;
  clear: both;
}

/*
  .col-principale flotte à gauche et occupe 62% de la largeur.
  box-sizing: border-box garantit que le padding est inclus dans ce %.
*/
.col-principale {
  float: left;
  width: 62%;
  background-color: #ffffff;
  padding: 24px;
  border: 1px solid #e0e0e0;
}

.col-principale h2 {
  color: #1A3A5C;
  margin-top: 0;
  font-size: 20px;
}

.col-principale p {
  line-height: 1.8;
  font-size: 15px;
  color: #444444;
}

/*
  .col-secondaire flotte à droite et occupe 30%.
  Les 8% restants (100 - 62 - 30) servent de gouttière entre les colonnes.
*/
.col-secondaire {
  float: right;
  width: 30%;
  background-color: #e8f0fe;
  padding: 20px;
  border: 1px solid #c8daf0;
}

.col-secondaire h3 {
  color: #1A3A5C;
  margin-top: 0;
  font-size: 16px;
  border-bottom: 2px solid #2E6DA4;
  padding-bottom: 8px;
}

.col-secondaire ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.col-secondaire li {
  padding: 8px 0;
  border-bottom: 1px solid #c8daf0;
}

.col-secondaire li:last-child {
  border-bottom: none;
}

.col-secondaire a {
  color: #2E6DA4;
  text-decoration: none;
  font-size: 14px;
}

.col-secondaire a:hover {
  text-decoration: underline;
}

/*
  clear: both sur le footer le force à se placer sous les deux colonnes
  flottantes, quelle que soit leur hauteur respective.
  Sans clear: both, le footer remonterait à côté des colonnes.
*/
footer {
  clear: both;
  background-color: #1A3A5C;
  color: #ffffff;
  text-align: center;
  padding: 20px 30px;
  font-size: 13px;
}

Explications et points de vigilance

La gouttière entre les colonnes : les deux colonnes font 62% + 30% = 92% de large. Les 8% restants forment naturellement l'espace entre elles. C'est une technique courante avec les flottants : on répartit les pourcentages de sorte que leur somme soit inférieure à 100%, et le reste crée la gouttière automatiquement.

box-sizing: border-box est indispensable : sans lui, le padding: 24px de .col-principale s'ajoute à ses 62% de largeur. La somme dépasse alors 100% et la colonne secondaire tombe à la ligne. border-box inclut le padding dans la largeur déclarée, ce qui rend les calculs de pourcentages fiables.

.clearfix::after sur le conteneur : sans le clearfix, .conteneur a une hauteur calculée de zéro car tous ses enfants sont des flottants. Son fond et sa bordure éventuels seraient invisibles, et footer remonterait directement sous header. Le clearfix force le conteneur à "voir" ses enfants flottants pour calculer sa hauteur.

clear: both sur footer : même avec le clearfix sur le conteneur, le footer est un élément extérieur au conteneur. Il faut lui indiquer explicitement de passer sous tous les flottants. Sans clear: both, il remonterait à côté des colonnes si sa largeur le permet.

Pourquoi ne plus utiliser float pour la mise en page ? Flexbox résout ces problèmes en une seule propriété, sans clearfix, sans calculs de pourcentages manuels, et avec un contrôle bien supérieur de l'alignement. float reste pertinent uniquement pour l'enroulement de texte autour d'images, son usage original.

6.7 Techniques de centrage

Le centrage est l'une des tâches les plus fréquentes en CSS, et paradoxalement l'une de celles qui a longtemps posé le plus de difficultés. Il n'existe pas une seule méthode universelle : la bonne technique dépend du type d'élément à centrer, de son contexte, et de si le centrage est horizontal, vertical, ou les deux. Cette section vous donne les outils pour répondre à chaque situation.

6.7.1 Centrage horizontal

Le centrage horizontal est le plus simple à obtenir, mais il existe plusieurs façons de le faire selon la nature de l'élément.

Centrer un élément bloc. Un élément bloc dont on a défini une largeur peut être centré horizontalement en lui appliquant margin: 0 auto. Les marges gauche et droite automatiques se partagent équitablement l'espace restant disponible de part et d'autre de l'élément.

/* margin: 0 auto fonctionne uniquement si :
   1. L'élément est un bloc (display: block)
   2. L'élément a une largeur définie (width ou max-width)
   Sans largeur explicite, le bloc prend 100% de son parent
   et il n'y a pas d'espace à partager — le centrage n'a aucun effet. */

.conteneur {
  width: 800px;    /* ou max-width: 800px */
  margin: 0 auto;  /* Gauche et droite automatiques = centrage */
}

C'est la technique la plus utilisée pour centrer le layout principal d'une page, les articles, les formulaires, et tous les blocs de contenu dont on veut limiter la largeur.

/* Usage typique : centrer le contenu principal d'une page */
.page {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px; /* Respiration sur les petits écrans */
}

Centrer du contenu inline ou inline-block. Pour centrer du texte, des images, des liens ou des éléments inline-block à l'intérieur de leur conteneur, on utilise text-align: center sur le parent. Cette propriété s'applique à tout le contenu inline du bloc.

/* text-align: center sur le parent centre tout son contenu inline */
.banniere {
  text-align: center;
  background-color: #1A3A5C;
  padding: 40px;
}

/* Le h1 et le p (dont le texte est inline) seront centrés */
/* Les éléments inline-block dans .banniere seront aussi centrés */
/* Centrer des boutons inline-block avec text-align sur le parent */
.groupe-boutons {
  text-align: center;
}

.bouton {
  display: inline-block;
  padding: 10px 24px;
  background-color: #2E6DA4;
  color: white;
}
/* Les boutons se centrent dans .groupe-boutons grâce au text-align du parent */

Distinction importante : text-align: center centre le contenu à l'intérieur d'un élément. margin: 0 auto centre l'élément lui-même dans son parent. Ces deux propriétés ne sont pas interchangeables.

Centrer un élément inline-block dans un bloc. Combinez les deux techniques : text-align: center sur le parent pour centrer l'inline-block, et si nécessaire text-align: left sur l'inline-block lui-même pour que son propre contenu texte ne soit pas centré.

.parent {
  text-align: center; /* Centre les enfants inline-block */
}

.enfant {
  display: inline-block;
  width: 300px;
  text-align: left; /* Le texte à l'intérieur reste aligné à gauche */
}

6.7.2 Centrage vertical

Le centrage vertical est historiquement beaucoup plus difficile à obtenir en CSS que le centrage horizontal. Contrairement à margin: 0 auto, il n'existe pas de propriété margin: auto 0 qui fonctionnerait pour les marges verticales dans le flux normal. Plusieurs techniques existent, chacune avec ses conditions et ses limites.

Technique 1 : line-height égale à la hauteur du conteneur. Cette technique fonctionne uniquement pour une seule ligne de texte dans un conteneur à hauteur fixe. Elle est très utilisée pour les boutons, les cellules de navigation, et les labels d'une ligne.

.bouton {
  height: 48px;
  line-height: 48px; /* line-height = height => texte centré verticalement */
  padding: 0 20px;
  text-align: center;
}

/* Limites : ne fonctionne que pour une seule ligne de texte.
   Si le texte passe sur deux lignes, le résultat est cassé. */

Technique 2 : padding symétrique. Plutôt que de fixer une hauteur et de jouer sur line-height, on peut laisser le contenu définir la hauteur de l'élément et appliquer un padding égal en haut et en bas. Le contenu se retrouve naturellement centré verticalement.

.carte {
  padding: 40px 30px; /* 40px en haut et en bas = centrage visuel vertical */
  /* Pas besoin de définir une hauteur : l'élément s'adapte au contenu */
}

/* Avantage : s'adapte à n'importe quelle quantité de contenu.
   Inconvénient : ne centre pas dans un conteneur à hauteur fixe. */

Technique 3 : position: absolute avec top: 50% et transform. Pour centrer un élément dans un conteneur à hauteur fixe, on peut combiner le positionnement absolu avec transform. C'est la technique classique pré-Flexbox pour un centrage précis.

.conteneur {
  position: relative;
  height: 300px;
}

.element-centre {
  position: absolute;
  top: 50%;                        /* Place le bord supérieur au milieu */
  left: 50%;                       /* Place le bord gauche au milieu */
  transform: translate(-50%, -50%); /* Décale de la moitié de sa propre taille */
}

/* Explication du translate :
   top: 50% positionne le COIN SUPÉRIEUR GAUCHE de l'élément au centre.
   L'élément dépasse donc vers le bas et la droite.
   transform: translate(-50%, -50%) le décale vers le haut et la gauche
   de la moitié de sa propre largeur et hauteur.
   Résultat : le CENTRE de l'élément coïncide avec le centre du conteneur. */

Technique 4 : display: table-cell avec vertical-align: middle. Une technique ancienne mais qui fonctionne dans des contextes spécifiques, notamment pour centrer verticalement dans un conteneur à hauteur fixe sans avoir recours au positionnement absolu.

.conteneur {
  display: table-cell;   /* Se comporte comme une cellule de tableau */
  vertical-align: middle; /* Centrage vertical natif des cellules de tableau */
  height: 200px;
  width: 400px;
}

Remarque : Flexbox et CSS Grid offrent des solutions bien plus simples et puissantes pour le centrage vertical. Ces techniques restent importantes à connaître pour comprendre du code existant et pour les rares cas où Flexbox n'est pas applicable.

Tableau de synthèse des techniques de centrage :

SituationTechnique recommandée

 

Centrer un bloc (div, article...)

margin: 0 auto + width défini

Centrer du texte ou des inline-block

text-align: center sur le parent

Une ligne de texte dans un bouton

line-height = height

Centrage vertical avec padding

padding symétrique haut/bas

Centrage horizontal + vertical précis

position: absolute + transform: translate(-50%, -50%)

Exercice 12 — La page d'accueil minimaliste

Objectif : construire une page d'accueil composée d'un héros centré (titre + sous-titre + bouton), d'une section de trois blocs côte à côte avec leur contenu centré, et d'un pied de page. L'exercice travaille les différentes techniques de centrage horizontal dans un contexte réaliste.

Consignes HTML :

  • Créez un fichier accueil.html.
  • Créez une <section> avec la classe hero contenant un <h1>, un <p> de sous-titre et un <a> avec la classe cta.
  • Créez une <section> avec la classe fonctionnalites contenant trois <div> avec la classe bloc. Chaque bloc contient un <div> avec la classe icone (une initiale ou un symbole), un <h3> et un <p> de description.
  • Créez un <footer> avec un <p> de copyright.
  • Liez un fichier accueil.css.

Consignes :

  • La section .hero occupe toute la largeur, a un fond coloré sombre. Son contenu (titre, sous-titre, bouton) doit être centré horizontalement. Le bouton .cta doit lui aussi être centré et avoir l'apparence d'un bouton.
  • La section .fonctionnalites doit avoir un contenu limité en largeur et centré sur la page. Les trois .bloc doivent s'afficher côte à côte et leur contenu interne doit être centré.
  • L'.icone de chaque bloc doit être centrée dans le bloc, avec une taille et un fond visibles.
  • Le <footer> a un fond sombre avec son texte centré.

Correction détaillée — Exercice 12

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="accueil.css">
  <title>Accueil</title>
</head>
<body>

  <section class="hero">
    <h1>Apprenez le CSS une bonne fois pour toutes</h1>
    <p class="sous-titre">Des formations claires, progressives et immédiatement applicables.</p>
    <a href="#" class="cta">Commencer maintenant</a>
  </section>

  <section class="fonctionnalites">
    <div class="bloc">
      <div class="icone">C</div>
      <h3>Cours structurés</h3>
      <p>Un parcours progressif du flux normal jusqu'aux techniques avancées de mise en page.</p>
    </div>
    <div class="bloc">
      <div class="icone">E</div>
      <h3>Exercices pratiques</h3>
      <p>Des exercices concrets après chaque notion pour ancrer les apprentissages durablement.</p>
    </div>
    <div class="bloc">
      <div class="icone">P</div>
      <h3>Projets réels</h3>
      <p>Des projets complets en fin de module pour mettre en pratique l'ensemble des notions.</p>
    </div>
  </section>

  <footer>
    <p>&copy; 2024 FormationCSS. Tous droits réservés.</p>
  </footer>

</body>
</html>

Correction CSS

/* accueil.css — Correction exercice 12 */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: Arial, sans-serif;
  color: #2c2c2c;
}

/* --- HERO --- */

.hero {
  background-color: #1A3A5C;
  padding: 80px 20px;
  /* text-align: center centre tout le contenu inline du hero :
     le h1, le p (leur texte) et le lien .cta (inline par défaut) */
  text-align: center;
}

.hero h1 {
  color: #ffffff;
  font-size: 32px;
  margin: 0 0 16px 0;
  /* max-width + margin: 0 auto limitent et centrent le titre
     pour qu'il ne soit pas trop large sur grand écran */
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 16px;
}

.sous-titre {
  color: #a8c4e0;
  font-size: 17px;
  margin: 0 0 32px 0;
}

/* .cta est un <a> : inline par défaut.
   display: inline-block lui permet d'avoir padding et border-radius
   tout en restant centré par le text-align: center du parent. */
.cta {
  display: inline-block;
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 14px 32px;
  text-decoration: none;
  font-weight: bold;
  font-size: 16px;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

.cta:hover {
  background-color: #3a84c4;
}

/* --- FONCTIONNALITES --- */

.fonctionnalites {
  /* max-width + margin: 0 auto centre le bloc de fonctionnalités.
     C'est la technique de centrage d'un élément bloc. */
  max-width: 900px;
  margin: 0 auto;
  padding: 60px 20px;
  /* font-size: 0 supprime les espaces blancs entre les .bloc inline-block */
  font-size: 0;
  text-align: center;
}

/* Chaque .bloc est inline-block pour être côte à côte.
   text-align: center centre leur contenu interne. */
.bloc {
  display: inline-block;
  vertical-align: top;
  width: 30%;
  margin: 0 1.5%;
  padding: 30px 20px;
  background-color: #f9f9f9;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  text-align: center; /* Centre le contenu à l'intérieur de chaque bloc */
  font-size: 15px;    /* Restaure la font-size annulée sur .fonctionnalites */
}

/* .icone est un <div> : bloc par défaut.
   margin: 0 auto le centre horizontalement dans .bloc.
   Il a une largeur fixe pour ne pas prendre toute la largeur. */
.icone {
  width: 60px;
  height: 60px;
  background-color: #2E6DA4;
  color: #ffffff;
  font-size: 26px;
  font-weight: bold;
  border-radius: 50%;
  /* line-height = height pour centrer verticalement le texte sur une ligne */
  line-height: 60px;
  text-align: center;
  margin: 0 auto 20px auto; /* margin auto gauche+droite = centrage horizontal du bloc */
}

.bloc h3 {
  color: #1A3A5C;
  font-size: 17px;
  margin: 0 0 10px 0;
}

.bloc p {
  color: #666666;
  font-size: 14px;
  line-height: 1.7;
  margin: 0;
}

/* --- FOOTER --- */

footer {
  background-color: #1A3A5C;
  color: #a8c4e0;
  text-align: center;
  padding: 24px;
  font-size: 13px;
}

footer p {
  margin: 0;
}

Explications et points de vigilance

text-align: center sur .hero : cette propriété s'applique au contenu inline du bloc, ce qui inclut le texte de h1, le texte de .sous-titre, et le lien .cta (qui est inline). En une seule déclaration sur le parent, tout le contenu se centre. C'est la technique pour centrer des éléments inline et inline-block.

margin: 0 auto sur .hero h1 : le titre a un max-width: 640px pour ne pas s'étirer sur toute la largeur sur les grands écrans. margin: 0 auto le centre horizontalement dans le hero. Ici on voit les deux techniques coexister : text-align: center pour le texte et les inline-block, margin: 0 auto pour le bloc lui-même.

display: inline-block sur .cta : le lien est inline par défaut. Sans changer son display, padding et border-radius ne s'appliqueraient pas correctement. inline-block lui donne l'apparence d'un bouton tout en restant centré par le text-align: center du parent.

margin: 0 auto sur .icone : l'icône est un <div> (bloc) avec une largeur fixe de 60px. Pour la centrer horizontalement dans .bloc, on lui applique margin: 0 auto. Le text-align: center du parent ne suffit pas ici car .icone est un bloc — text-align ne centre que les éléments inline.

Erreur fréquente : appliquer text-align: center sur un élément bloc en espérant le centrer dans son parent. text-align centre le contenu à l'intérieur, pas l'élément lui-même. Pour centrer un bloc, c'est margin: 0 auto qu'il faut utiliser.

Exercice 13 — La carte de profil centrée

Objectif : construire une carte de profil parfaitement centrée sur la page, horizontalement et verticalement. L'avatar, le nom et les informations doivent être centrés à l'intérieur de la carte. L'exercice travaille la combinaison des techniques de centrage vertical et horizontal dans un cas concret.

Consignes HTML :

  • Créez un fichier profil.html.
  • Créez une <div> avec la classe page (représente toute la page).
  • À l'intérieur, créez une <div> avec la classe carte-profil contenant :
    • Un <div> avec la classe avatar (simulera la photo de profil).
    • Un <h2> avec un nom.
    • Un <p> avec la classe role (ex : Développeuse Front-End).
    • Un <p> avec la classe bio (une phrase de description).
    • Un <a> avec la classe btn-contact et le texte Me contacter.
  • Liez un fichier profil.css.

Consignes :

  • La .page occupe toute la hauteur de la fenêtre du navigateur. La carte doit être centrée horizontalement et verticalement dans cette page. Trouvez la technique adaptée parmi celles vues dans ce module.
  • La .carte-profil a une largeur fixe, un fond blanc, du padding et une ombre. Tout son contenu interne est centré horizontalement.
  • L'.avatar est un cercle centré, avec un fond coloré et une taille de 100px par 100px.
  • Le .btn-contact ressemble à un bouton, centré dans la carte.

Correction détaillée — Exercice 13

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="profil.css">
  <title>Carte de profil</title>
</head>
<body>

  <div class="page">
    <div class="carte-profil">
      <div class="avatar"></div>
      <h2>Thomas Leroy</h2>
      <p class="role">Développeur Front-End</p>
      <p class="bio">Passionné par le CSS et l'accessibilité web, je crée des interfaces soignées et performantes.</p>
      <a href="#" class="btn-contact">Me contacter</a>
    </div>
  </div>

</body>
</html>

Correction CSS

/* profil.css — Correction exercice 13 */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
}

/* La page doit occuper toute la hauteur de la fenêtre.
   height: 100vh = 100% de la hauteur du viewport.
   position: relative est nécessaire pour que le centrage
   absolu de la carte fonctionne par rapport à elle. */
.page {
  position: relative;
  height: 100vh;
  background-color: #f0f4f8;
}

/* Centrage horizontal + vertical avec position absolute et transform.
   C'est la technique classique pré-Flexbox pour un centrage précis
   dans un conteneur à hauteur fixe (ici 100vh). */
.carte-profil {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);

  width: 320px;
  background-color: #ffffff;
  border-radius: 12px;
  padding: 40px 30px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);

  /* text-align: center centre tout le contenu inline de la carte :
     textes, et les éléments inline-block comme .btn-contact */
  text-align: center;
}

/* L'avatar est un <div> (bloc) avec une largeur fixe.
   margin: 0 auto le centre horizontalement dans la carte.
   border-radius: 50% le transforme en cercle. */
.avatar {
  width: 100px;
  height: 100px;
  background-color: #2E6DA4;
  border-radius: 50%;
  margin: 0 auto 20px auto;
}

.carte-profil h2 {
  font-size: 20px;
  color: #1A3A5C;
  margin-bottom: 6px;
}

.role {
  color: #2E6DA4;
  font-size: 14px;
  font-weight: bold;
  margin-bottom: 16px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.bio {
  font-size: 13px;
  color: #666666;
  line-height: 1.7;
  margin-bottom: 24px;
}

/* .btn-contact est un <a> : inline par défaut.
   display: inline-block lui donne l'apparence d'un bouton.
   Il est centré par le text-align: center de .carte-profil. */
.btn-contact {
  display: inline-block;
  background-color: #1A3A5C;
  color: #ffffff;
  padding: 10px 28px;
  text-decoration: none;
  border-radius: 4px;
  font-size: 14px;
  font-weight: bold;
  transition: background-color 0.2s ease;
}

.btn-contact:hover {
  background-color: #2E6DA4;
}

Explications et points de vigilance

position: absolute + transform: translate(-50%, -50%) : c'est la technique de centrage précis dans un conteneur à hauteur connue. top: 50% et left: 50% placent le coin supérieur gauche de la carte au centre de la page. Mais l'élément dépasse alors vers le bas et la droite. transform: translate(-50%, -50%) le décale vers le haut et la gauche de la moitié de sa propre taille. Au final, le centre de la carte coïncide avec le centre de la page.

height: 100vh sur .page : vh signifie "viewport height" — 1vh équivaut à 1% de la hauteur de la fenêtre du navigateur. Sans cette déclaration, .page n'aurait que la hauteur de son contenu (la carte), et le centrage vertical ne fonctionnerait pas : il n'y aurait pas d'espace vertical dans lequel centrer.

Deux techniques de centrage horizontal coexistent : text-align: center sur .carte-profil centre son contenu inline (textes, .btn-contact). margin: 0 auto sur .avatar centre l'avatar lui-même dans la carte, car c'est un élément bloc avec une largeur fixe. Ces deux techniques ne sont pas interchangeables et s'appliquent à des situations différentes.

Erreur fréquente : oublier position: relative sur .page. Sans cette déclaration, la carte se positionne par rapport au <body> ou au premier ancêtre positionné — généralement le même résultat dans cet exercice, mais dans une vraie page avec d'autres conteneurs, le résultat pourrait être totalement inattendu.

7.1 Origine et objectifs de Flexbox

Flexbox — dont le nom complet est Flexible Box Layout Module — est une technologie de mise en page CSS introduite pour répondre à des limitations fondamentales du modèle de boîte traditionnel. Avant d'apprendre à l'utiliser, il est essentiel de comprendre pourquoi elle a été créée et quels problèmes concrets elle résout. Cela vous permettra de saisir sa logique profonde plutôt que d'en mémoriser les propriétés de manière mécanique.

7.1.1 Problèmes résolus par Flexbox

Pendant toute la première décennie du web moderne, les développeurs ont dû composer avec des outils de mise en page qui n'avaient pas été conçus pour cet usage. float avait été imaginé pour enrouler du texte autour d'images. display: inline-block était un hybride utile mais imprécis. Les tableaux HTML étaient détournés pour créer des grilles. Chacune de ces approches fonctionnait, mais toutes généraient des problèmes récurrents que vous avez déjà rencontrés dans les chapitres précédents.

Problème 1 : aligner des éléments verticalement dans un conteneur. C'était l'un des problèmes les plus frustrants du CSS classique. Centrer verticalement un élément dans un conteneur dont la hauteur n'était pas connue à l'avance nécessitait des acrobaties : position: absolute combiné à transform: translate, ou display: table-cell avec vertical-align: middle. Aucune de ces solutions n'était intuitive ni robuste.

/* Avant Flexbox : centrage vertical laborieux */
.conteneur {
  position: relative;
  height: 300px;
}

.element {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* Fonctionne, mais sort l'élément du flux et nécessite
     de connaître le contexte de positionnement exact */
}
/* Avec Flexbox : centrage vertical en trois propriétés */
.conteneur {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 300px;
}
/* L'élément enfant est centré horizontalement et verticalement.
   Il reste dans le flux. Aucune connaissance de sa taille n'est requise. */

Problème 2 : distribuer l'espace disponible entre des éléments. Avec float ou inline-block, répartir des éléments uniformément dans un conteneur demandait des calculs manuels de pourcentages, des gouttières gérées à la main, et un clearfix pour éviter l'effondrement du conteneur.

/* Avant Flexbox : trois colonnes égales avec float */
.colonne {
  float: left;
  width: 33.333%;      /* Calcul manuel */
  padding: 0 10px;     /* Gouttière manuelle */
  box-sizing: border-box;
}

/* Il faut aussi gérer le clearfix sur le parent,
   et si le nombre de colonnes change, recalculer tout */
/* Avec Flexbox : trois colonnes égales sans calcul */
.conteneur {
  display: flex;
  gap: 20px; /* Gouttière gérée automatiquement */
}

.colonne {
  flex: 1; /* Chaque colonne prend une part égale de l'espace disponible */
  /* Si on ajoute une quatrième colonne, les quatre se partagent l'espace
     automatiquement, sans toucher aux autres règles */
}

Problème 3 : faire en sorte que des éléments aient tous la même hauteur. Avec float, chaque colonne avait sa propre hauteur déterminée par son contenu. Si la colonne de gauche avait plus de texte que celle de droite, elles n'étaient pas de la même hauteur, ce qui cassait l'harmonie visuelle. On utilisait alors des hacks comme des min-height fixes ou du JavaScript pour équilibrer les hauteurs.

/* Avant Flexbox : colonnes inégales, problème courant */
.col-gauche {
  float: left;
  width: 50%;
  /* Hauteur déterminée par le contenu : peut être 200px ou 500px */
}

.col-droite {
  float: right;
  width: 50%;
  /* Hauteur différente si le contenu est plus court ou plus long */
}
/* Avec Flexbox : hauteur égale automatique */
.conteneur {
  display: flex;
}

.col-gauche,
.col-droite {
  flex: 1;
  /* Les deux colonnes ont automatiquement la même hauteur :
     celle de la plus haute des deux. Aucun calcul, aucun JS. */
}

Problème 4 : inverser ou réordonner visuellement des éléments sans toucher au HTML. Avant Flexbox, l'ordre visuel des éléments était strictement lié à leur ordre dans le code HTML. Changer l'ordre d'affichage impliquait de modifier le HTML, ce qui pouvait poser des problèmes d'accessibilité ou de logique de code.

/* Avec Flexbox : réordonner visuellement sans toucher au HTML */
.conteneur {
  display: flex;
  flex-direction: row-reverse; /* Inverse l'ordre d'affichage */
}

/* Ou en ciblant un élément spécifique */
.element-en-premier {
  order: -1; /* S'affiche avant tous les autres, quelle que soit sa position dans le HTML */
}

7.1.2 Cas d'usage typiques

Flexbox excelle dans certains contextes précis. Connaître ces cas d'usage vous aidera à identifier immédiatement quand y recourir plutôt qu'à une autre technique.

Navigation horizontale. C'est sans doute l'usage le plus répandu de Flexbox. Une liste de liens à distribuer dans une barre de navigation, avec parfois un logo à gauche et des liens à droite, ou des liens répartis équitablement sur toute la largeur.

.navbar {
  display: flex;
  justify-content: space-between; /* Logo à gauche, liens à droite */
  align-items: center;            /* Alignement vertical centré */
  padding: 0 30px;
  background-color: #1A3A5C;
  height: 60px;
}

Centrage d'un élément dans une zone. Que ce soit une carte centrée dans une page, une icône centrée dans un bouton, ou un texte centré dans une bannière — Flexbox est la solution la plus propre et la plus lisible.

.hero {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 400px;
}
/* L'enfant unique du hero sera centré horizontalement et verticalement */

Mise en page en colonnes de même hauteur. Des cartes de contenu, des blocs de fonctionnalités, des colonnes d'article — tous ces éléments bénéficient de l'égalisation automatique des hauteurs que Flexbox offre sans effort supplémentaire.

.grille-cartes {
  display: flex;
  gap: 20px;
}

.carte {
  flex: 1; /* Toutes les cartes ont la même largeur ET la même hauteur */
  padding: 20px;
  background-color: #ffffff;
  border: 1px solid #e0e0e0;
}

Alignement d'un groupe d'éléments avec espacement automatique. Placer un bouton tout à droite d'une barre d'outils, aligner des icônes dans un footer, séparer des éléments de formulaire — Flexbox gère l'espace entre les éléments de manière déclarative.

.barre-outils {
  display: flex;
  align-items: center;
  gap: 12px;
}

.barre-outils .action-principale {
  margin-left: auto; /* Pousse cet élément et tout ce qui suit vers la droite */
}

Formulaires et groupes d'inputs. Aligner un label à côté d'un champ de saisie, placer un bouton de validation à droite d'un input, créer un champ de recherche avec un bouton intégré — Flexbox simplifie considérablement ces mises en page.

.champ-recherche {
  display: flex;
}

.champ-recherche input {
  flex: 1;      /* L'input prend tout l'espace disponible */
  padding: 10px;
  border: 1px solid #cccccc;
  border-right: none;
}

.champ-recherche button {
  padding: 10px 20px;
  background-color: #2E6DA4;
  color: white;
  border: none;
  cursor: pointer;
}

Flexbox vs CSS Grid : Flexbox est conçu pour la mise en page unidimensionnelle — une ligne ou une colonne à la fois. CSS Grid est conçu pour la mise en page bidimensionnelle — lignes et colonnes simultanément. En pratique, on utilise Flexbox pour les composants (navigations, cartes, formulaires) et Grid pour les grandes structures de page. Les deux coexistent parfaitement et se complètent.

7.2 Conteneur flex et éléments flex

Flexbox repose sur une relation entre deux types d'acteurs : le conteneur flex et les éléments flex. Comprendre cette distinction est la première chose à maîtriser avant d'aborder les propriétés. Tout le système de Flexbox s'organise autour de cette dualité : certaines propriétés s'appliquent au conteneur, d'autres aux éléments. Les confondre est la source d'erreurs la plus fréquente chez les débutants.

7.2.1 Définition d'un conteneur flex

Un conteneur flex est simplement un élément HTML auquel on applique la déclaration display: flex. Cette unique instruction transforme radicalement le comportement de l'élément et de ses enfants directs.

.conteneur {
  display: flex;
}

C'est tout. Une seule propriété suffit à activer Flexbox. À partir de ce moment, le navigateur applique automatiquement un ensemble de comportements par défaut que vous allez maintenant découvrir.

Ce qui change sur le conteneur lui-même : Le conteneur reste un élément bloc dans le flux du document — il occupe toujours toute la largeur disponible et force un saut de ligne avant et après lui. Si vous voulez qu'un conteneur flex se comporte comme un élément inline, vous pouvez utiliser display: inline-flex.

.conteneur-bloc {
  display: flex;         /* Reste un bloc dans le flux */
}

.conteneur-inline {
  display: inline-flex;  /* Se comporte comme inline-block dans le flux,
                            mais ses enfants sont gérés par Flexbox */
}

Ce qui change sur les enfants directs — automatiquement, sans rien écrire :

Dès que display: flex est appliqué au parent, ses enfants directs deviennent des éléments flex et acquièrent de nouveaux comportements automatiques :

  • Ils se placent côte à côte sur une ligne horizontale, de gauche à droite, par défaut.
  • Ils ne passent pas à la ligne même si l'espace vient à manquer (comportement modifiable).
  • Ils s'alignent automatiquement sur la même hauteur — celle du plus grand d'entre eux.
  • Ils perdent leur comportement block ou inline d'origine : un span et un div se comportent de la même façon à l'intérieur d'un conteneur flex.

Voici un exemple concret pour observer ces comportements par défaut :

<!-- exemple-flex-base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="exemple-flex-base.css">
  <title>Conteneur flex de base</title>
</head>
<body>

  <h2>Sans Flexbox (comportement par défaut)</h2>
  <div class="sans-flex">
    <div class="boite">Boite 1</div>
    <div class="boite">Boite 2</div>
    <div class="boite">Boite 3</div>
  </div>

  <h2>Avec Flexbox (display: flex)</h2>
  <div class="avec-flex">
    <div class="boite">Boite 1</div>
    <div class="boite">Boite 2 avec plus de texte</div>
    <div class="boite">Boite 3</div>
  </div>

</body>
</html>
/* exemple-flex-base.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

h2 {
  color: #1A3A5C;
  margin-bottom: 12px;
}

/* Sans Flexbox : les div s'empilent verticalement (comportement bloc) */
.sans-flex {
  background-color: #e0e0e0;
  padding: 10px;
  margin-bottom: 40px;
}

/* Avec Flexbox : les div se placent côte à côte automatiquement */
.avec-flex {
  display: flex;           /* Une seule ligne active tout Flexbox */
  background-color: #e0e0e0;
  padding: 10px;
}

.boite {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 20px;
  margin: 5px;
  font-size: 14px;
}

Observation clé : dans .avec-flex, les trois boîtes sont côte à côte ET ont toutes la même hauteur, même si "Boite 2" contient plus de texte. Sans Flexbox, chaque boîte aurait sa propre hauteur déterminée par son contenu. C'est l'un des premiers cadeaux de Flexbox.

7.2.2 Identification des éléments flex

Les éléments flex sont les enfants directs d'un conteneur flex — ni plus, ni moins. Il est crucial de comprendre cette notion de directness : seuls les enfants immédiats sont concernés. Les petits-enfants, arrière-petits-enfants et autres descendants ne sont pas des éléments flex, sauf si leur propre parent est lui-même un conteneur flex.

<!-- identification-elements-flex.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="identification-elements-flex.css">
  <title>Identification des éléments flex</title>
</head>
<body>

  <div class="conteneur-flex">

    <!-- ÉLÉMENT FLEX : enfant direct du conteneur -->
    <div class="enfant-direct">
      Enfant direct (élément flex)

      <!-- PAS un élément flex : c'est un petit-enfant du conteneur -->
      <p class="petit-enfant">
        Petit-enfant (PAS un élément flex).
        Il est géré par le flux normal à l'intérieur de son parent.
      </p>
    </div>

    <!-- ÉLÉMENT FLEX : enfant direct du conteneur -->
    <div class="enfant-direct">
      Enfant direct (élément flex)
    </div>

    <!-- ÉLÉMENT FLEX : enfant direct, même si c'est un span (inline par défaut) -->
    <span class="enfant-direct">
      Span enfant direct (élément flex).
      Même les éléments inline deviennent flex ici.
    </span>

  </div>

</body>
</html>
/* identification-elements-flex.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

.conteneur-flex {
  display: flex;
  background-color: #1A3A5C;
  padding: 10px;
  gap: 10px;
}

/* Les enfants directs deviennent automatiquement des éléments flex */
.enfant-direct {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 16px;
  font-size: 14px;
  font-weight: bold;
}

/* Le petit-enfant n'est PAS un élément flex : il suit le flux normal
   à l'intérieur de son parent .enfant-direct */
.petit-enfant {
  background-color: #a8c4e0;
  color: #1A3A5C;
  padding: 8px;
  margin-top: 8px;
  font-weight: normal;
  font-size: 13px;
}

Un élément flex perd son type d'affichage d'origine. Avant de devenir un élément flex, chaque enfant avait un display propre : block pour un div, inline pour un span, list-item pour un li... Dès qu'il devient élément flex, ces comportements sont remplacés par le comportement flex. En particulier :

  • Un span (inline) peut maintenant avoir une largeur, une hauteur, des marges verticales — sans avoir à écrire display: block ou display: inline-block.
  • Un div (block) ne force plus de saut de ligne — il se place côte à côte avec ses frères.
<!-- flex-inline-vs-block.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-inline-vs-block.css">
  <title>Flex : inline vs block</title>
</head>
<body>

  <h2>Sans Flexbox</h2>
  <div class="sans-flex">
    <div>div (bloc)</div>
    <span>span (inline)</span>
    <span>span (inline)</span>
  </div>

  <h2>Avec Flexbox</h2>
  <div class="avec-flex">
    <div>div (maintenant flex)</div>
    <span>span (maintenant flex)</span>
    <span>span (maintenant flex)</span>
  </div>

</body>
</html>
/* flex-inline-vs-block.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

h2 {
  color: #1A3A5C;
  margin-bottom: 10px;
}

.sans-flex,
.avec-flex {
  background-color: #e8e8e8;
  padding: 10px;
  margin-bottom: 40px;
}

.avec-flex {
  display: flex;
  gap: 10px;
}

/* On applique les mêmes styles à tous les enfants dans les deux cas */
.sans-flex div,
.sans-flex span,
.avec-flex div,
.avec-flex span {
  background-color: #C0392B;
  color: white;
  padding: 16px 20px;
  font-size: 14px;
  /* On tente de donner une hauteur : ignorée sur span sans flex */
  height: 80px;
}

Observation : sans Flexbox, height: 80px est ignoré sur les span (éléments inline). Avec Flexbox, la hauteur s'applique à tous les enfants, qu'ils soient div ou span, car leur type d'affichage d'origine est remplacé par le comportement flex.

Les éléments flex peuvent eux-mêmes devenir des conteneurs flex. C'est l'un des points les plus puissants de Flexbox : un élément flex peut simultanément être un conteneur flex pour ses propres enfants. On appelle cela le flex imbriqué.

<!-- flex-imbrique.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-imbrique.css">
  <title>Flex imbriqué</title>
</head>
<body>

  <div class="conteneur-principal">

    <div class="carte">
      <div class="carte-header">
        <span class="titre">Projet Alpha</span>
        <span class="statut">Actif</span>
      </div>
      <p>Description du projet Alpha et de ses objectifs principaux.</p>
    </div>

    <div class="carte">
      <div class="carte-header">
        <span class="titre">Projet Beta</span>
        <span class="statut">En pause</span>
      </div>
      <p>Description du projet Beta et de son état d'avancement actuel.</p>
    </div>

  </div>

</body>
</html>
/* flex-imbrique.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

/* Conteneur principal : flex pour placer les cartes côte à côte */
.conteneur-principal {
  display: flex;
  gap: 20px;
}

/* .carte est un élément flex (enfant de .conteneur-principal)
   ET un conteneur flex pour son propre contenu */
.carte {
  flex: 1;
  background-color: #ffffff;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  padding: 20px;
}

/* .carte-header est un conteneur flex pour placer titre et statut
   sur la même ligne avec de l'espace entre eux */
.carte-header {
  display: flex;                  /* Conteneur flex imbriqué */
  justify-content: space-between; /* Titre à gauche, statut à droite */
  align-items: center;
  margin-bottom: 12px;
}

.titre {
  font-weight: bold;
  color: #1A3A5C;
  font-size: 16px;
}

.statut {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 20px;
  background-color: #2E6DA4;
  color: white;
}

.carte p {
  font-size: 14px;
  color: #666666;
  line-height: 1.6;
  margin: 0;
}

À retenir : .conteneur-principal est un conteneur flex. .carte est à la fois un élément flex (géré par .conteneur-principal) ET un conteneur flex ordinaire pour ses enfants div et p. .carte-header est lui-même un conteneur flex. Il n'y a aucune limite à l'imbrication des conteneurs flex.

Exercice 16 — La grille de profils

Objectif : construire une rangée de cartes de profil côte à côte en utilisant display: flex sur le conteneur. L'exercice travaille l'activation d'un conteneur flex et l'observation des comportements automatiques des éléments flex : alignement côte à côte, égalisation des hauteurs, perte du comportement bloc/inline d'origine.

Consignes HTML :

  • Créez un fichier profils.html.
  • Créez une <div> avec la classe grille contenant quatre <div> avec la classe profil.
  • Chaque .profil contient :
    • Un <div> avec la classe avatar (initiale ou lettre au choix).
    • Un <h3> avec un prénom.
    • Un <p> avec la classe metier (un métier fictif).
    • Un <p> avec la classe bio. Variez volontairement la longueur du texte entre les quatre profils pour observer l'égalisation des hauteurs.
  • Liez un fichier profils.css.

Consignes :

  • Activez Flexbox sur .grille pour que les profils s'affichent côte à côte.
  • Chaque .profil a un fond blanc, une bordure, du padding et des coins arrondis. Son contenu est centré horizontalement.
  • L'.avatar est un cercle de 70px de diamètre, centré dans la carte, avec une initiale en grand.
  • Observez que toutes les cartes ont la même hauteur malgré des contenus différents.
  • Ajoutez une classe différente sur chaque div profil et jouez avec flex et indiquez des valeurs différentes sur chaque div profil(exemple flex:1, flex: 1.5)

Correction détaillée — Exercice 16

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
  <title>Grille de profils</title>
</head>
<body>

  <h1>Notre équipe</h1>

  <div class="grille">

    <div class="profil un">
      <div class="avatar">A</div>
      <h3>Alice Martin</h3>
      <p class="metier">Designer UI</p>
      <p class="bio">Spécialisée en design d'interfaces accessibles et en systèmes de design.Spécialisée en design d'interfaces accessibles et en systèmes de design.Spécialisée en design d'interfaces accessibles et en systèmes de design.Spécialisée en design d'interfaces accessibles et en systèmes de design.Spécialisée en design d'interfaces accessibles et en systèmes de design.</p>
    </div>

    <div class="profil deux">
      <div class="avatar">B</div>
      <h3>Baptiste Roux</h3>
      <p class="metier">Développeur Front-End</p>
      <p class="bio">Expert CSS et JavaScript. Passionné par les animations et les performances web. Contributeur open source depuis 2018.</p>
    </div>

    <div class="profil trois">
      <div class="avatar">C</div>
      <h3>Clara Dubois</h3>
      <p class="metier">Chef de projet</p>
      <p class="bio">Coordinatrice d'équipes techniques et garante de la qualité des livrables.</p>
    </div>

    <div class="profil quatre">
      <div class="avatar">D</div>
      <h3>David Lefort</h3>
      <p class="metier">Développeur Back-End</p>
      <p class="bio">Architecte de bases de données et d'API REST. Maîtrise Python, Node.js et PostgreSQL. Dix ans d'expérience en développement serveur pour des projets à forte charge.</p>
    </div>

  </div>

</body>
</html>

Correction CSS

/* profils.css — Correction exercice 16 */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 40px;
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
  color: #2c2c2c;
}

h1 {
  color: #1A3A5C;
  margin-bottom: 30px;
}

/*
  display: flex est la seule instruction qui place les quatre profils
  côte à côte ET leur donne automatiquement la même hauteur.
  gap gère l'espace entre les cartes sans calcul de marges manuelles.
*/
.grille {
  display: flex;
  gap: 20px;
}

.profil {
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 10px;
  padding: 24px 20px;
  text-align: center;
}

.un {
  flex: 1.5;
}

.deux {
  flex: 2;
}

.trois {
  flex: 3;
}

.quatre {
  flex: 1;
}

/*
  .avatar est un <div> (bloc).
  margin: 0 auto le centre horizontalement dans .profil.
  border-radius: 50% le transforme en cercle.
  line-height = height pour centrer l'initiale verticalement.
*/
.avatar {
  width: 70px;
  height: 70px;
  border-radius: 50%;
  background-color: #2E6DA4;
  color: #ffffff;
  font-size: 28px;
  font-weight: bold;
  line-height: 70px;
  text-align: center;
  margin: 0 auto 16px auto;
}

.profil h3 {
  color: #1A3A5C;
  font-size: 16px;
  margin: 0 0 6px 0;
}

.metier {
  color: #2E6DA4;
  font-size: 13px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin: 0 0 12px 0;
}

.bio {
  font-size: 13px;
  color: #666666;
  line-height: 1.7;
  margin: 0;
}

Explications et points de vigilance

display: flex sur .grille : cette unique déclaration remplace ce qui aurait nécessité float + clearfix + inline-block + font-size: 0 avec les techniques précédentes. Les quatre cartes se placent côte à côte immédiatement.

Égalisation automatique des hauteurs : observez que la carte de David, qui contient beaucoup plus de texte, est plus haute — et toutes les autres cartes s'étendent automatiquement à cette même hauteur. Avec float ou inline-block, chaque carte aurait eu sa propre hauteur. Ici, Flexbox gère cela sans aucune propriété supplémentaire.

Erreur fréquente : appliquer display: flex sur .profil au lieu de .grille. Flexbox agit sur les enfants directs du conteneur — si on l'applique sur la carte elle-même, c'est le contenu de la carte (avatar, titre, bio) qui devient flex, pas les cartes entre elles.

7.3 Axes de Flexbox

Flexbox organise ses éléments le long de deux axes perpendiculaires. Comprendre ces axes est absolument indispensable avant d'aborder les propriétés d'alignement : toutes les propriétés de Flexbox font référence à l'un ou l'autre de ces axes. Sans cette compréhension, les propriétés comme justify-content et align-items semblent arbitraires. Avec elle, elles deviennent parfaitement logiques.

7.3.1 Axe principal

L'axe principal est la direction dans laquelle les éléments flex se placent les uns à la suite des autres. Par défaut, cet axe est horizontal, de gauche à droite. C'est pourquoi, sans rien écrire d'autre que display: flex, les éléments se placent en ligne horizontale.

L'axe principal est défini par la propriété flex-direction, que vous étudierez en détail dans la section 7.4. Pour l'instant, retenez que l'axe principal peut être horizontal (gauche à droite, ou droite à gauche) ou vertical (haut en bas, ou bas en haut).

<!-- axes-flexbox.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="axes-flexbox.css">
  <title>Axes Flexbox</title>
</head>
<body>

  <h2>Axe principal horizontal (par défaut : flex-direction: row)</h2>
  <div class="conteneur axe-horizontal">
    <div class="boite">1</div>
    <div class="boite">2</div>
    <div class="boite">3</div>
  </div>

  <h2>Axe principal vertical (flex-direction: column)</h2>
  <div class="conteneur axe-vertical">
    <div class="boite">1</div>
    <div class="boite">2</div>
    <div class="boite">3</div>
  </div>

</body>
</html>
/* axes-flexbox.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

h2 {
  color: #1A3A5C;
  margin-bottom: 12px;
  font-size: 16px;
}

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 12px;
  margin-bottom: 40px;
  gap: 10px;
}

/* Axe principal horizontal : les boîtes vont de gauche à droite */
.axe-horizontal {
  flex-direction: row; /* Valeur par défaut, peut être omise */
}

/* Axe principal vertical : les boîtes vont de haut en bas */
.axe-vertical {
  flex-direction: column;
  width: 200px; /* Limite la largeur pour rendre le résultat visible */
}

.boite {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 20px 28px;
  font-size: 18px;
  font-weight: bold;
  text-align: center;
  border-radius: 4px;
}

Point clé : l'axe principal détermine la direction dans laquelle les éléments flex se succèdent. Toutes les propriétés de distribution de l'espace (justify-content) agissent le long de cet axe.

7.3.2 Axe secondaire

L'axe secondaire — aussi appelé axe transversal ou cross axis — est perpendiculaire à l'axe principal. Quand l'axe principal est horizontal, l'axe secondaire est vertical, et vice versa.

L'axe secondaire contrôle comment les éléments sont alignés dans la direction perpendiculaire à leur sens de défilement. En pratique, c'est lui qui permet de centrer verticalement des éléments dans un conteneur horizontal — l'un des cas d'usage les plus fréquents de Flexbox.

<!-- axe-secondaire.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="axe-secondaire.css">
  <title>Axe secondaire Flexbox</title>
</head>
<body>

  <h2>Axe secondaire : alignement en haut (align-items: flex-start)</h2>
  <div class="conteneur align-start">
    <div class="boite grande">Grande boite</div>
    <div class="boite">Petite</div>
    <div class="boite">Petite</div>
  </div>

  <h2>Axe secondaire : alignement au centre (align-items: center)</h2>
  <div class="conteneur align-center">
    <div class="boite grande">Grande boite</div>
    <div class="boite">Petite</div>
    <div class="boite">Petite</div>
  </div>

  <h2>Axe secondaire : étirement complet (align-items: stretch — par défaut)</h2>
  <div class="conteneur align-stretch">
    <div class="boite grande">Grande boite</div>
    <div class="boite">Petite</div>
    <div class="boite">Petite</div>
  </div>

</body>
</html>
/* axe-secondaire.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

h2 {
  color: #1A3A5C;
  margin-bottom: 12px;
  font-size: 16px;
}

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 12px;
  margin-bottom: 40px;
  gap: 10px;
  height: 140px; /* Hauteur fixe pour rendre l'axe secondaire visible */
}

.align-start   { align-items: flex-start; }
.align-center  { align-items: center; }
.align-stretch { align-items: stretch; } /* Valeur par défaut */

.boite {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 16px 24px;
  font-size: 14px;
  font-weight: bold;
  text-align: center;
  border-radius: 4px;
}

/* La grande boite a une hauteur explicite pour forcer la différence */
.grande {
  height: 100px;
  background-color: #1A3A5C;
  line-height: 68px; /* Centrage vertical du texte dans la grande boite */
}

Le comportement stretch par défaut : sans aucune instruction d'alignement, les éléments flex s'étirent automatiquement pour atteindre la hauteur du conteneur (ou la hauteur du plus grand élément). C'est pourquoi, dans l'exercice 16, toutes les cartes de profil avaient la même hauteur sans qu'on ait eu besoin de l'écrire : c'est align-items: stretch qui s'applique par défaut.

Récapitulatif des deux axes selon flex-direction :

flex-direction Axe principal Axe secondaire
row (défaut) Horizontal (gauche → droite) Vertical (haut → bas)
row-reverse Horizontal (droite → gauche) Vertical (haut → bas)
column Vertical (haut → bas) Horizontal (gauche → droite)
column-reverse Vertical (bas → haut) Horizontal (gauche → droite)

À retenir absolument : quand flex-direction change, les axes s'échangent. Ce qui était l'axe principal devient secondaire, et inversement. Cela signifie que justify-content et align-items échangent aussi leur rôle. C'est une source fréquente de confusion — gardez ce tableau en tête.

Exercice 17 — Observer les deux axes

Objectif : manipuler directement les deux axes de Flexbox en faisant varier flex-direction et en observant comment les éléments se réorganisent. L'exercice ancre visuellement la notion d'axe principal et d'axe secondaire avant d'aborder les propriétés d'alignement.

Consignes HTML :

  • Créez un fichier axes.html.
  • Créez quatre sections identiques, chacune avec un <h2> descriptif et un <div class="conteneur"> contenant cinq <div class="item"> numérotés de 1 à 5.
  • Donnez à chaque conteneur une classe supplémentaire : row, row-reverse, column, column-reverse.
  • Liez un fichier axes.css.

Consignes :

  • Tous les .conteneur ont display: flex, un fond coloré, du padding et une hauteur fixe de 200px.
  • Chaque classe supplémentaire applique une valeur différente de flex-direction.
  • Les .item sont des blocs colorés, numérotés, avec du padding. Leur contenu est centré.
  • Observez dans chaque cas : dans quelle direction les éléments se succèdent-ils ? Quel est l'axe principal ? Quel est l'axe secondaire ? Comment les éléments occupent-ils la hauteur disponible ?

Correction détaillée — Exercice 17

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="axes.css">
  <title>Les deux axes de Flexbox</title>
</head>
<body>

  <h1>Les axes de Flexbox</h1>

  <h2>flex-direction: row (défaut) — axe principal : horizontal →</h2>
  <div class="conteneur row">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
  </div>

  <h2>flex-direction: row-reverse — axe principal : horizontal ←</h2>
  <div class="conteneur row-reverse">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
  </div>

  <h2>flex-direction: column — axe principal : vertical ↓</h2>
  <div class="conteneur column">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
  </div>

  <h2>flex-direction: column-reverse — axe principal : vertical ↑</h2>
  <div class="conteneur column-reverse">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
  </div>

</body>
</html>

Correction CSS

/* axes.css — Correction exercice 17 */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 30px;
  font-family: Arial, sans-serif;
  background-color: #f4f4f4;
  color: #2c2c2c;
}

h1 {
  color: #1A3A5C;
  margin-bottom: 30px;
}

h2 {
  color: #2E6DA4;
  font-size: 15px;
  margin-bottom: 10px;
  margin-top: 40px;
}

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 12px;
  gap: 10px;
}

/* Chaque classe applique une direction différente */
.row         { flex-direction: row; }
.row-reverse { flex-direction: row-reverse; }
.column      { flex-direction: column; }
.column-reverse { flex-direction: column-reverse; }

.item {
  background-color: #2E6DA4;
  color: #ffffff;
  font-size: 20px;
  font-weight: bold;
  padding: 10px 20px;
  border-radius: 4px;
  /*
    En row/row-reverse : les items s'étirent sur toute la hauteur (stretch par défaut).
    En column/column-reverse : les items s'étirent sur toute la largeur (stretch par défaut).
  */
  display: flex;
  align-items: center;
  justify-content: center;
}

Explications et points de vigilance

row et row-reverse : l'axe principal est horizontal. Les items se placent de gauche à droite (row) ou de droite à gauche (row-reverse). L'axe secondaire est vertical — les items s'étirent sur toute la hauteur du conteneur grâce à stretch.

column et column-reverse : l'axe principal devient vertical. Les items se placent de haut en bas (column) ou de bas en haut (column-reverse). L'axe secondaire devient horizontal — les items s'étirent sur toute la largeur du conteneur.

L'échange des axes : observez attentivement que dans row, les items occupent toute la hauteur (axe secondaire = vertical, stretch). Dans column, les items occupent toute la largeur (axe secondaire = horizontal, stretch). C'est la même propriété par défaut (align-items: stretch) qui produit ces deux comportements différents selon la direction.

Erreur fréquente : croire que justify-content aligne toujours horizontalement et align-items toujours verticalement. C'est faux. justify-content agit sur l'axe principal — qui peut être vertical si flex-direction: column. align-items agit sur l'axe secondaire — qui peut être horizontal dans ce même cas. Les propriétés suivent les axes, pas les directions absolues.

7.4 Propriétés du conteneur

Les propriétés du conteneur flex sont celles qui s'appliquent sur l'élément parent — celui sur lequel vous écrivez display: flex. Elles contrôlent la direction des éléments, leur comportement face au manque d'espace, et leur alignement sur les deux axes. Ces propriétés sont le cœur de Flexbox et celles que vous utiliserez dans la quasi-totalité de vos mises en page.

7.4.1 Direction des éléments

La propriété flex-direction définit l'axe principal, c'est-à-dire la direction dans laquelle les éléments flex se succèdent. Vous avez vu ses quatre valeurs dans la section 7.3 — voici maintenant comment les utiliser en contexte réel.

<!-- flex-direction.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-direction.css">
  <title>flex-direction</title>
</head>
<body>

  <h2>row — navigation horizontale classique</h2>
  <nav class="nav-horizontale">
    <a href="#">Accueil</a>
    <a href="#">Articles</a>
    <a href="#">Portfolio</a>
    <a href="#">Contact</a>
  </nav>

  <h2>column — menu vertical</h2>
  <nav class="nav-verticale">
    <a href="#">Accueil</a>
    <a href="#">Articles</a>
    <a href="#">Portfolio</a>
    <a href="#">Contact</a>
  </nav>

  <h2>row-reverse — éléments dans l'ordre inversé</h2>
  <div class="liste-inversee">
    <div class="tag">HTML</div>
    <div class="tag">CSS</div>
    <div class="tag">JavaScript</div>
  </div>

</body>
</html>
/* flex-direction.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

h2 { color: #1A3A5C; margin: 30px 0 10px; font-size: 15px; }

.nav-horizontale {
  display: flex;
  flex-direction: row;
  gap: 4px;
  background-color: #1A3A5C;
  padding: 0 16px;
}

.nav-horizontale a {
  display: block;
  padding: 14px 18px;
  color: #ffffff;
  text-decoration: none;
}

.nav-horizontale a:hover { background-color: #2E6DA4; }

.nav-verticale {
  display: flex;
  flex-direction: column;
  width: 200px;
  background-color: #1A3A5C;
}

.nav-verticale a {
  padding: 12px 20px;
  color: #ffffff;
  text-decoration: none;
  border-bottom: 1px solid #2E6DA4;
}

.nav-verticale a:hover { background-color: #2E6DA4; }

.liste-inversee {
  display: flex;
  flex-direction: row-reverse;
  gap: 8px;
}

.tag {
  background-color: #2E6DA4;
  color: white;
  padding: 6px 16px;
  border-radius: 20px;
  font-size: 13px;
}

Cas d'usage de row-reverse et column-reverse : l'inversion de l'ordre visuel est utile pour l'accessibilité (garder un ordre HTML logique pour les lecteurs d'écran tout en inversant l'affichage) et pour les interfaces chat où le dernier message doit apparaître en bas.

7.4.2 Gestion du retour à la ligne

Par défaut, les éléments flex ne passent jamais à la ligne. Si le conteneur est trop étroit, ils se compriment pour tenir sur une seule ligne. La propriété flex-wrap contrôle ce comportement.

.conteneur { flex-wrap: nowrap; }       /* Défaut : tout sur une ligne */
.conteneur { flex-wrap: wrap; }         /* Passage à la ligne si besoin */
.conteneur { flex-wrap: wrap-reverse; } /* Passage à la ligne vers le haut */
<!-- flex-wrap.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-wrap.css">
  <title>flex-wrap</title>
</head>
<body>

  <h2>nowrap (défaut) — les éléments se compriment sur une seule ligne</h2>
  <div class="conteneur nowrap">
    <div class="carte">Carte 1</div>
    <div class="carte">Carte 2</div>
    <div class="carte">Carte 3</div>
    <div class="carte">Carte 4</div>
    <div class="carte">Carte 5</div>
  </div>

  <h2>wrap — les éléments passent à la ligne suivante</h2>
  <div class="conteneur wrap">
    <div class="carte">Carte 1</div>
    <div class="carte">Carte 2</div>
    <div class="carte">Carte 3</div>
    <div class="carte">Carte 4</div>
    <div class="carte">Carte 5</div>
  </div>

</body>
</html>
/* flex-wrap.css */

body {
  font-family: Arial, sans-serif;
  padding: 30px;
  background-color: #f4f4f4;
}

h2 { color: #1A3A5C; margin: 30px 0 10px; font-size: 15px; }

.conteneur {
  display: flex;
  gap: 10px;
  background-color: #e0e8f5;
  padding: 12px;
  margin-bottom: 30px;
}

.nowrap { flex-wrap: nowrap; }
.wrap   { flex-wrap: wrap; }

.carte {
  min-width: 160px;
  background-color: #2E6DA4;
  color: white;
  padding: 20px;
  text-align: center;
  border-radius: 4px;
  font-weight: bold;
}

flex-wrap: wrap et le responsive : combiné à une min-width sur les éléments, flex-wrap: wrap crée des grilles naturellement responsives sans media query. Quand le conteneur est trop étroit, les éléments passent à la ligne automatiquement.

La propriété raccourcie flex-flow combine flex-direction et flex-wrap :

/* Ces deux écritures sont équivalentes */
.conteneur {
  flex-direction: row;
  flex-wrap: wrap;
}

.conteneur {
  flex-flow: row wrap;
}

7.4.3 Alignement sur les axes

Trois propriétés gèrent l'alignement : justify-content pour l'axe principal, align-items pour l'axe secondaire, et align-content pour les conteneurs multi-lignes.

justify-content — distribution sur l'axe principal.

<!-- justify-content.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="justify-content.css">
  <title>justify-content</title>
</head>
<body>

  <h2>flex-start (défaut)</h2>
  <div class="conteneur jc-start">
    <div class="boite">A</div><div class="boite">B</div><div class="boite">C</div>
  </div>

  <h2>flex-end</h2>
  <div class="conteneur jc-end">
    <div class="boite">A</div><div class="boite">B</div><div class="boite">C</div>
  </div>

  <h2>center</h2>
  <div class="conteneur jc-center">
    <div class="boite">A</div><div class="boite">B</div><div class="boite">C</div>
  </div>

  <h2>space-between — premier et dernier aux extrémités</h2>
  <div class="conteneur jc-between">
    <div class="boite">A</div><div class="boite">B</div><div class="boite">C</div>
  </div>

  <h2>space-around — espace égal autour de chaque élément</h2>
  <div class="conteneur jc-around">
    <div class="boite">A</div><div class="boite">B</div><div class="boite">C</div>
  </div>

  <h2>space-evenly — espace strictement identique partout</h2>
  <div class="conteneur jc-evenly">
    <div class="boite">A</div><div class="boite">B</div><div class="boite">C</div>
  </div>

</body>
</html>
/* justify-content.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 12px;
  margin-bottom: 8px;
}

.jc-start   { justify-content: flex-start; }
.jc-end     { justify-content: flex-end; }
.jc-center  { justify-content: center; }
.jc-between { justify-content: space-between; }
.jc-around  { justify-content: space-around; }
.jc-evenly  { justify-content: space-evenly; }

.boite {
  background-color: #2E6DA4;
  color: white;
  width: 60px;
  height: 60px;
  line-height: 60px;
  text-align: center;
  font-size: 20px;
  font-weight: bold;
  border-radius: 4px;
}

align-items — alignement sur l'axe secondaire.

<!-- align-items.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="align-items.css">
  <title>align-items</title>
</head>
<body>

  <h2>stretch (défaut) — éléments étirés sur toute la hauteur</h2>
  <div class="conteneur ai-stretch">
    <div class="boite">A</div><div class="boite haute">B</div><div class="boite">C</div>
  </div>

  <h2>flex-start — éléments alignés en haut</h2>
  <div class="conteneur ai-start">
    <div class="boite">A</div><div class="boite haute">B</div><div class="boite">C</div>
  </div>

  <h2>center — éléments centrés verticalement</h2>
  <div class="conteneur ai-center">
    <div class="boite">A</div><div class="boite haute">B</div><div class="boite">C</div>
  </div>

  <h2>flex-end — éléments alignés en bas</h2>
  <div class="conteneur ai-end">
    <div class="boite">A</div><div class="boite haute">B</div><div class="boite">C</div>
  </div>

  <h2>baseline — alignement sur la ligne de base du texte</h2>
  <div class="conteneur ai-baseline">
    <div class="boite petit-texte">Aa</div>
    <div class="boite grand-texte">Aa</div>
    <div class="boite petit-texte">Aa</div>
  </div>

</body>
</html>
/* align-items.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 12px;
  gap: 10px;
  height: 120px;
  margin-bottom: 8px;
}

.ai-stretch  { align-items: stretch; }
.ai-start    { align-items: flex-start; }
.ai-center   { align-items: center; }
.ai-end      { align-items: flex-end; }
.ai-baseline { align-items: baseline; }

.boite {
  background-color: #2E6DA4;
  color: white;
  padding: 10px 20px;
  font-weight: bold;
  border-radius: 4px;
  text-align: center;
}

.haute       { height: 70px; background-color: #1A3A5C; line-height: 50px; }
.petit-texte { font-size: 13px; padding-top: 20px; }
.grand-texte { font-size: 32px; }

baseline en pratique : aligne les éléments sur la ligne de base typographique — la ligne imaginaire sur laquelle repose le bas des lettres. Utile quand des éléments avec des tailles de police différentes doivent paraître alignés de façon cohérente.

align-content — alignement des lignes dans un conteneur multi-lignes. Ne s'applique que quand flex-wrap: wrap est actif. Contrôle comment les lignes sont distribuées sur l'axe secondaire, à la manière de justify-content pour les éléments.

.conteneur {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;    /* Lignes groupées en haut */
  align-content: center;        /* Lignes groupées au centre */
  align-content: space-between; /* Espace égal entre les lignes */
  align-content: stretch;       /* Lignes étirées (valeur par défaut) */
}

Exemple:


<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Align Content Demo</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <div class="container">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
  </div>

</body>
</html>
body {
  margin: 0;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f2f2f2;
}

.container {
  display: flex;
  flex-wrap: wrap;           /* OBLIGATOIRE */
  width: 300px;
  height: 400px;             /* Plus grand que le contenu */
  background: white;
  border: 2px solid black;

  align-content: center;     /* 👈 La star ici */
  gap: 10px;
}

.item {
  width: 80px;
  height: 80px;
  background: steelblue;
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
}

Exercice 18 — Le tableau de bord d'une application

Objectif : construire le tableau de bord d'une application fictive : une barre de navigation, une rangée de quatre cartes de statistiques et un pied de page. Chaque zone mobilise une combinaison différente de justify-content et align-items.

Consignes HTML :

  • Créez un fichier dashboard.html.
  • Créez un <header class="navbar"> contenant un <span class="logo"> et une <nav> avec trois <a> : Vue d'ensemble, Statistiques, Paramètres.
  • Créez une <main class="contenu"> contenant quatre <div class="stat-card">. Chaque carte contient un <p class="stat-label"> et un <p class="stat-valeur">.
  • Créez un <footer> avec un <p> de copyright et un <span class="version">.
  • Liez un fichier dashboard.css.

Consignes :

  • La .navbar est un conteneur flex : logo à gauche, liens à droite, tout centré verticalement. La navbar a une hauteur fixe de 60px.
  • Les quatre .stat-card s'affichent côte à côte avec un espace identique entre elles. Le contenu de chaque carte est centré horizontalement et verticalement à l'intérieur.
  • Le <footer> est un conteneur flex : copyright à gauche, version à droite, centrés verticalement.

Correction détaillée — Exercice 18

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="dashboard.css">
  <title>Tableau de bord</title>
</head>
<body>

  <header class="navbar">
    <span class="logo">AppDash</span>
    <nav>
      <a href="#">Vue d'ensemble</a>
      <a href="#">Statistiques</a>
      <a href="#">Paramètres</a>
    </nav>
  </header>

  <main class="contenu">

    <div class="stat-card">
      <p class="stat-label">Utilisateurs actifs</p>
      <p class="stat-valeur">12 480</p>
    </div>

    <div class="stat-card">
      <p class="stat-label">Commandes du jour</p>
      <p class="stat-valeur">847</p>
    </div>

    <div class="stat-card">
      <p class="stat-label">Chiffre d'affaires</p>
      <p class="stat-valeur">38 920 €</p>
    </div>

    <div class="stat-card">
      <p class="stat-label">Taux de conversion</p>
      <p class="stat-valeur">6,8 %</p>
    </div>

  </main>

  <footer>
    <p>© 2025 AppDash. Tous droits réservés.</p>
    <span class="version">v2.4.1</span>
  </footer>

</body>
</html>

Correction CSS

/* dashboard.css — Correction exercice 18 */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
  color: #2c2c2c;
}

/*
  justify-content: space-between pousse le logo à gauche
  et la nav à droite.
  align-items: center centre verticalement sans line-height couplé.
*/
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #1A3A5C;
  padding: 0 30px;
  height: 60px;
}

.logo {
  color: #ffffff;
  font-size: 20px;
  font-weight: bold;
}

.navbar nav {
  display: flex;
  gap: 4px;
}

.navbar nav a {
  display: block;
  padding: 8px 16px;
  color: #a8c4e0;
  text-decoration: none;
  border-radius: 4px;
  font-size: 14px;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.navbar nav a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/*
  justify-content: space-between distribue l'espace entre les cartes.
  Les cartes s'étirent automatiquement à la même hauteur (stretch par défaut).
*/
.contenu {
  display: flex;
  justify-content: space-between;
  padding: 40px 30px;
  gap: 20px;
}

/*
  Chaque carte est elle-même un conteneur flex en colonne
  pour centrer son contenu dans les deux axes.
*/
.stat-card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  flex: 1;
  background-color: #ffffff;
  border-radius: 10px;
  padding: 30px 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  text-align: center;
}

.stat-label {
  font-size: 13px;
  color: #888888;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 12px;
}

.stat-valeur {
  font-size: 32px;
  font-weight: bold;
  color: #1A3A5C;
}

/* Même pattern que la navbar */
footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #1A3A5C;
  padding: 16px 30px;
  color: #a8c4e0;
  font-size: 13px;
}

.version {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 3px 10px;
  border-radius: 10px;
  font-size: 12px;
}

Explications et points de vigilance

justify-content: space-between pour la navbar et le footer : place le premier enfant au début et le dernier à la fin, tout l'espace restant se répartit entre eux. C'est le pattern le plus courant pour les barres avec un élément à gauche et un autre à droite.

align-items: center sans line-height couplé : comparez avec l'exercice 15. Ici, le centrage vertical de la navbar est obtenu en une propriété, indépendamment de la hauteur. Si demain la navbar passe à 80px, rien d'autre à modifier.

Flex imbriqué sur .stat-card : chaque carte est à la fois un élément flex géré par .contenu, et un conteneur flex pour son propre contenu. flex-direction: column + justify-content: center + align-items: center centre verticalement et horizontalement le label et la valeur.

Erreur fréquente : inverser justify-content et align-items quand flex-direction: row. Dans ce cas, justify-content agit horizontalement et align-items verticalement — les confondre produit un résultat inattendu.

Exercice 19 — La page de tarification

Objectif : construire une section de tarification avec trois offres côte à côte. L'offre centrale est mise en avant grâce à align-items: flex-end combiné à un padding plus généreux. L'exercice travaille justify-content, align-items et flex-wrap dans un cas concret de mise en page de cartes de tailles différentes.

Consignes HTML :

  • Créez un fichier tarifs.html.
  • Créez une <section class="tarification"> avec un <h1> et une <div class="offres">.
  • Dans .offres, créez trois <div> avec la classe offre et les classes supplémentaires essentiel, pro, entreprise.
  • Chaque .offre contient : un <h2>, un <p class="prix">, un <ul> de quatre <li> et un <a class="btn-offre">.
  • Liez un fichier tarifs.css.

Consignes :

  • .offres est un conteneur flex. Les trois offres sont centrées horizontalement dans la section et alignées par le bas — ce qui fait naturellement dépasser vers le haut la carte .pro qui est plus haute.
  • Chaque .offre a une largeur fixe de 260px, un fond blanc, du padding et une ombre.
  • L'offre .pro a davantage de padding vertical et une couleur de fond distincte.
  • Le .btn-offre est un bouton pleine largeur.

Correction détaillée — Exercice 19

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="tarifs.css">
  <title>Tarification</title>
</head>
<body>

  <section class="tarification">

    <h1>Choisissez votre offre</h1>

    <div class="offres">

      <div class="offre essentiel">
        <h2>Essentiel</h2>
        <p class="prix">9 € <span class="periode">/mois</span></p>
        <ul>
          <li>3 projets</li>
          <li>5 Go de stockage</li>
          <li>Support par email</li>
          <li>Mises à jour incluses</li>
        </ul>
        <a href="#" class="btn-offre">Commencer</a>
      </div>

      <div class="offre pro">
        <h2>Pro</h2>
        <p class="prix">29 € <span class="periode">/mois</span></p>
        <ul>
          <li>Projets illimités</li>
          <li>50 Go de stockage</li>
          <li>Support prioritaire</li>
          <li>Accès API inclus</li>
        </ul>
        <a href="#" class="btn-offre">Choisir Pro</a>
      </div>

      <div class="offre entreprise">
        <h2>Entreprise</h2>
        <p class="prix">79 € <span class="periode">/mois</span></p>
        <ul>
          <li>Tout de Pro</li>
          <li>Stockage illimité</li>
          <li>Support dédié 24h/7j</li>
          <li>SSO & audit logs</li>
        </ul>
        <a href="#" class="btn-offre">Nous contacter</a>
      </div>

    </div>

  </section>

</body>
</html>

Correction CSS

/* tarifs.css — Correction exercice 19 */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
  color: #2c2c2c;
}

.tarification {
  padding: 60px 30px;
}

.tarification h1 {
  text-align: center;
  color: #1A3A5C;
  font-size: 28px;
  margin-bottom: 40px;
}

/*
  justify-content: center groupe les offres au centre de la page.
  align-items: flex-end aligne toutes les cartes par le bas :
  la carte .pro, plus haute grâce à son padding supérieur,
  dépasse naturellement vers le haut — effet classique de mise en avant.
*/
.offres {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 20px;
}

.offre {
  width: 260px;
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 10px;
  padding: 30px 24px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.07);
  text-align: center;
}

/* Padding vertical plus grand = carte plus haute = dépasse vers le haut */
.pro {
  background-color: #1A3A5C;
  border-color: #1A3A5C;
  padding: 46px 24px;
  color: #ffffff;
}

.offre h2 {
  font-size: 20px;
  margin-bottom: 16px;
  color: #1A3A5C;
}

.pro h2 { color: #ffffff; }

.prix {
  font-size: 32px;
  font-weight: bold;
  color: #1A3A5C;
  margin-bottom: 24px;
}

.pro .prix { color: #ffffff; }

.periode {
  font-size: 14px;
  font-weight: normal;
  color: #888888;
}

.pro .periode { color: #a8c4e0; }

.offre ul {
  list-style: none;
  margin-bottom: 28px;
  padding: 0;
}

.offre ul li {
  padding: 8px 0;
  font-size: 14px;
  color: #555555;
  border-bottom: 1px solid #f0f0f0;
}

.offre ul li:last-child { border-bottom: none; }

.pro ul li {
  color: #d0e4f5;
  border-bottom-color: #2E6DA4;
}

.btn-offre {
  display: block;
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 12px;
  text-decoration: none;
  border-radius: 6px;
  font-weight: bold;
  font-size: 14px;
  transition: background-color 0.2s ease;
}

.btn-offre:hover { background-color: #1A3A5C; }

.pro .btn-offre {
  background-color: #ffffff;
  color: #1A3A5C;
}

.pro .btn-offre:hover { background-color: #a8c4e0; }

Explications et points de vigilance

align-items: flex-end : toutes les cartes sont alignées par leur bord inférieur. La carte Pro, ayant plus de padding vertical, est naturellement plus haute — elle dépasse vers le haut. C'est un pattern de design très courant pour les pages de tarification, obtenu ici en une seule propriété Flexbox.

justify-content: center : les trois cartes ont une largeur fixe de 260px. Sans cette propriété, elles seraient groupées à gauche. center les place au milieu de la section quelle que soit la largeur de l'écran.

Erreur fréquente : tenter d'obtenir l'effet de surélévation avec margin-top: -30px ou position: relative. Ces approches fonctionnent mais sont fragiles. align-items: flex-end combiné à un padding plus grand est la solution Flexbox naturelle et robuste.

7.5 Propriétés des éléments flex

Jusqu'ici, toutes les propriétés étudiées s'appliquaient sur le conteneur. Les propriétés de cette section s'appliquent sur les éléments flex eux-mêmes — les enfants directs du conteneur. Elles permettent de contrôler comment chaque élément se comporte individuellement face à l'espace disponible : est-ce qu'il grandit ? Est-ce qu'il rétrécit ? Quelle est sa taille de départ ?

7.5.1 Croissance et réduction

flex-grow — la croissance.

flex-grow définit la capacité d'un élément à grandir pour occuper l'espace disponible dans le conteneur. Sa valeur est un nombre sans unité qui représente une proportion.

<!-- flex-grow.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-grow.css">
  <title>flex-grow</title>
</head>
<body>

  <h2>flex-grow: 0 sur tous (défaut) — les éléments prennent leur taille naturelle</h2>
  <div class="conteneur">
    <div class="item g0">A</div>
    <div class="item g0">B</div>
    <div class="item g0">C</div>
  </div>

  <h2>flex-grow: 1 sur tous — l'espace est partagé équitablement</h2>
  <div class="conteneur">
    <div class="item g1">A</div>
    <div class="item g1">B</div>
    <div class="item g1">C</div>
  </div>

  <h2>flex-grow: 1 / 2 / 1 — B prend deux fois plus d'espace que A et C</h2>
  <div class="conteneur">
    <div class="item g1">A</div>
    <div class="item g2">B</div>
    <div class="item g1">C</div>
  </div>

  <h2>flex-grow: 0 / 1 / 0 — seul B s'étire pour combler l'espace</h2>
  <div class="conteneur">
    <div class="item g0">A</div>
    <div class="item g1">B</div>
    <div class="item g0">C</div>
  </div>

</body>
</html>
/* flex-grow.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 10px;
  gap: 10px;
  margin-bottom: 8px;
}

.item {
  background-color: #2E6DA4;
  color: white;
  padding: 16px;
  text-align: center;
  font-weight: bold;
  font-size: 18px;
  border-radius: 4px;
}

.g0 { flex-grow: 0; } /* Défaut : ne grandit pas */
.g1 { flex-grow: 1; }
.g2 { flex-grow: 2; } /* Prend deux fois plus d'espace libre que flex-grow: 1 */

Comment fonctionne le calcul : flex-grow ne définit pas une largeur absolue. Il définit la proportion de l'espace libre que chaque élément reçoit. Si le conteneur a 300px d'espace non occupé et que trois éléments ont flex-grow: 1, chacun reçoit 100px supplémentaires. Si l'un a flex-grow: 2 et les deux autres flex-grow: 1, le premier reçoit 150px et les deux autres 75px chacun.

flex-shrink — la réduction.

flex-shrink définit la capacité d'un élément à rétrécir quand l'espace disponible est insuffisant. Sa valeur est également un nombre proportionnel. La valeur par défaut est 1 — tous les éléments rétrécissent proportionnellement par défaut.

<!-- flex-shrink.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-shrink.css">
  <title>flex-shrink</title>
</head>
<body>

  <h2>flex-shrink: 1 sur tous (défaut) — tous rétrécissent proportionnellement</h2>
  <div class="conteneur">
    <div class="item s1">A — 200px</div>
    <div class="item s1">B — 200px</div>
    <div class="item s1">C — 200px</div>
  </div>

  <h2>flex-shrink: 0 sur B — B refuse de rétrécir, A et C absorbent le manque</h2>
  <div class="conteneur">
    <div class="item s1">A — 200px</div>
    <div class="item s0">B — 200px (ne rétrécit pas)</div>
    <div class="item s1">C — 200px</div>
  </div>

</body>
</html>
/* flex-shrink.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

/* Conteneur délibérément étroit pour forcer le rétrécissement */
.conteneur {
  display: flex;
  width: 500px;
  background-color: #e0e8f5;
  padding: 10px;
  gap: 8px;
  margin-bottom: 8px;
}

.item {
  width: 200px; /* 3 x 200px = 600px > 500px : il faut rétrécir */
  background-color: #2E6DA4;
  color: white;
  padding: 16px 10px;
  text-align: center;
  font-size: 13px;
  font-weight: bold;
  border-radius: 4px;
}

.s1 { flex-shrink: 1; } /* Rétrécit normalement (défaut) */
.s0 { flex-shrink: 0; background-color: #C0392B; } /* Refuse de rétrécir */

flex-shrink: 0 en pratique : cette valeur est très utilisée pour les éléments qui ne doivent jamais se comprimer — une icône dans une barre de navigation, un avatar dans une carte de profil, un logo. Sans flex-shrink: 0, ces éléments pourraient se déformer sur les petits écrans.

7.5.2 Taille de base des éléments

flex-basis — le point de départ.

flex-basis définit la taille initiale d'un élément flex, avant que flex-grow et flex-shrink n'entrent en jeu. C'est le point de départ des calculs de distribution de l'espace.

.item { flex-basis: auto; }    /* Défaut : la taille est déterminée par le contenu
                                  ou par width/height si défini */
.item { flex-basis: 200px; }   /* Taille de base fixe de 200px */
.item { flex-basis: 33.333%; } /* Taille de base proportionnelle */
.item { flex-basis: 0; }       /* Taille de base nulle : tout l'espace est "libre"
                                  et réparti selon flex-grow */
<!-- flex-basis.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-basis.css">
  <title>flex-basis</title>
</head>
<body>

  <h2>flex-basis: auto (défaut) — taille déterminée par le contenu</h2>
  <div class="conteneur">
    <div class="item">Contenu court</div>
    <div class="item">Contenu beaucoup plus long qui prend plus de place</div>
    <div class="item">Moyen</div>
  </div>

  <h2>flex-basis: 200px — taille de départ fixe identique</h2>
  <div class="conteneur">
    <div class="item basis-200">Contenu court</div>
    <div class="item basis-200">Contenu beaucoup plus long qui prend plus de place</div>
    <div class="item basis-200">Moyen</div>
  </div>

  <h2>flex-basis: 0 + flex-grow: 1 — distribution purement proportionnelle</h2>
  <div class="conteneur">
    <div class="item basis-0">Contenu court</div>
    <div class="item basis-0">Contenu beaucoup plus long</div>
    <div class="item basis-0">Moyen</div>
  </div>

</body>
</html>
/* flex-basis.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

.conteneur {
  display: flex;
  background-color: #e0e8f5;
  padding: 10px;
  gap: 10px;
  margin-bottom: 8px;
}

.item {
  background-color: #2E6DA4;
  color: white;
  padding: 16px;
  font-size: 13px;
  border-radius: 4px;
}

.basis-200 {
  flex-basis: 200px;
  /* Taille de départ à 200px pour chaque élément.
     Avec flex-grow: 0 par défaut, ils gardent cette taille si l'espace suffit. */
}

.basis-0 {
  flex-basis: 0;
  flex-grow: 1;
  /* Taille de départ nulle : TOUT l'espace du conteneur est considéré
     comme libre et distribué à parts égales par flex-grow: 1.
     Le contenu n'influence plus la taille de départ. */
}

La propriété raccourcie flex.

Dans la pratique, flex-grow, flex-shrink et flex-basis sont presque toujours écrits ensemble via la propriété raccourcie flex. C'est la forme recommandée car elle initialise automatiquement les valeurs non précisées de manière cohérente.

/* Syntaxe complète */
.item { flex: <flex-grow> <flex-shrink> <flex-basis>; }

/* Exemples courants */
.item { flex: 1; }
/* Équivalent à : flex-grow: 1, flex-shrink: 1, flex-basis: 0
   L'élément grandit et rétrécit librement, taille de base nulle.
   C'est la valeur la plus utilisée pour des colonnes de taille égale. */

.item { flex: auto; }
/* Équivalent à : flex-grow: 1, flex-shrink: 1, flex-basis: auto
   L'élément grandit et rétrécit, mais en partant de sa taille naturelle. */

.item { flex: none; }
/* Équivalent à : flex-grow: 0, flex-shrink: 0, flex-basis: auto
   L'élément ne grandit ni ne rétrécit. Taille figée à son contenu. */

.item { flex: 0 0 250px; }
/* L'élément ne grandit pas, ne rétrécit pas, fait toujours 250px. */

.item { flex: 2 1 300px; }
/* Taille de base 300px, grandit deux fois plus vite que flex: 1,
   rétrécit normalement. */
<!-- flex-raccourci.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="flex-raccourci.css">
  <title>Propriété raccourcie flex</title>
</head>
<body>

  <h2>Sidebar fixe + contenu flexible — pattern très courant</h2>
  <div class="layout">
    <aside class="sidebar">Sidebar<br>fixe 260px</aside>
    <main class="principal">Contenu principal — prend tout l'espace restant</main>
  </div>

  <h2>Trois colonnes : petite / grande / petite</h2>
  <div class="trois-col">
    <div class="col-small">Petite<br>flex: 1</div>
    <div class="col-large">Grande<br>flex: 2</div>
    <div class="col-small">Petite<br>flex: 1</div>
  </div>

</body>
</html>
/* flex-raccourci.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

/* Pattern sidebar + contenu : le plus fréquent en mise en page */
.layout {
  display: flex;
  gap: 20px;
  margin-bottom: 40px;
  height: 150px;
}

.sidebar {
  flex: 0 0 260px; /* Ne grandit pas, ne rétrécit pas, toujours 260px */
  background-color: #1A3A5C;
  color: white;
  padding: 20px;
  border-radius: 4px;
  font-size: 14px;
}

.principal {
  flex: 1; /* Prend tout l'espace restant après la sidebar */
  background-color: #2E6DA4;
  color: white;
  padding: 20px;
  border-radius: 4px;
  font-size: 14px;
}

/* Trois colonnes avec proportions différentes */
.trois-col {
  display: flex;
  gap: 10px;
  height: 100px;
}

.col-small {
  flex: 1; /* 1 part de l'espace disponible */
  background-color: #2E6DA4;
  color: white;
  padding: 16px;
  text-align: center;
  border-radius: 4px;
  font-size: 13px;
}

.col-large {
  flex: 2; /* 2 parts de l'espace disponible */
  background-color: #1A3A5C;
  color: white;
  padding: 16px;
  text-align: center;
  border-radius: 4px;
  font-size: 13px;
}

La propriété align-self — alignement individuel.

Toutes les propriétés d'alignement vues jusqu'ici s'appliquaient à tous les éléments du conteneur. align-self permet de surcharger align-items pour un seul élément, en lui donnant un alignement différent de ses frères.

<!-- align-self.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="align-self.css">
  <title>align-self</title>
</head>
<body>

  <h2>align-items: center sur le conteneur, mais chaque élément peut se positionner individuellement</h2>
  <div class="conteneur">
    <div class="item self-start">flex-start</div>
    <div class="item self-center">center (hérite)</div>
    <div class="item self-end">flex-end</div>
    <div class="item self-stretch">stretch</div>
  </div>

</body>
</html>
/* align-self.css */

body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }
h2 { color: #1A3A5C; margin: 24px 0 8px; font-size: 14px; }

.conteneur {
  display: flex;
  align-items: center; /* Valeur par défaut pour tous les enfants */
  background-color: #e0e8f5;
  padding: 12px;
  gap: 10px;
  height: 160px;
}

.item {
  background-color: #2E6DA4;
  color: white;
  padding: 16px;
  border-radius: 4px;
  font-size: 13px;
  font-weight: bold;
  text-align: center;
}

/* Chaque élément surcharge align-items du conteneur */
.self-start   { align-self: flex-start; background-color: #C0392B; }
.self-center  { align-self: center; }      /* Même que le conteneur : identique */
.self-end     { align-self: flex-end; background-color: #1E6B2E; }
.self-stretch { align-self: stretch; background-color: #8E44AD; }

align-self en pratique : très utile pour les cas où un seul élément doit se comporter différemment des autres — par exemple un bouton "Supprimer" aligné en bas d'une carte alors que le reste est centré, ou un logo aligné en haut dans une navbar alors que les liens sont centrés.

Exercice 20 — La mise en page d'un article de blog

Objectif : construire la mise en page d'un article de blog avec une colonne principale et une sidebar fixe. L'exercice travaille flex: 1 sur le contenu principal et flex: 0 0 <valeur> sur la sidebar pour créer un layout robuste où la sidebar reste à largeur constante quelle que soit la taille de l'écran.

Consignes HTML :

  • Créez un fichier article-blog.html.
  • Créez un <header> avec un <h1> (titre du blog) et une <nav> avec trois liens.
  • Créez un <div class="page"> contenant :
    • Un <article class="contenu-principal"> avec un <h2>, trois <p> de texte et une <div class="tags"> contenant quatre <span class="tag">.
    • Une <aside class="sidebar"> contenant un <h3>, une <ul> de cinq liens, et un second bloc avec un <h3> et un <p> de description.
  • Créez un <footer> avec un <p>.
  • Liez un fichier article-blog.css.

Consignes :

  • Le <header> est un conteneur flex avec le titre à gauche et la navigation à droite, centrés verticalement.
  • .page est un conteneur flex : .contenu-principal prend tout l'espace disponible, .sidebar reste à largeur fixe et ne rétrécit jamais.
  • Les .tag sont des éléments inline qui se placent naturellement côte à côte — pas besoin de Flexbox sur leur conteneur.
  • Le <footer> est centré horizontalement.

Correction détaillée — Exercice 20

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="article-blog.css">
  <title>Article de blog</title>
</head>
<body>

  <header class="header-blog">
    <h1>Le Blog du Dev</h1>
    <nav>
      <a href="#">Accueil</a>
      <a href="#">Articles</a>
      <a href="#">À propos</a>
    </nav>
  </header>

  <div class="page">

    <article class="contenu-principal">
      <h2>Flexbox : la propriété qui a changé la mise en page CSS</h2>
      <p>Pendant des années, les développeurs web ont dû contourner les limites du modèle de boîte traditionnel pour créer des mises en page en colonnes. Les flottants, détournés de leur usage typographique originel, ont permis de construire des interfaces complètes mais au prix d'une complexité croissante.</p>
      <p>L'arrivée de Flexbox a changé radicalement la donne. En s'appliquant sur le conteneur plutôt que sur chaque élément individuellement, Flexbox offre un niveau d'abstraction bien supérieur. Les calculs manuels de pourcentages, les clearfix et les problèmes de hauteur inégale appartiennent désormais au passé.</p>
      <p>Aujourd'hui, Flexbox est supporté par tous les navigateurs modernes avec une compatibilité quasi universelle. Il est devenu l'outil de référence pour les mises en page de composants, tandis que CSS Grid prend en charge les grandes structures de page. Les deux technologies sont complémentaires et s'utilisent conjointement dans la majorité des projets.</p>

      <div class="tags">
        <span class="tag">CSS</span>
        <span class="tag">Flexbox</span>
        <span class="tag">Mise en page</span>
        <span class="tag">Tutoriel</span>
      </div>
    </article>

    <aside class="sidebar">

      <div class="widget">
        <h3>Articles récents</h3>
        <ul>
          <li><a href="#">Introduction à CSS Grid</a></li>
          <li><a href="#">Les variables CSS natives</a></li>
          <li><a href="#">Media queries et responsive</a></li>
          <li><a href="#">Animations CSS modernes</a></li>
          <li><a href="#">Optimiser ses sélecteurs CSS</a></li>
        </ul>
      </div>

      <div class="widget">
        <h3>À propos</h3>
        <p>Ce blog est dédié au développement web front-end. Retrouvez des tutoriels, des bonnes pratiques et des retours d'expérience sur le CSS, JavaScript et les outils modernes du web.</p>
      </div>

    </aside>

  </div>

  <footer class="footer-blog">
    <p>&copy; 2024 Le Blog du Dev. Tous droits réservés.</p>
  </footer>

</body>
</html>

Correction CSS

/* article-blog.css — Correction exercice 20 */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Georgia, serif;
  background-color: #f5f5f0;
  color: #2c2c2c;
}

/* Header */
.header-blog {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #1A3A5C;
  padding: 0 30px;
  height: 64px;
}

.header-blog h1 {
  color: #ffffff;
  font-size: 22px;
  letter-spacing: 1px;
}

.header-blog nav {
  display: flex;
  gap: 4px;
}

.header-blog nav a {
  display: block;
  padding: 8px 14px;
  color: #a8c4e0;
  text-decoration: none;
  font-size: 14px;
  font-family: Arial, sans-serif;
  border-radius: 4px;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.header-blog nav a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/* Layout principal */
.page {
  display: flex;
  gap: 30px;
  max-width: 1000px;
  margin: 0 auto;
  padding: 40px 20px;
}

/*
  flex: 1 — l'article prend tout l'espace disponible après la sidebar.
  Il grandit et rétrécit librement selon la largeur de la fenêtre.
*/
.contenu-principal {
  flex: 1;
  background-color: #ffffff;
  padding: 32px;
  border: 1px solid #e0ddd6;
}

.contenu-principal h2 {
  color: #1A3A5C;
  font-size: 22px;
  line-height: 1.4;
  margin-bottom: 20px;
}

.contenu-principal p {
  font-size: 15px;
  line-height: 1.9;
  color: #444444;
  margin-bottom: 16px;
}

/* Les tags sont des <span> inline : ils se placent naturellement côte à côte */
.tags {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid #e8e4dc;
}

.tag {
  display: inline-block;
  background-color: #e8f0fe;
  color: #2E6DA4;
  font-family: Arial, sans-serif;
  font-size: 12px;
  font-weight: bold;
  padding: 4px 12px;
  border-radius: 20px;
  margin-right: 6px;
  margin-bottom: 6px;
}

/*
  flex: 0 0 260px — la sidebar ne grandit pas (0), ne rétrécit pas (0),
  et fait toujours 260px (260px).
  Sans le flex-shrink: 0, la sidebar pourrait se comprimer
  sur les écrans moyens, ce qui casserait le layout.
*/
.sidebar {
  flex: 0 0 260px;
}

.widget {
  background-color: #ffffff;
  border: 1px solid #e0ddd6;
  padding: 20px;
  margin-bottom: 20px;
}

.widget h3 {
  color: #1A3A5C;
  font-size: 16px;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 2px solid #2E6DA4;
  font-family: Arial, sans-serif;
}

.widget ul {
  list-style: none;
}

.widget ul li {
  padding: 7px 0;
  border-bottom: 1px solid #f0ece4;
}

.widget ul li:last-child { border-bottom: none; }

.widget ul li a {
  color: #2E6DA4;
  text-decoration: none;
  font-family: Arial, sans-serif;
  font-size: 14px;
  transition: color 0.2s ease;
}

.widget ul li a:hover { color: #1A3A5C; }

.widget p {
  font-size: 13px;
  line-height: 1.7;
  color: #666666;
}

/* Footer */
.footer-blog {
  background-color: #1A3A5C;
  color: #a8c4e0;
  text-align: center;
  padding: 20px;
  font-family: Arial, sans-serif;
  font-size: 13px;
}

Explications et points de vigilance

flex: 1 sur .contenu-principal : cette valeur est l'équivalent de flex: 1 1 0. L'article part d'une taille de base nulle et prend tout l'espace disponible dans le conteneur après que la sidebar a occupé ses 260px. Si la fenêtre s'élargit, l'article s'élargit. Si elle rétrécit, l'article rétrécit. La sidebar, elle, reste intacte.

flex: 0 0 260px sur .sidebar : les deux premiers 0 signifient que la sidebar ne grandit jamais (flex-grow: 0) et ne rétrécit jamais (flex-shrink: 0). Le 260px est sa flex-basis — sa taille permanente. C'est le pattern fondamental de toute mise en page avec sidebar fixe en Flexbox.

Erreur fréquente : écrire seulement width: 260px sur la sidebar sans flex-shrink: 0. Sur un écran plus étroit, Flexbox comprimera quand même la sidebar si nécessaire car flex-shrink: 1 est la valeur par défaut. Le résultat : la sidebar rétrécit alors qu'on ne le souhaitait pas.

Exercice 21 — Les cartes de fonctionnalités

Objectif : construire une section de fonctionnalités avec des cartes de tailles proportionnelles différentes. Une fonctionnalité principale prend deux fois plus de place que les secondaires. L'exercice travaille flex-grow avec des proportions variées, flex-wrap pour le passage à la ligne et align-self pour une carte qui se distingue verticalement.

Consignes HTML :

Créez un fichier fonctionnalites.html.

Créez une <section class="section-fonc"> avec un <h1> et une <div class="grille">.

Dans .grille, créez cinq <div class="fonc"> avec les classes supplémentaires : fonc-principale, fonc-secondaire (x3) et fonc-cta.

Chaque .fonc contient un <div class="fonc-icone"> (une initiale), un <h2> et un <p>.

.fonc-cta contient en plus un <a class="btn"> avec le texte En savoir plus.

Liez un fichier fonctionnalites.css.

Consignes :

  • .grille est un conteneur flex avec flex-wrap: wrap et un gap.
  • .fonc-principale a un flex qui lui donne deux fois plus de place que les cartes secondaires.
  • Les trois .fonc-secondaire ont un flex égal entre elles.
  • .fonc-cta a la même taille qu'une .fonc-secondaire mais s'aligne en bas du conteneur grâce à align-self, quelle que soit la hauteur des autres cartes.
  • Chaque .fonc a un fond blanc, du padding, une bordure et des coins arrondis.

Correction détaillée — Exercice 21

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="fonctionnalites.css">
  <title>Fonctionnalités</title>
</head>
<body>

  <section class="section-fonc">

    <h1>Tout ce dont vous avez besoin</h1>

    <div class="grille">

      <div class="fonc fonc-principale">
        <div class="fonc-icone">P</div>
        <h2>Performance optimisée</h2>
        <p>Notre infrastructure est conçue pour offrir des temps de réponse inférieurs à 100ms, même sous forte charge. Les données sont distribuées sur plusieurs régions pour garantir une disponibilité maximale et une latence minimale pour vos utilisateurs, où qu'ils se trouvent dans le monde.</p>
      </div>

      <div class="fonc fonc-secondaire">
        <div class="fonc-icone">S</div>
        <h2>Sécurité renforcée</h2>
        <p>Chiffrement de bout en bout et authentification multi-facteurs inclus dans tous les plans.</p>
      </div>

      <div class="fonc fonc-secondaire">
        <div class="fonc-icone">A</div>
        <h2>API complète</h2>
        <p>Une API REST documentée pour intégrer nos services dans votre stack technique existant.</p>
      </div>

      <div class="fonc fonc-secondaire">
        <div class="fonc-icone">R</div>
        <h2>Rapports détaillés</h2>
        <p>Tableaux de bord en temps réel et exports CSV pour analyser vos données en profondeur.</p>
      </div>

      <div class="fonc fonc-cta">
        <div class="fonc-icone">+</div>
        <h2>Et bien plus encore</h2>
        <p>Découvrez l'ensemble de nos fonctionnalités et trouvez l'offre adaptée à vos besoins.</p>
        <a href="#" class="btn">En savoir plus</a>
      </div>

    </div>

  </section>

</body>
</html>

Correction CSS

/* fonctionnalites.css — Correction exercice 21 */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
  color: #2c2c2c;
}

.section-fonc {
  max-width: 1000px;
  margin: 0 auto;
  padding: 60px 20px;
}

.section-fonc h1 {
  color: #1A3A5C;
  font-size: 28px;
  text-align: center;
  margin-bottom: 40px;
}

/*
  flex-wrap: wrap : les cartes passent à la ligne si l'espace manque.
  align-items: stretch (défaut) : toutes les cartes d'une même ligne
  ont la même hauteur.
*/
.grille {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: stretch;
}

/* Styles communs à toutes les cartes */
.fonc {
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 10px;
  padding: 28px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

/*
  flex: 2 — la carte principale prend deux fois plus d'espace
  que les cartes secondaires (flex: 1).
  min-width évite qu'elle devienne trop étroite sur petits écrans.
*/
.fonc-principale {
  flex: 2;
  min-width: 300px;
  border-top: 4px solid #1A3A5C;
}

/*
  flex: 1 — les cartes secondaires se partagent équitablement l'espace.
  min-width force un passage à la ligne sur les petits écrans.
*/
.fonc-secondaire {
  flex: 1;
  min-width: 180px;
}

/*
  Même taille qu'une .fonc-secondaire.
  align-self: flex-end la colle au bas du conteneur.
  Sur la même ligne que les fonc-secondaire, elle apparaîtra
  alignée par le bas plutôt que par le haut (flex-start).
*/
.fonc-cta {
  flex: 1;
  min-width: 180px;
  align-self: flex-end;
  border-top: 4px solid #2E6DA4;
}

.fonc-icone {
  width: 48px;
  height: 48px;
  border-radius: 10px;
  background-color: #2E6DA4;
  color: #ffffff;
  font-size: 22px;
  font-weight: bold;
  line-height: 48px;
  text-align: center;
  margin-bottom: 16px;
}

.fonc-principale .fonc-icone {
  background-color: #1A3A5C;
}

.fonc h2 {
  font-size: 16px;
  color: #1A3A5C;
  margin-bottom: 10px;
}

.fonc p {
  font-size: 14px;
  color: #666666;
  line-height: 1.7;
}

.btn {
  display: block;
  margin-top: 20px;
  background-color: #2E6DA4;
  color: #ffffff;
  text-align: center;
  padding: 10px;
  text-decoration: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: bold;
  transition: background-color 0.2s ease;
}

.btn:hover { background-color: #1A3A5C; }

Explications et points de vigilance

flex: 2 vs flex: 1 : la carte principale reçoit deux fois plus d'espace libre que chaque carte secondaire. Si la grille a 900px d'espace libre et que les cinq cartes ont respectivement flex: 2, 1, 1, 1, 1 (soit 6 parts en tout), la principale reçoit 300px et chaque secondaire 150px supplémentaires, en plus de leur taille de base nulle.

align-self: flex-end sur .fonc-cta : alors que toutes les autres cartes sont étirées par align-items: stretch pour avoir la même hauteur, .fonc-cta surcharge cette règle et se colle au bas du conteneur. Elle garde sa hauteur naturelle et laisse un espace vide au-dessus. C'est align-self qui permet ce comportement individuel sans toucher aux autres cartes.

min-width et flex-wrap : les min-width sur chaque carte définissent le seuil en dessous duquel elles ne peuvent pas rétrécir. Combinés à flex-wrap: wrap, les cartes passent à la ligne suivante quand leur min-width ne peut plus être respectée. C'est la technique Flexbox pour créer des grilles responsives légères.

7.6 Cas d'usage courants

Cette section consolide tout ce que vous avez appris sur Flexbox en l'appliquant aux situations que vous rencontrerez systématiquement dans vos projets. Ce ne sont pas de nouveaux concepts — ce sont les mêmes propriétés, combinées intelligemment pour résoudre des problèmes concrets et récurrents.

7.6.1 Menus

La navigation est sans doute le composant où Flexbox brille le plus. Chaque type de menu a ses particularités, mais tous reposent sur les mêmes combinaisons de propriétés.

Navigation horizontale avec logo et liens. Le pattern le plus courant : logo à gauche, liens à droite. justify-content: space-between sur le conteneur fait tout le travail.

<!-- menu-horizontal.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="menu-horizontal.css">
  <title>Menu horizontal</title>
</head>
<body>

  <header class="header">
    <a href="#" class="logo">Marque</a>
    <nav class="nav-principale">
      <a href="#">Accueil</a>
      <a href="#">Services</a>
      <a href="#">Références</a>
      <a href="#">Blog</a>
      <a href="#" class="btn-contact">Contact</a>
    </nav>
  </header>

  <main style="padding: 40px; font-family: Arial, sans-serif; color: #555;">
    <p>Contenu de la page...</p>
  </main>

</body>
</html>
/* menu-horizontal.css */

* { box-sizing: border-box; margin: 0; padding: 0; }

body { font-family: Arial, sans-serif; background-color: #f4f4f4; }

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #1A3A5C;
  padding: 0 30px;
  height: 64px;
}

.logo {
  color: #ffffff;
  font-size: 20px;
  font-weight: bold;
  text-decoration: none;
  letter-spacing: 1px;
}

.nav-principale {
  display: flex;
  align-items: center;
  gap: 4px;
}

.nav-principale a {
  display: block;
  padding: 8px 16px;
  color: #a8c4e0;
  text-decoration: none;
  font-size: 14px;
  border-radius: 4px;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.nav-principale a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/* Le bouton Contact se démarque des autres liens */
.btn-contact {
  background-color: #2E6DA4;
  color: #ffffff !important;
  border-radius: 4px;
}

.btn-contact:hover {
  background-color: #3a84c4 !important;
}

Navigation avec un élément poussé à droite via margin-left: auto. Parfois, un seul élément doit être isolé à droite sans que tout le groupe de liens le soit. margin-left: auto sur cet élément consomme tout l'espace disponible et le pousse à l'extrémité droite.

<!-- menu-margin-auto.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="menu-margin-auto.css">
  <title>Menu margin auto</title>
</head>
<body>

  <header class="header">
    <a href="#" class="logo">Marque</a>
    <a href="#">Accueil</a>
    <a href="#">Services</a>
    <a href="#">Blog</a>
    <!-- margin-left: auto sur cet élément le pousse tout à droite -->
    <a href="#" class="compte">Mon compte</a>
  </header>

</body>
</html>
/* menu-margin-auto.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; }

.header {
  display: flex;
  align-items: center;
  background-color: #1A3A5C;
  padding: 0 30px;
  height: 64px;
  gap: 4px;
}

.logo {
  color: #ffffff;
  font-size: 20px;
  font-weight: bold;
  text-decoration: none;
  margin-right: 20px; /* Espace entre le logo et les liens */
}

.header a {
  display: block;
  padding: 8px 16px;
  color: #a8c4e0;
  text-decoration: none;
  font-size: 14px;
  border-radius: 4px;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.header a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/*
  margin-left: auto consomme tout l'espace horizontal disponible
  à gauche de .compte, le poussant tout à droite.
  Les autres liens restent groupés à gauche avec le logo.
*/
.compte {
  margin-left: auto;
  background-color: #2E6DA4;
  color: #ffffff !important;
}

margin: auto dans un contexte flex : dans un conteneur flex, margin: auto se comporte différemment du flux normal. Une marge auto absorbe tout l'espace disponible dans la direction concernée. margin-left: auto sur un élément le pousse à l'extrémité droite. margin: auto sur un seul élément le centre dans le conteneur. C'est une technique puissante et souvent plus simple que de diviser le conteneur en plusieurs groupes.

7.6.2 Colonnes simples

La création de colonnes côte à côte est l'un des cas d'usage les plus fréquents de Flexbox. Selon les besoins, plusieurs combinaisons de propriétés s'appliquent.

Colonnes de taille égale.

<!-- colonnes-egales.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="colonnes-egales.css">
  <title>Colonnes égales</title>
</head>
<body>

  <div class="grille">
    <div class="col">
      <h3>Colonne 1</h3>
      <p>Contenu de la première colonne. Peu importe la quantité de texte, toutes les colonnes ont la même largeur.</p>
    </div>
    <div class="col">
      <h3>Colonne 2</h3>
      <p>Contenu de la deuxième colonne avec un peu plus de texte pour illustrer l'égalisation automatique des hauteurs par Flexbox.</p>
    </div>
    <div class="col">
      <h3>Colonne 3</h3>
      <p>Contenu bref.</p>
    </div>
  </div>

</body>
</html>
/* colonnes-egales.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }

.grille {
  display: flex;
  gap: 20px;
}

/* flex: 1 = flex: 1 1 0 : toutes les colonnes partent de 0
   et se partagent l'espace équitablement. */
.col {
  flex: 1;
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 8px;
  padding: 24px;
}

.col h3 { color: #1A3A5C; margin-bottom: 10px; }
.col p  { font-size: 14px; color: #666; line-height: 1.7; }

Colonnes de taille proportionnelle.

<!-- colonnes-proportionnelles.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="colonnes-proportionnelles.css">
  <title>Colonnes proportionnelles</title>
</head>
<body>

  <div class="page">
    <main class="contenu">
      <h2>Contenu principal</h2>
      <p>Cette colonne prend les deux tiers de l'espace disponible grâce à flex: 2. Elle est naturellement plus large pour accueillir le contenu principal de la page.</p>
      <p>C'est le pattern classique d'un article avec une sidebar : le contenu principal large, la sidebar plus étroite.</p>
    </main>
    <aside class="sidebar">
      <h3>Sidebar</h3>
      <p>Un tiers de l'espace. Parfait pour des liens, des résumés ou des widgets secondaires.</p>
    </aside>
  </div>

</body>
</html>
/* colonnes-proportionnelles.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }

.page {
  display: flex;
  gap: 20px;
}

/* flex: 2 — prend 2/3 de l'espace total */
.contenu {
  flex: 2;
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  padding: 24px;
  border-radius: 8px;
}

/* flex: 1 — prend 1/3 de l'espace total */
.sidebar {
  flex: 1;
  background-color: #e8f0fe;
  border: 1px solid #c8daf0;
  padding: 24px;
  border-radius: 8px;
}

.contenu h2, .sidebar h3 { color: #1A3A5C; margin-bottom: 12px; }
.contenu p, .sidebar p { font-size: 14px; color: #555; line-height: 1.7; margin-bottom: 10px; }

Colonnes responsives avec flex-wrap.

<!-- colonnes-responsives.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="colonnes-responsives.css">
  <title>Colonnes responsives</title>
</head>
<body>

  <div class="grille">
    <div class="carte">Carte 1</div>
    <div class="carte">Carte 2</div>
    <div class="carte">Carte 3</div>
    <div class="carte">Carte 4</div>
    <div class="carte">Carte 5</div>
    <div class="carte">Carte 6</div>
  </div>

</body>
</html>
/* colonnes-responsives.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }

.grille {
  display: flex;
  flex-wrap: wrap; /* Les cartes passent à la ligne si l'espace manque */
  gap: 16px;
}

/*
  flex: 1 1 200px :
  - grandit pour remplir l'espace disponible
  - rétrécit si nécessaire
  - mais jamais en dessous de 200px (flex-basis)
  Résultat : sur grand écran, 3-4 cartes par ligne.
  Sur petit écran, 2 cartes, puis 1 seule. Sans media query.
*/
.carte {
  flex: 1 1 200px;
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 30px 20px;
  text-align: center;
  border-radius: 8px;
  font-weight: bold;
  font-size: 15px;
}

7.6.3 Alignements fréquents

Voici les patterns d'alignement les plus utilisés en production, avec leur implémentation directe.

Centrage complet dans une zone. Centrer un élément horizontalement et verticalement dans son conteneur — le cas le plus demandé.

<!-- centrage-complet.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="centrage-complet.css">
  <title>Centrage complet</title>
</head>
<body>

  <div class="hero">
    <div class="hero-contenu">
      <h1>Bienvenue</h1>
      <p>Un message centré horizontalement et verticalement dans la zone.</p>
      <a href="#" class="btn">Commencer</a>
    </div>
  </div>

  <div class="icone-wrapper">
    <span class="icone">★</span>
  </div>

</body>
</html>
/* centrage-complet.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; background-color: #f4f4f4; }

/* Centrage d'un bloc de contenu dans une grande zone */
.hero {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 300px;
  background-color: #1A3A5C;
  text-align: center;
}

.hero-contenu h1 { color: #fff; font-size: 32px; margin-bottom: 12px; }
.hero-contenu p  { color: #a8c4e0; font-size: 15px; margin-bottom: 20px; }

.btn {
  display: inline-block;
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 10px 28px;
  text-decoration: none;
  border-radius: 4px;
  font-weight: bold;
}

/* Centrage d'une icône dans un carré */
.icone-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80px;
  height: 80px;
  background-color: #2E6DA4;
  border-radius: 50%;
  margin: 30px auto 0;
}

.icone {
  color: #ffffff;
  font-size: 32px;
}

Éléments d'une carte alignés en bas. Dans une liste de cartes avec des contenus inégaux, le bouton doit toujours apparaître en bas de la carte, quelle que soit la quantité de texte.

<!-- carte-bouton-bas.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="carte-bouton-bas.css">
  <title>Bouton en bas de carte</title>
</head>
<body>

  <div class="grille">

    <div class="carte">
      <h3>Offre Essentielle</h3>
      <p>Une courte description.</p>
      <a href="#" class="btn-carte">Choisir</a>
    </div>

    <div class="carte">
      <h3>Offre Professionnelle</h3>
      <p>Une description beaucoup plus longue qui occupe plusieurs lignes et qui repousse naturellement le bouton vers le bas de la carte, créant un alignement inégal sans technique particulière.</p>
      <a href="#" class="btn-carte">Choisir</a>
    </div>

    <div class="carte">
      <h3>Offre Entreprise</h3>
      <p>Description de longueur intermédiaire pour cette offre dédiée aux grandes structures.</p>
      <a href="#" class="btn-carte">Choisir</a>
    </div>

  </div>

</body>
</html>
/* carte-bouton-bas.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; padding: 30px; background-color: #f4f4f4; }

/* Le conteneur de cartes */
.grille {
  display: flex;
  gap: 20px;
  align-items: stretch; /* Toutes les cartes ont la même hauteur */
}

/*
  Chaque carte est elle-même un conteneur flex en colonne.
  Le contenu (titre, texte) s'empile normalement.
  margin-top: auto sur le bouton consomme tout l'espace vertical restant
  et le colle au bas de la carte, quelle que soit la hauteur du texte.
*/
.carte {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 8px;
  padding: 24px;
}

.carte h3 {
  color: #1A3A5C;
  font-size: 16px;
  margin-bottom: 12px;
}

.carte p {
  font-size: 14px;
  color: #666;
  line-height: 1.7;
  margin-bottom: 0;
}

/* margin-top: auto pousse le bouton tout en bas de la carte */
.btn-carte {
  display: block;
  margin-top: auto;
  padding-top: 20px; /* Espace minimal entre le texte et le bouton */
  color: #2E6DA4;
  text-decoration: none;
  font-weight: bold;
  font-size: 14px;
  border-top: 1px solid #e8e8e8;
  margin-left: -24px;
  margin-right: -24px;
  padding: 14px 24px 0;
  transition: color 0.2s ease;
}

.btn-carte:hover { color: #1A3A5C; }

margin-top: auto pour pousser vers le bas : dans un conteneur flex en colonne (flex-direction: column), margin-top: auto sur un élément consomme tout l'espace vertical disponible au-dessus de lui. C'est la technique la plus propre pour garder un bouton en bas d'une carte quelle que soit la hauteur du contenu. Elle est préférable à position: absolute qui sort l'élément du flux.

Exercice 22 — La barre de navigation complète

Objectif : construire une barre de navigation avec trois zones distinctes : un logo à gauche, des liens au centre, et un groupe d'actions (icône utilisateur + bouton) à droite. L'exercice travaille les différentes techniques de distribution avec justify-content, margin-left: auto et le flex imbriqué pour les sous-groupes.

Consignes HTML :

  • Créez un fichier navbar-complete.html.
  • Créez un <header class="navbar"> contenant :
    • Un <a class="logo"> avec le nom du site.
    • Une <nav class="liens-centre"> avec quatre <a>.
    • Un <div class="actions"> contenant un <span class="user"> (initiale de l'utilisateur) et un <a class="btn-app"> avec le texte Tableau de bord.
  • Créez un <main> avec quelques lignes de contenu.
  • Liez un fichier navbar-complete.css.

Consignes :

  • La .navbar est un conteneur flex. Le .logo est à gauche, .liens-centre est centré dans la navbar, et .actions est à droite.
  • .liens-centre et .actions sont eux-mêmes des conteneurs flex.
  • Le .user est un cercle avec une initiale, stylisé comme un avatar.
  • Tous les éléments de la navbar sont centrés verticalement.
  • Trouvez comment centrer .liens-centre entre le logo et les actions tout en gardant les actions à droite.

Correction détaillée — Exercice 22

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="navbar-complete.css">
  <title>Navbar complète</title>
</head>
<body>

  <header class="navbar">
    <a href="#" class="logo">Plateforme</a>

    <nav class="liens-centre">
      <a href="#">Découvrir</a>
      <a href="#">Solutions</a>
      <a href="#">Tarifs</a>
      <a href="#">Documentation</a>
    </nav>

    <div class="actions">
      <span class="user">M</span>
      <a href="#" class="btn-app">Tableau de bord</a>
    </div>
  </header>

  <main class="page-contenu">
    <h1>Bienvenue sur la plateforme</h1>
    <p>Ceci est le contenu principal de la page.</p>
  </main>

</body>
</html>

Correction CSS

/* navbar-complete.css — Correction exercice 22 */

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
}

/*
  La technique pour centrer .liens-centre entre le logo et les actions :
  donner à .logo et .actions la même flex: 1.
  .logo prend 1/3 de l'espace, .liens-centre prend 1/3, .actions prend 1/3.
  En centrant le contenu de .logo à gauche et de .actions à droite,
  .liens-centre reste naturellement centré au milieu.

  Alternative avec margin auto : margin-left: auto sur .liens-centre
  et margin-left: auto sur .actions — mais cela ne centre pas parfaitement.
*/
.navbar {
  display: flex;
  align-items: center;
  background-color: #1A3A5C;
  padding: 0 24px;
  height: 64px;
}

.logo {
  flex: 1; /* Prend 1/3 de la largeur */
  color: #ffffff;
  font-size: 18px;
  font-weight: bold;
  text-decoration: none;
  letter-spacing: 1px;
}

/* .liens-centre est un conteneur flex pour ses propres liens */
.liens-centre {
  flex: 1; /* Prend 1/3 de la largeur */
  display: flex;
  justify-content: center; /* Centre les liens dans ce tiers */
  align-items: center;
  gap: 2px;
}

.liens-centre a {
  display: block;
  padding: 8px 14px;
  color: #a8c4e0;
  text-decoration: none;
  font-size: 14px;
  border-radius: 4px;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.liens-centre a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/* .actions est un conteneur flex aligné à droite */
.actions {
  flex: 1; /* Prend 1/3 de la largeur */
  display: flex;
  justify-content: flex-end; /* Pousse le contenu à droite dans son tiers */
  align-items: center;
  gap: 12px;
}

.user {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background-color: #2E6DA4;
  color: #ffffff;
  font-weight: bold;
  font-size: 15px;
  flex-shrink: 0; /* Ne rétrécit jamais */
}

.btn-app {
  display: block;
  padding: 7px 16px;
  background-color: #2E6DA4;
  color: #ffffff;
  text-decoration: none;
  border-radius: 4px;
  font-size: 13px;
  font-weight: bold;
  white-space: nowrap;
  transition: background-color 0.2s ease;
}

.btn-app:hover { background-color: #3a84c4; }

.page-contenu {
  max-width: 800px;
  margin: 60px auto;
  padding: 0 20px;
  color: #2c2c2c;
}

.page-contenu h1 { color: #1A3A5C; margin-bottom: 12px; }
.page-contenu p  { font-size: 15px; line-height: 1.7; color: #555; }

Explications et points de vigilance

Centrer .liens-centre entre le logo et les actions : la technique consiste à donner flex: 1 aux trois éléments directs de la navbar — logo, liens, actions — pour qu'ils se partagent chacun un tiers de la largeur totale. Ensuite, justify-content: center sur .liens-centre centre ses liens dans son tiers, et justify-content: flex-end sur .actions pousse son contenu à droite dans son tiers. Le logo, étant un élément flex seul dans son tiers, s'aligne naturellement à gauche.

flex-shrink: 0 sur .user : l'avatar est un cercle de taille fixe. Sans flex-shrink: 0, il pourrait se déformer sur les petits écrans. C'est le même principe que pour la sidebar de l'exercice 20.

Erreur fréquente : utiliser uniquement justify-content: space-between sur la navbar pour les trois zones. Cela fonctionne pour avoir logo à gauche et actions à droite, mais ne centre pas parfaitement .liens-centre — il se retrouverait calé à gauche des actions plutôt qu'au milieu géométrique de la navbar.

Exercice 23 — La page de résultats de recherche

Objectif : construire une page de résultats de recherche avec une barre de recherche en haut, des filtres en colonne à gauche, et une liste de résultats à droite. Chaque résultat est une carte avec un titre, une description et des métadonnées alignées en bas. L'exercice mobilise l'ensemble des propriétés vues dans le module : layout en colonnes, flex imbriqué, flex-direction: column avec margin-top: auto et flex-wrap.

Consignes HTML :

  • Créez un fichier recherche.html.
  • Créez un <header> avec une <div class="barre-recherche"> contenant un <input> et un <button>.
  • Créez un <div class="layout"> contenant :
    • Un <aside class="filtres"> avec un <h2> et trois <div class="groupe-filtre">, chacun avec un <h3> et trois <label> contenant chacun un <input type="checkbox"> et un texte.
    • Un <section class="resultats"> avec quatre <article class="resultat">. Chaque article contient un <h2>, un <p class="description"> et une <div class="meta"> avec un <span class="source"> et un <span class="date">.
  • Liez un fichier recherche.css.

Consignes :

  • La .barre-recherche est un conteneur flex : l'<input> prend tout l'espace disponible, le <button> garde sa taille naturelle.
  • .layout est un conteneur flex : .filtres est à largeur fixe et ne rétrécit jamais, .resultats prend le reste.
  • .resultats est un conteneur flex avec flex-wrap: wrap : les .resultat font environ 45% de large.
  • Chaque .resultat est un conteneur flex en colonne. La .meta (source + date) est toujours collée en bas grâce à margin-top: auto.
  • .meta est un conteneur flex avec source à gauche et date à droite.

Correction détaillée — Exercice 23

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="recherche.css">
  <title>Résultats de recherche</title>
</head>
<body>

  <header class="header-recherche">
    <div class="barre-recherche">
      <input type="text" placeholder="Rechercher un article, un tutoriel..." value="Flexbox CSS">
      <button>Rechercher</button>
    </div>
  </header>

  <div class="layout">

    <aside class="filtres">
      <h2>Filtres</h2>

      <div class="groupe-filtre">
        <h3>Type</h3>
        <label><input type="checkbox" checked> Tutoriel</label>
        <label><input type="checkbox"> Vidéo</label>
        <label><input type="checkbox"> Article</label>
      </div>

      <div class="groupe-filtre">
        <h3>Niveau</h3>
        <label><input type="checkbox" checked> Débutant</label>
        <label><input type="checkbox" checked> Intermédiaire</label>
        <label><input type="checkbox"> Avancé</label>
      </div>

      <div class="groupe-filtre">
        <h3>Langue</h3>
        <label><input type="checkbox" checked> Français</label>
        <label><input type="checkbox"> Anglais</label>
        <label><input type="checkbox"> Espagnol</label>
      </div>
    </aside>

    <section class="resultats">

      <article class="resultat">
        <h2>Flexbox en pratique : construire une navbar responsive</h2>
        <p class="description">Découvrez comment créer une barre de navigation moderne et responsive avec Flexbox. Nous couvrons justify-content, align-items et les patterns les plus courants en production.</p>
        <div class="meta">
          <span class="source">css-tricks.fr</span>
          <span class="date">12 nov. 2024</span>
        </div>
      </article>

      <article class="resultat">
        <h2>Guide complet des propriétés Flexbox</h2>
        <p class="description">Référence exhaustive de toutes les propriétés Flexbox avec des exemples interactifs. De flex-grow à align-self, tout ce que vous devez savoir.</p>
        <div class="meta">
          <span class="source">devdocs.io</span>
          <span class="date">3 oct. 2024</span>
        </div>
      </article>

      <article class="resultat">
        <h2>Flexbox vs CSS Grid : quand utiliser lequel ?</h2>
        <p class="description">Une comparaison détaillée des deux systèmes de mise en page modernes. Cas d'usage, avantages, limites et recommandations pour choisir le bon outil.</p>
        <div class="meta">
          <span class="source">alsacreations.com</span>
          <span class="date">28 sept. 2024</span>
        </div>
      </article>

      <article class="resultat">
        <h2>Les pièges de Flexbox à éviter</h2>
        <p class="description">Analyse des erreurs les plus fréquentes avec Flexbox : mauvaise direction des axes, confusion justify-content / align-items, oubli de flex-shrink sur les éléments fixes.</p>
        <div class="meta">
          <span class="source">grafikart.fr</span>
          <span class="date">15 août 2024</span>
        </div>
      </article>

    </section>

  </div>

</body>
</html>

Correction CSS

/* recherche.css — Correction exercice 23 */

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
  color: #2c2c2c;
}

/* Header avec barre de recherche */
.header-recherche {
  background-color: #1A3A5C;
  padding: 20px 30px;
}

/*
  La barre de recherche est un conteneur flex.
  L'input reçoit flex: 1 pour prendre tout l'espace disponible.
  Le bouton garde sa taille naturelle (flex-grow: 0 par défaut).
*/
.barre-recherche {
  display: flex;
  max-width: 700px;
  margin: 0 auto;
  gap: 0;
}

.barre-recherche input {
  flex: 1;
  padding: 12px 16px;
  font-size: 15px;
  border: none;
  border-radius: 4px 0 0 4px;
  outline: none;
}

.barre-recherche button {
  padding: 12px 24px;
  background-color: #2E6DA4;
  color: #ffffff;
  border: none;
  border-radius: 0 4px 4px 0;
  font-size: 15px;
  font-weight: bold;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.2s ease;
}

.barre-recherche button:hover { background-color: #3a84c4; }

/* Layout principal */
.layout {
  display: flex;
  max-width: 1100px;
  margin: 0 auto;
  padding: 30px 20px;
  gap: 30px;
}

/*
  flex: 0 0 220px : la sidebar de filtres est fixe à 220px,
  ne grandit pas, ne rétrécit pas.
*/
.filtres {
  flex: 0 0 220px;
  align-self: flex-start; /* Ne s'étire pas sur toute la hauteur du layout */
}

.filtres h2 {
  color: #1A3A5C;
  font-size: 16px;
  margin-bottom: 20px;
  padding-bottom: 10px;
  border-bottom: 2px solid #2E6DA4;
}

.groupe-filtre {
  margin-bottom: 20px;
}

.groupe-filtre h3 {
  font-size: 13px;
  color: #888888;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 8px;
}

.groupe-filtre label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #444444;
  padding: 4px 0;
  cursor: pointer;
}

/* Résultats */
.resultats {
  flex: 1; /* Prend tout l'espace restant après les filtres */
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-content: flex-start;
}

/*
  Chaque résultat fait environ 45% pour tenir deux par ligne.
  flex-direction: column pour empiler titre, description et meta.
  La meta sera poussée en bas par margin-top: auto.
*/
.resultat {
  flex: 1 1 45%;
  display: flex;
  flex-direction: column;
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 8px;
  padding: 20px;
  transition: box-shadow 0.2s ease;
}

.resultat:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}

.resultat h2 {
  font-size: 16px;
  color: #1A3A5C;
  margin-bottom: 10px;
  line-height: 1.4;
}

.resultat h2:hover { text-decoration: underline; cursor: pointer; }

.description {
  font-size: 13px;
  color: #666666;
  line-height: 1.7;
  margin-bottom: 0;
}

/*
  margin-top: auto pousse .meta en bas de la carte,
  quelle que soit la longueur de la description.
  .meta est elle-même un conteneur flex pour aligner
  source à gauche et date à droite.
*/
.meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid #f0f0f0;
  margin-left: -20px;
  margin-right: -20px;
  padding-left: 20px;
  padding-right: 20px;
}

.source {
  font-size: 12px;
  color: #2E6DA4;
  font-weight: bold;
}

.date {
  font-size: 12px;
  color: #aaaaaa;
}

Explications et points de vigilance

flex: 1 sur l'<input> de la barre de recherche : l'input absorbe tout l'espace disponible, laissant le bouton à sa taille naturelle. C'est le pattern barre de recherche par excellence — simple et robuste.

align-self: flex-start sur .filtres : sans cette propriété, les filtres s'étireraient sur toute la hauteur de .layout grâce à align-items: stretch (défaut). Avec flex-start, la sidebar garde sa hauteur naturelle et ne s'étire pas inutilement.

flex-direction: column + margin-top: auto sur .resultat : c'est le pattern bouton/meta collé en bas de carte. La carte s'étire verticalement pour avoir la même hauteur que sa voisine (grâce à stretch), et margin-top: auto sur .meta consomme tout l'espace vertical restant au-dessus d'elle.

align-content: flex-start sur .resultats : quand il y a plusieurs lignes (avec flex-wrap), align-content contrôle comment ces lignes sont distribuées verticalement. flex-start groupe les lignes en haut plutôt que de les étirer sur toute la hauteur — ce qui évite un espacement excessif entre les deux lignes de résultats.

8. Responsive design et adaptation aux écrans

8.1 Enjeux du responsive design

Un site web n'est plus consulté depuis un seul type d'appareil. Concevoir une interface, c'est aujourd'hui concevoir pour une mosaïque d'écrans dont vous ne contrôlez ni la taille, ni la densité de pixels, ni les conditions d'utilisation. Le responsive design est la discipline qui consiste à faire en sorte qu'une même page HTML s'adapte correctement à tous ces contextes, sans qu'il soit nécessaire de maintenir plusieurs versions du site.

8.1.1 Multiplicité des écrans

Les statistiques mondiales de navigation montrent une réalité tranchée : plus de la moitié du trafic web mondial provient aujourd'hui d'appareils mobiles. Cette proportion varie selon les secteurs — elle peut dépasser 80% pour les médias grand public et descendre sous 30% pour certains outils professionnels — mais la tendance de fond est structurelle et irréversible.

Concrètement, un même fichier HTML peut être affiché sur :

  • Un smartphone d'entrée de gamme avec un écran de 320px de large et une connexion 3G lente.
  • Un téléphone récent avec un écran de 390 à 430px et un ratio de pixels de 3 (chaque pixel CSS représente 3 pixels physiques).
  • Une tablette en mode portrait à 768px ou en mode paysage à 1024px.
  • Un ordinateur portable avec un écran de 1280 à 1440px.
  • Un moniteur de bureau large à 1920px ou plus.
  • Une télévision connectée ou un écran 4K à 2560px et au-delà.

À cela s'ajoutent des variables que la largeur seule ne suffit pas à décrire :

L'orientation. Un même téléphone peut être tenu en portrait (étroit et long) ou en paysage (large et court). Les deux orientations produisent des expériences très différentes.

La densité de pixels. Les écrans modernes, notamment sur mobile, ont une densité très élevée (Retina, AMOLED). Une image de 100px CSS peut s'afficher sur 200 ou 300 pixels physiques. Une image raster non adaptée apparaîtra floue sur ces écrans.

Les capacités d'interaction. Un écran tactile n'a pas de curseur hoverable. Les cibles interactives (boutons, liens) doivent être dimensionnées pour le doigt, soit au minimum 44px de hauteur, et non pour un pointeur de souris précis au pixel.

La luminosité ambiante et la lisibilité. Un site consulté en plein soleil sur mobile impose des contrastes élevés. Un site consulté la nuit dans l'obscurité peut bénéficier d'un mode sombre.

Le responsive design n'est pas un gadget. Un site non responsive est pénalisé par les moteurs de recherche depuis 2015 (Google Mobile-First Indexing). Il est inutilisable pour une fraction croissante de vos utilisateurs. Et il signale un manque de soin professionnel. Aujourd'hui, la capacité d'adaptation aux écrans est une exigence de base, pas une option.

8.1.2 Contraintes techniques

Rendre un site responsive ne se résume pas à "rétrécir la mise en page". Cela implique de résoudre plusieurs contraintes techniques qui se manifestent différemment selon les écrans.

Contrainte 1 : la largeur disponible est inconnue et variable. Contrairement à une mise en page imprimée où le format papier est fixe, une page web peut être affichée dans n'importe quelle largeur. Une mise en page rigide en pixels fixes est condamnée à être soit trop large (scroll horizontal sur mobile), soit trop étroite (espace gâché sur grand écran). La réponse technique est l'utilisation d'unités relatives (%, em, rem, vw) et de contraintes souples (max-width, min-width) plutôt que de valeurs absolues.

Contrainte 2 : la quantité de contenu peut déborder. Sur un écran large, une navigation horizontale avec dix liens tient sur une ligne. Sur mobile, ces mêmes liens débordent. Un tableau de données à douze colonnes est lisible sur desktop, illisible sur un écran de 375px. Le responsive design impose de repenser l'organisation du contenu, pas seulement sa taille.

Contrainte 3 : les performances réseau. Un utilisateur mobile sur une connexion 4G — ou pire, 3G — n'a pas les mêmes capacités de chargement qu'un utilisateur sur fibre optique. Charger une image de 4 Mo sur mobile est inacceptable. Le responsive design s'accompagne donc d'une réflexion sur les images adaptatives, les polices optimisées et la réduction du poids des ressources selon le contexte.

Contrainte 4 : le viewport et son comportement par défaut. Sans instruction spécifique, les navigateurs mobiles appliquent un comportement hérité : ils affichent la page comme si elle avait environ 980px de large, puis la rétrécissent pour qu'elle tienne dans l'écran — rendant le texte illisible et les boutons inatteignables. Pour désactiver ce comportement et indiquer au navigateur que la page est conçue pour mobile, la balise <meta name="viewport"> est indispensable.

<!-- Cette balise doit être présente dans le <head> de toutes vos pages -->
<meta name="viewport" content="width=device-width, initial-scale=1">

<!--
  width=device-width : la largeur de la page correspond à la largeur réelle
  de l'écran, en pixels CSS (et non en pixels physiques).

  initial-scale=1 : pas de zoom appliqué au chargement de la page.

  Sans cette balise, vos media queries ne fonctionneront pas correctement
  sur mobile — c'est l'erreur de débutant numéro un en responsive design.
-->

Contrainte 5 : la complexité de maintenance. Une approche naïve du responsive design consiste à écrire une mise en page desktop complète, puis à la "réparer" pour chaque taille d'écran en ajoutant des media queries. Cette approche produit un CSS difficile à maintenir, truffé d'annulations de propriétés. L'alternative est d'adopter une stratégie de conception cohérente dès le départ — mobile-first ou desktop-first — que nous allons détailler dans la section 8.2.

8.2 Approches de conception

Il existe deux stratégies opposées pour aborder le responsive design. Le choix entre les deux n'est pas anodin : il détermine la structure de votre CSS, l'ordre dans lequel vous écrivez vos règles, et la facilité de maintenance à long terme. Connaître les deux vous permettra de choisir la bonne approche selon le contexte du projet, et de lire et comprendre le code d'autrui quelle que soit la stratégie adoptée.

8.2.1 Mobile-first

L'approche mobile-first consiste à écrire en premier le CSS pour les petits écrans — le mobile — puis à ajouter des règles pour les écrans plus larges à mesure que la largeur augmente. En pratique, cela signifie que votre CSS de base, sans aucune media query, est déjà fonctionnel et soigné sur mobile. Les media queries ne servent qu'à enrichir la mise en page pour les écrans plus grands.

/* Approche mobile-first : le CSS de base cible le mobile */

.grille {
  /* Sur mobile : les cartes s'empilent en une seule colonne */
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* À partir de 768px (tablette) : deux colonnes */
@media (min-width: 768px) {
  .grille {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .carte {
    flex: 1 1 45%;
  }
}

/* À partir de 1024px (desktop) : trois colonnes */
@media (min-width: 1024px) {
  .carte {
    flex: 1 1 30%;
  }
}

Les media queries mobile-first utilisent min-width : "à partir de cette largeur, applique ces règles supplémentaires". L'ensemble des styles de base reste actif et n'est jamais annulé — on ne fait qu'ajouter.

Pourquoi le mobile-first est aujourd'hui l'approche recommandée.

Premièrement, elle force à prioriser le contenu. Sur un écran de 375px, vous ne pouvez pas tout afficher. Vous devez décider ce qui est essentiel. Cette contrainte est saine : elle produit des interfaces plus claires et mieux hiérarchisées, y compris sur desktop.

Deuxièmement, elle produit un CSS plus léger sur mobile. Un appareil mobile ne charge que le CSS de base, sans devoir parser et ignorer des règles desktop inutiles. C'est un gain de performance modeste mais réel.

Troisièmement, elle s'aligne avec la manière dont Google indexe les pages depuis 2019 (Mobile-First Indexing). Google analyse en priorité la version mobile de votre site. Si cette version est négligée, le référencement en pâtit.

Quatrièmement, elle produit un CSS plus propre. Ajouter des propriétés est plus simple qu'en annuler. En mobile-first, vous ne passez pas votre temps à écrire display: block pour annuler un display: flex défini pour desktop.

Mobile-first ne signifie pas "concevoir uniquement pour mobile". Cela signifie commencer par le mobile et enrichir progressivement. Le résultat final sur desktop est aussi soigné — souvent davantage, car la réflexion sur le contenu essentiel a été faite en amont.

8.2.2 Desktop-first

L'approche desktop-first est l'inverse : vous écrivez d'abord le CSS pour les grands écrans, puis vous ajoutez des règles pour adapter la mise en page aux écrans plus petits. Les media queries utilisent max-width : "jusqu'à cette largeur, applique ces règles qui annulent ou remplacent le comportement desktop".

/* Approche desktop-first : le CSS de base cible le desktop */

.grille {
  /* Sur desktop : trois colonnes côte à côte */
  display: flex;
  gap: 20px;
}

.carte {
  flex: 1;
}

/* En dessous de 1024px (tablette) : deux colonnes */
@media (max-width: 1024px) {
  .grille {
    flex-wrap: wrap;
  }

  .carte {
    flex: 1 1 45%;
  }
}

/* En dessous de 768px (mobile) : une seule colonne */
@media (max-width: 768px) {
  .grille {
    flex-direction: column;
  }
}

Quand le desktop-first reste pertinent.

Pour des projets dont l'audience est exclusivement ou massivement desktop — outils de gestion interne, logiciels de comptabilité, interfaces d'administration complexes — le desktop-first est une approche pragmatique. Si vos utilisateurs n'accèdent jamais au produit sur mobile, optimiser d'abord pour desktop est une allocation de temps raisonnable.

Le desktop-first est aussi courant dans les projets existants. Si vous reprenez un codebase écrit en desktop-first depuis des années, le convertir en mobile-first représente une réécriture massive. Dans ce cas, continuer en desktop-first et améliorer progressivement la version mobile est souvent plus sage.

Les pièges du desktop-first.

Le principal problème est la tendance à accumuler des annulations. Une règle définie pour desktop doit être explicitement annulée pour mobile. Sur une base de code importante, cela produit des cascades d'annulations difficiles à suivre :

/* Desktop-first : on annule des propriétés en cascade */

.sidebar {
  display: block;
  width: 280px;
  float: left;
}

@media (max-width: 768px) {
  .sidebar {
    display: none;  /* Annulation */
    width: auto;    /* Annulation */
    float: none;    /* Annulation */
  }
}

/* En mobile-first, .sidebar n'existerait pas par défaut sur mobile,
   et on l'ajouterait seulement à partir du breakpoint desktop. */

Tableau comparatif des deux approches :

Media query utilisée

min-width

max-width

CSS de base cible

Mobile

Desktop

Direction

On ajoute en élargissant

On annule en rétrécissant

Recommandé pour

La majorité des projets web

Outils internes, apps desktop

Aligne avec Google

Oui

Non

Risque principal

Oublier de soigner le desktop

Accumuler des annulations

En pratique, les deux approches peuvent coexister dans un même projet. On peut écrire le layout global en mobile-first et certains composants complexes en desktop-first si leur conception part naturellement du grand écran. L'important est d'être cohérent à l'échelle d'un composant, et d'en documenter le choix dans les commentaires.

Mobile first

Desktop first

Exercice 25 — Construire une page en mobile-first

Objectif : construire une page de tableau de bord en approche mobile-first, sans media query. Le CSS de base doit être fonctionnel et lisible à 375px de large. Cet exercice travaille la discipline de penser le CSS à partir du plus petit écran : quelles largeurs deviennent relatives, quels éléments s'empilent par défaut, qu'est-ce qui est vraiment nécessaire sur mobile.

Consignes HTML :

Créez un fichier mobile-first.html avec la balise <meta name="viewport" content="width=device-width, initial-scale=1"> dans le <head>.

Créez un <header> contenant une <div class="navbar"> avec un <span class="logo"> (texte : DashApp) et une <nav> avec quatre <a> : Accueil, Rapports, Équipe, Paramètres.

Créez un <div class="page"> contenant :

Un <main class="contenu"> avec un <h1> (Tableau de bord), deux <p> de texte, et une <div class="stats"> contenant trois <div class="stat"> — chacun avec un <span class="stat-valeur"> (un nombre) et un <span class="stat-label"> (un libellé).

Un <aside class="sidebar"> avec un <h2> et trois <p> de texte secondaire.

Créez un <footer> avec un <p> de copyright.

Liez un fichier mobile-first.css.

Consignes CSS :

Réfléchissez à chaque décision comme si vous étiez sur un écran de 375px. Aucune media query dans ce fichier.

  • Conteneurs principaux (.navbar, .page) : ils ne doivent jamais dépasser une certaine largeur sur les grands écrans, mais doivent se rétrécir librement sur les petits. Comment définir une largeur qui se comporte ainsi ? Comment les centrer sur la page ?

  • Mise en page de .page : sur mobile, le contenu principal et la sidebar doivent apparaître l'un sous l'autre dans un ordre logique pour un lecteur mobile — le contenu d'abord, la sidebar ensuite. Quelle est la technique la plus simple pour obtenir cet empilement sans écrire de propriété de positionnement ? Pourquoi serait-il une erreur d'utiliser Flexbox ici dans le CSS de base ?

  • La navbar (.navbar) : le logo doit rester visible et ne jamais rétrécir. Les liens de navigation peuvent se comprimer si l'espace manque, mais ne doivent pas créer de scroll horizontal. Comment garantir ces deux comportements ?

  • Les statistiques (.stats et .stat) : les trois blocs de statistiques doivent se partager l'espace disponible équitablement, quelle que soit la largeur de l'écran. Si l'espace devient vraiment insuffisant, ils doivent pouvoir passer à la ligne. Comment définir leur taille pour qu'elle soit à la fois flexible et dotée d'un minimum raisonnable ?

  • Typographie : les tailles de police ne doivent pas être figées en pixels. Quelle unité utiliseriez-vous pour respecter les préférences d'accessibilité de l'utilisateur tout en gardant des proportions cohérentes ?

  • Vérification finale : ouvrez les DevTools de votre navigateur, passez en mode mobile et réglez la largeur à 375px. Aucun élément ne doit dépasser la largeur de l'écran et créer un scroll horizontal. Si c'est le cas, cherchez quel élément a une largeur trop rigide.

Correction détaillée — Exercice 25

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="mobile-first.css">
  <title>Tableau de bord — Mobile First</title>
</head>
<body>

  <header>
    <div class="navbar">
      <span class="logo">DashApp</span>
      <nav>
        <a href="#">Accueil</a>
        <a href="#">Rapports</a>
        <a href="#">Équipe</a>
        <a href="#">Paramètres</a>
      </nav>
    </div>
  </header>

  <div class="page">

    <main class="contenu">
      <h1>Tableau de bord</h1>
      <p>Voici les indicateurs clés de performance pour le mois en cours. Ces données sont mises à jour quotidiennement à partir de nos sources analytiques.</p>
      <p>Retrouvez le détail de chaque indicateur dans les rapports hebdomadaires disponibles dans votre espace personnel.</p>

      <div class="stats">
        <div class="stat">
          <span class="stat-valeur">12 480</span>
          <span class="stat-label">Visiteurs</span>
        </div>
        <div class="stat">
          <span class="stat-valeur">847</span>
          <span class="stat-label">Conversions</span>
        </div>
        <div class="stat">
          <span class="stat-valeur">6,8 %</span>
          <span class="stat-label">Taux</span>
        </div>
      </div>
    </main>

    <aside class="sidebar">
      <h2>Infos</h2>
      <p>Dernière mise à jour : 02 mars 2024.</p>
      <p>Rapport mensuel disponible en téléchargement depuis la section Documents.</p>
      <p>Support disponible du lundi au vendredi, de 9h à 18h.</p>
    </aside>

  </div>

  <footer>
    <p>&copy; 2024 DashApp. Tous droits réservés.</p>
  </footer>

</body>
</html>

Correction CSS

/* mobile-first.css — Correction exercice 25
   CSS de base : mobile en premier, sans aucune media query.
   Ce fichier sera enrichi de media queries dans les exercices suivants. */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  font-size: 1rem; /* Unité relative : respecte les préférences système */
  background-color: #f0f4f8;
  color: #2c2c2c;
}

/* --- HEADER --- */

header {
  background-color: #1A3A5C;
}

/*
  max-width + margin: 0 auto : la navbar ne dépasse jamais 960px,
  mais se rétrécit librement en dessous — elle ne déborde jamais.
*/
.navbar {
  max-width: 960px;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 16px;
  height: 56px;
}

.logo {
  color: #ffffff;
  font-size: 1.125rem;
  font-weight: bold;
  flex-shrink: 0; /* Le logo ne rétrécit jamais */
  margin-right: 12px;
}

.navbar nav {
  display: flex;
  gap: 2px;
  overflow: hidden; /* Les liens qui dépassent sont masqués, pas de scroll */
}

.navbar nav a {
  display: block;
  padding: 6px 10px;
  color: #a8c4e0;
  text-decoration: none;
  font-size: 0.875rem;
  border-radius: 4px;
  white-space: nowrap;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.navbar nav a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/* --- LAYOUT PRINCIPAL --- */

.page {
  max-width: 960px;
  margin: 0 auto;
  padding: 20px 16px;
  /*
    Pas de display: flex ici.
    Sur mobile, .contenu et .sidebar s'empilent naturellement
    dans le flux normal — c'est exactement ce qu'on veut.
    Ajouter display: flex avec flex-direction: column serait
    du desktop-first déguisé : on partirait d'un état flex
    qu'on modifierait ensuite. Le flux normal fait le travail
    gratuitement. On ajoutera display: flex en media query (section 8.3).
  */
}

/* --- CONTENU PRINCIPAL --- */

.contenu {
  background-color: #ffffff;
  padding: 20px 16px;
  border: 1px solid #dde3ed;
  margin-bottom: 16px;
}

.contenu h1 {
  color: #1A3A5C;
  font-size: 1.375rem;
  margin-bottom: 14px;
}

.contenu p {
  font-size: 0.9375rem;
  line-height: 1.7;
  color: #555;
  margin-bottom: 12px;
}

/* --- STATS --- */

.stats {
  display: flex;
  gap: 10px;
  margin-top: 20px;
  flex-wrap: wrap; /* Les stats passent à la ligne si l'espace manque */
}

/*
  flex: 1 1 80px : chaque stat grandit équitablement
  et descend au minimum à 80px avant de passer à la ligne.
*/
.stat {
  flex: 1 1 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #e8f0fe;
  border: 1px solid #c8daf0;
  padding: 14px 10px;
  border-radius: 8px;
}

.stat-valeur {
  font-size: 1.25rem;
  font-weight: bold;
  color: #1A3A5C;
}

.stat-label {
  font-size: 0.75rem;
  color: #666;
  margin-top: 4px;
  text-align: center;
}

/* --- SIDEBAR --- */

.sidebar {
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  padding: 20px 16px;
}

.sidebar h2 {
  color: #1A3A5C;
  font-size: 1rem;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 2px solid #2E6DA4;
}

.sidebar p {
  font-size: 0.875rem;
  color: #555;
  line-height: 1.6;
  margin-bottom: 10px;
}

.sidebar p:last-child {
  margin-bottom: 0;
}

/* --- FOOTER --- */

footer {
  background-color: #1A3A5C;
  color: #a8c4e0;
  text-align: center;
  padding: 16px;
  font-size: 0.8125rem;
  margin-top: 16px;
}

Explications et points de vigilance

max-width au lieu d'une largeur fixe : max-width: 960px avec margin: 0 auto dit : "ne dépasse pas 960px, mais rétrécis librement en dessous". Le conteneur s'adapte naturellement à n'importe quelle largeur d'écran. Une largeur fixe en pixels déborde ; une max-width s'adapte.

Pas de Flexbox sur .page dans le CSS de base : sur mobile, .contenu et .sidebar sont des blocs qui s'empilent dans le flux normal — c'est exactement le comportement voulu. Utiliser display: flex avec flex-direction: column pour "simuler" cet empilement serait du desktop-first déguisé : on partirait d'un état flex qu'on devrait modifier en media query. En mobile-first pur, le flux normal gère l'empilement gratuitement, et Flexbox n'est introduit qu'en media query pour les écrans larges.

font-size: 1rem : contrairement à 16px fixe, 1rem respecte les préférences d'accessibilité de l'utilisateur. Un utilisateur qui a configuré une police plus grande dans son navigateur verra le texte s'adapter correctement.

flex-shrink: 0 sur .logo : sans cette déclaration, Flexbox pourrait comprimer le logo si la navbar manque d'espace. On garantit ainsi que le logo reste toujours entièrement visible, quelle que soit la largeur de l'écran.

8.3 Media queries

Les media queries sont le mécanisme CSS qui permet d'appliquer des règles conditionnellement, en fonction des caractéristiques de l'environnement d'affichage. C'est l'outil central du responsive design : sans media queries, votre CSS s'applique de manière identique sur tous les écrans. Avec elles, vous pouvez adapter précisément la mise en page, la typographie et l'organisation du contenu selon le contexte.

8.3.1 Syntaxe de base

Une media query s'écrit avec le mot-clé @media, suivi d'une condition entre parenthèses, puis d'un bloc de règles CSS entre accolades. Ces règles ne s'appliquent que si la condition est vérifiée.

@media (condition) {
  /* Ces règles s'appliquent uniquement si la condition est vraie */
  sélecteur {
    propriété: valeur;
  }
}

Voici la forme la plus courante, que vous utiliserez dans la quasi-totalité de vos projets :

/* S'applique à partir de 768px de large — approche mobile-first */
@media (min-width: 768px) {
  .page {
    display: flex;
    gap: 24px;
  }
}

/* S'applique jusqu'à 768px de large — approche desktop-first */
@media (max-width: 768px) {
  .sidebar {
    display: none;
  }
}

Placement dans le fichier CSS.

Les media queries se placent généralement après les règles qu'elles enrichissent ou modifient. En mobile-first, le fichier CSS suit une logique progressive : les règles de base en premier, puis les media queries pour les écrans plus larges, dans l'ordre croissant des breakpoints.

/* 1. Règles de base — s'appliquent à tous les écrans */
.nav {
  flex-direction: column;
}

/* 2. Enrichissement à partir de 600px */
@media (min-width: 600px) {
  .nav {
    flex-direction: row;
  }
}

/* 3. Enrichissement supplémentaire à partir de 1024px */
@media (min-width: 1024px) {
  .nav {
    gap: 30px;
  }
}

Combiner plusieurs conditions.

On peut combiner plusieurs conditions avec and pour qu'une règle ne s'applique que dans une plage de largeurs précise.

/* S'applique uniquement entre 600px et 1023px — format tablette uniquement */
@media (min-width: 600px) and (max-width: 1023px) {
  .grille {
    flex-wrap: wrap;
  }
}

On peut également cibler plusieurs conditions indépendantes avec une virgule, qui joue le rôle d'un ou :

/* S'applique en dessous de 480px OU au-dessus de 1400px */
@media (max-width: 480px), (min-width: 1400px) {
  .encart {
    display: none;
  }
}

Cibler le type de média.

Avant min-width et max-width, les media queries ciblaient le type de support : screen pour les écrans, print pour l'impression, speech pour les synthèses vocales. Cette syntaxe reste utile, en particulier pour les feuilles de style d'impression.

/* Règles appliquées uniquement à l'impression */
@media print {
  .navbar,
  .sidebar,
  footer {
    display: none;
  }

  body {
    font-size: 12pt;
    color: #000000;
    background: none;
  }
}

En pratique, omettre le type de média (screen) est parfaitement valide et très courant. La quasi-totalité du code responsive que vous écrirez n'utilisera que min-width et max-width, sans préciser le type.

8.3.2 Conditions courantes

Au-delà de min-width et max-width, plusieurs autres conditions sont régulièrement utiles en production.

orientation.

Permet de différencier portrait (hauteur > largeur) et paysage (largeur > hauteur). Particulièrement utile sur tablette.

@media (orientation: landscape) and (min-width: 768px) {
  .galerie {
    flex-direction: row;
  }
}

prefers-color-scheme.

Détecte si l'utilisateur a activé le mode sombre dans ses préférences système. C'est aujourd'hui une attente courante des utilisateurs, et CSS permet d'y répondre sans JavaScript.

<!-- prefers-color-scheme.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="prefers-color-scheme.css">
  <title>Mode sombre</title>
</head>
<body>
  <div class="carte">
    <h2>Titre de la carte</h2>
    <p>Ce contenu s'adapte automatiquement au mode sombre ou clair défini dans les préférences système de l'utilisateur.</p>
  </div>
</body>
</html>
/* prefers-color-scheme.css */

body {
  background-color: #f0f4f8;
  color: #2c2c2c;
  font-family: Arial, sans-serif;
  padding: 40px;
}

.carte {
  background-color: #ffffff;
  border: 1px solid #dde3ed;
  border-radius: 8px;
  padding: 24px;
  max-width: 400px;
  margin: 0 auto;
}

.carte h2 { color: #1A3A5C; margin-bottom: 10px; }
.carte p  { color: #555; line-height: 1.7; font-size: 14px; }

@media (prefers-color-scheme: dark) {
  body {
    background-color: #0f1923;
    color: #e0e6ef;
  }

  .carte {
    background-color: #1a2a3a;
    border-color: #2e4460;
  }

  .carte h2 { color: #a8c4e0; }
  .carte p  { color: #9aafc4; }
}

prefers-reduced-motion.

Certains utilisateurs souffrent de troubles vestibulaires ou d'épilepsie photosensible. Cette condition détecte leur préférence système pour supprimer les animations.

.bouton {
  transition: background-color 0.3s ease, transform 0.2s ease;
}

.bouton:hover {
  transform: scale(1.05);
}

@media (prefers-reduced-motion: reduce) {
  .bouton {
    transition: none;
  }

  .bouton:hover {
    transform: none;
  }
}

hover et pointer.

Ces conditions détectent les capacités d'interaction de l'appareil. Un écran tactile n'a pas de curseur — les effets :hover peuvent s'y déclencher de façon inattendue.

/* Les effets hover ne s'appliquent que si l'appareil a une souris */
@media (hover: hover) and (pointer: fine) {
  .carte:hover {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
    transform: translateY(-2px);
  }
}

/* Sur les appareils tactiles, les cibles doivent être plus grandes */
@media (pointer: coarse) {
  .btn {
    min-height: 44px;
    padding: 12px 20px;
  }
}

Récapitulatif des conditions les plus utilisées :

 

min-width

Enrichir la mise en page en élargissant (mobile-first)

max-width

Adapter la mise en page en rétrécissant (desktop-first)

orientation: portrait/landscape

Adapter selon l'orientation de la tablette

prefers-color-scheme: dark

Proposer un thème sombre automatique

prefers-reduced-motion: reduce

Supprimer les animations pour l'accessibilité

hover: hover

Réserver les effets hover aux appareils avec souris

pointer: coarse

Agrandir les cibles tactiles sur mobile

print

Adapter la mise en page à l'impression

Condition

Usage typique

Exercice 26 — La carte de profil de développeur

Objectif : construire une page de profil de développeur, comme on en trouve sur GitHub ou dans un portfolio personnel. Sur mobile, tout s'empile en une colonne centrée. Sur tablette, le profil s'organise en deux zones côte à côte. Sur desktop, la mise en page s'élargit et s'aère, avec une grille de compétences sur plusieurs colonnes. En bonus, la page supporte le mode sombre automatiquement.

C'est un composant que vous réutiliserez dans vos propres projets : carte de profil, page "À propos", présentation d'équipe.

Consignes HTML :

  • Créez un fichier profil-dev.html avec la balise <meta name="viewport">.
  • Créez un <div class="page"> contenant une unique <div class="profil">.
  • À l'intérieur de .profil, créez deux zones :
    • Une <div class="profil-gauche"> contenant :
      • Un <div class="avatar"> avec vos initiales.
      • Un <h1> avec un nom.
      • Un <p class="titre-poste"> (ex : Développeur Front-End).
      • Un <p class="bio"> (deux phrases de présentation).
      • Une <div class="reseaux"> avec trois <a> : GitHub, LinkedIn, Portfolio.
    • Une <div class="profil-droite"> contenant :
      • Un <div class="section"> avec un <h2> (Compétences) et une <div class="competences"> contenant au moins huit <span class="tag"> (CSS, HTML, JavaScript, Flexbox, Git, React, Figma, Responsive).
      • Un <div class="section"> avec un <h2> (Projets récents) et deux <div class="projet">, chacun avec un <h3>, un <p> de description et un <span class="statut"> (En cours ou Terminé).
  • Liez un fichier profil-dev.css.

Consignes CSS :

Pensez à chaque écran avant d'écrire la moindre ligne. Que voit un utilisateur sur son téléphone ? Que voit-il en ouvrant la page sur son ordinateur ?

  • CSS de base (mobile) : la page est centrée avec une largeur maximale. .profil est une colonne unique — .profil-gauche et .profil-droite s'empilent. L'avatar est un grand cercle centré avec vos initiales dedans. Les .tag de compétences se répartissent naturellement en ligne et passent à la ligne si besoin. Chaque .projet a un fond, du padding et un aspect de carte.

  • À partir de 768px : .profil passe en deux colonnes côte à côte — .profil-gauche occupe environ un tiers de la largeur et ne rétrécit pas, .profil-droite prend le reste. La colonne gauche reste collée en haut.

  • À partir de 1100px : la page s'aère davantage. La .profil-gauche s'élargit légèrement. Les .competences s'organisent pour afficher les tags sur davantage de colonnes. Les .projet passent côte à côte.

  • Mode sombre : adaptez au minimum le fond de la page, le fond du profil, les couleurs de texte et les tags. Le résultat doit être lisible et cohérent.

Correction détaillée — Exercice 26

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="profil-dev.css">
  <title>Profil développeur</title>
</head>
<body>

  <div class="page">
    <div class="profil">

      <div class="profil-gauche">
        <div class="avatar">AL</div>
        <h1>Alice Laurent</h1>
        <p class="titre-poste">Développeuse Front-End</p>
        <p class="bio">Passionnée par le CSS, l'accessibilité et les interfaces soignées. Je transforme les maquettes en expériences web fluides et performantes.</p>
        <div class="reseaux">
          <a href="#">GitHub</a>
          <a href="#">LinkedIn</a>
          <a href="#">Portfolio</a>
        </div>
      </div>

      <div class="profil-droite">

        <div class="section">
          <h2>Compétences</h2>
          <div class="competences">
            <span class="tag">HTML</span>
            <span class="tag">CSS</span>
            <span class="tag">JavaScript</span>
            <span class="tag">Flexbox</span>
            <span class="tag">CSS Grid</span>
            <span class="tag">React</span>
            <span class="tag">Git</span>
            <span class="tag">Figma</span>
            <span class="tag">Responsive</span>
            <span class="tag">Accessibilité</span>
          </div>
        </div>

        <div class="section">
          <h2>Projets récents</h2>

          <div class="projet">
            <h3>Refonte du site vitrine</h3>
            <p>Migration d'un site desktop-only vers une expérience responsive mobile-first. Gain de 40% sur le Core Web Vitals mobile.</p>
            <span class="statut statut-termine">Terminé</span>
          </div>

          <div class="projet">
            <h3>Composant de design system</h3>
            <p>Création d'une bibliothèque de composants réutilisables en HTML/CSS pur, documentée et accessible.</p>
            <span class="statut statut-en-cours">En cours</span>
          </div>

        </div>

      </div>

    </div>
  </div>

</body>
</html>

Correction CSS

/* profil-dev.css */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* =====================
   CSS DE BASE — MOBILE
   ===================== */

body {
  font-family: Arial, sans-serif;
  font-size: 1rem;
  background-color: #f0f4f8;
  color: #2c2c2c;
  min-height: 100vh;
}

.page {
  max-width: 1100px;
  margin: 0 auto;
  padding: 24px 16px;
}

/* Sur mobile : .profil est une colonne unique */
.profil {
  background-color: #ffffff;
  border-radius: 12px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
  overflow: hidden;
}

/* --- COLONNE GAUCHE --- */

.profil-gauche {
  background-color: #1A3A5C;
  padding: 32px 24px;
  text-align: center;
}

.avatar {
  width: 90px;
  height: 90px;
  border-radius: 50%;
  background-color: #2E6DA4;
  color: #ffffff;
  font-size: 1.75rem;
  font-weight: bold;
  line-height: 90px;
  text-align: center;
  margin: 0 auto 20px;
  border: 3px solid rgba(255, 255, 255, 0.2);
}

.profil-gauche h1 {
  color: #ffffff;
  font-size: 1.375rem;
  margin-bottom: 6px;
}

.titre-poste {
  color: #a8c4e0;
  font-size: 0.875rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 16px;
}

.bio {
  color: #c8daf0;
  font-size: 0.875rem;
  line-height: 1.7;
  margin-bottom: 24px;
}

.reseaux {
  display: flex;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}

.reseaux a {
  display: block;
  padding: 7px 16px;
  background-color: rgba(255, 255, 255, 0.1);
  color: #a8c4e0;
  text-decoration: none;
  border-radius: 20px;
  font-size: 0.8125rem;
  border: 1px solid rgba(255, 255, 255, 0.15);
  transition: background-color 0.2s ease, color 0.2s ease;
}

.reseaux a:hover {
  background-color: #2E6DA4;
  color: #ffffff;
}

/* --- COLONNE DROITE --- */

.profil-droite {
  padding: 28px 24px;
}

.section {
  margin-bottom: 32px;
}

.section:last-child {
  margin-bottom: 0;
}

.section h2 {
  color: #1A3A5C;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 14px;
  padding-bottom: 8px;
  border-bottom: 2px solid #e8f0fe;
}

/* Tags de compétences — inline naturel, passent à la ligne si besoin */
.competences {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tag {
  display: inline-block;
  background-color: #e8f0fe;
  color: #2E6DA4;
  font-size: 0.8125rem;
  font-weight: bold;
  padding: 5px 12px;
  border-radius: 20px;
}

/* Cartes de projets */
.projet {
  background-color: #f8fafd;
  border: 1px solid #dde3ed;
  border-radius: 8px;
  padding: 18px;
  margin-bottom: 12px;
  position: relative;
}

.projet:last-child {
  margin-bottom: 0;
}

.projet h3 {
  color: #1A3A5C;
  font-size: 0.9375rem;
  margin-bottom: 8px;
}

.projet p {
  color: #666;
  font-size: 0.8125rem;
  line-height: 1.6;
  margin-bottom: 12px;
}

.statut {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: bold;
  padding: 3px 10px;
  border-radius: 10px;
}

.statut-termine {
  background-color: #e6f4ea;
  color: #2d7a3a;
}

.statut-en-cours {
  background-color: #fff4e0;
  color: #b06a00;
}

/* =========================
   À PARTIR DE 768px — TABLETTE
   ========================= */

@media (min-width: 768px) {

  .page {
    padding: 40px 24px;
  }

  /*
    .profil passe en deux colonnes :
    .profil-gauche à largeur fixe, .profil-droite prend le reste.
    align-items: stretch étire les deux colonnes à la même hauteur.
  */
  .profil {
    display: flex;
    align-items: stretch;
  }

  .profil-gauche {
    flex: 0 0 260px;
    text-align: left;
  }

  .avatar {
    margin: 0 0 20px;
  }

  .reseaux {
    justify-content: flex-start;
  }

  .profil-droite {
    flex: 1;
    padding: 32px;
  }
}

/* =========================
   À PARTIR DE 1100px — DESKTOP
   ========================= */

@media (min-width: 1100px) {

  .page {
    padding: 60px 24px;
  }

  .profil-gauche {
    flex: 0 0 300px;
    padding: 40px 32px;
  }

  .avatar {
    width: 110px;
    height: 110px;
    line-height: 110px;
    font-size: 2rem;
  }

  .profil-gauche h1 {
    font-size: 1.625rem;
  }

  .profil-droite {
    padding: 40px;
  }

  /* Les projets passent côte à côte sur desktop */
  .section:last-child .projet {
    display: inline-block;
    width: calc(50% - 6px);
    vertical-align: top;
    margin-bottom: 0;
  }

  .section:last-child .projet:first-of-type {
    margin-right: 12px;
  }
}

/* =============
   MODE SOMBRE
   ============= */

@media (prefers-color-scheme: dark) {

  body {
    background-color: #0c1520;
    color: #d0dce8;
  }

  .profil {
    background-color: #132030;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
  }

  .profil-gauche {
    background-color: #0a1828;
  }

  .profil-droite {
    background-color: #132030;
  }

  .section h2 {
    color: #a8c4e0;
    border-bottom-color: #1e3a56;
  }

  .tag {
    background-color: #1a3a56;
    color: #7ab0d8;
  }

  .projet {
    background-color: #0f2030;
    border-color: #1e3a56;
  }

  .projet h3 {
    color: #a8c4e0;
  }

  .projet p {
    color: #7a9bbf;
  }

  .statut-termine {
    background-color: #0f2a18;
    color: #5aba72;
  }

  .statut-en-cours {
    background-color: #2a1e00;
    color: #d4900a;
  }
}

Explications et points de vigilance

display: flex sur .profil n'apparaît qu'à 768px : sur mobile, .profil-gauche et .profil-droite s'empilent dans le flux normal — aucune propriété de mise en page n'est nécessaire. Flexbox est introduit uniquement quand l'espace permet une mise en page en colonnes. C'est la discipline mobile-first appliquée strictement.

align-items: stretch sur .profil (valeur par défaut) : les deux colonnes ont la même hauteur automatiquement, même si .profil-gauche contient moins de contenu que .profil-droite. Le fond sombre de la colonne gauche s'étend jusqu'en bas, ce qui donne l'effet de barre latérale colorée sans aucun hack.

flex: 0 0 300px sur .profil-gauche : la colonne gauche ne grandit pas, ne rétrécit pas et fait toujours 300px. C'est le pattern sidebar fixe vu en 7.5, appliqué ici dans un contexte responsive : la valeur passe de 260px à 300px entre 768px et 1100px grâce aux deux media queries.

Les projets côte à côte avec inline-block à 1100px : on aurait pu utiliser Flexbox sur le conteneur de projets, mais cela aurait nécessité de modifier la structure HTML. Ici, inline-block avec calc(50% - 6px) suffit pour deux cartes côte à côte, sans toucher au HTML. C'est une solution légère et ciblée pour un besoin précis.

8.4 Points de rupture

Un point de rupture — ou breakpoint — est la largeur à laquelle votre mise en page change de comportement. C'est la valeur que vous écrivez dans vos @media (min-width: ???px). Bien choisir ces valeurs est une décision de conception à part entière : de mauvais breakpoints produisent des mises en page qui se cassent entre deux tailles d'écran, ou qui ignorent les appareils réels de vos utilisateurs.

8.4.1 Choisir des breakpoints pertinents

Les breakpoints de référence : Bootstrap.

Plutôt que d'inventer des valeurs arbitraires, il est judicieux de s'appuyer sur un système éprouvé. Bootstrap, le framework CSS le plus utilisé au monde, a défini depuis sa version 5 un jeu de six breakpoints qui couvrent la quasi-totalité des appareils du marché. Ces valeurs sont aujourd'hui des références dans l'industrie, y compris pour les projets qui n'utilisent pas Bootstrap.

Nom Valeur min-width Contexte typique
xs — Extra small Aucune (CSS de base) Smartphones compacts, < 576px
sm — Small 576px Grands smartphones en paysage
md — Medium 768px Tablettes
lg — Large 992px Ordinateurs portables
xl — Extra large 1200px Desktops
xxl — Extra extra large 1400px Grands écrans, moniteurs larges

En mobile-first, le CSS de base (sans media query) cible le palier xs. Chaque media query min-width fait ensuite monter la mise en page d'un palier.

/* xs — CSS de base, aucune media query */
.conteneur {
  padding: 0 16px;
}

/* sm — à partir de 576px */
@media (min-width: 576px) {
  .conteneur {
    padding: 0 24px;
  }
}

/* md — à partir de 768px */
@media (min-width: 768px) {
  .conteneur {
    max-width: 720px;
    margin: 0 auto;
  }
}

/* lg — à partir de 992px */
@media (min-width: 992px) {
  .conteneur {
    max-width: 960px;
  }
}

/* xl — à partir de 1200px */
@media (min-width: 1200px) {
  .conteneur {
    max-width: 1140px;
  }
}

/* xxl — à partir de 1400px */
@media (min-width: 1400px) {
  .conteneur {
    max-width: 1320px;
  }
}

Faut-il toujours utiliser les six paliers ?

Non. Ces six valeurs sont un référentiel, pas une obligation. La majorité des projets n'en utilisent que deux ou trois. L'intérêt est de partir d'une base cohérente et reconnue plutôt que d'inventer des valeurs de toutes pièces. Choisissez les paliers dont votre contenu a besoin, et ignorez les autres.

/*
  Projet simple : on n'utilise que md et lg.
  La mise en page n'a besoin de changer qu'à ces deux moments.
*/

/* xs/sm — CSS de base */
.grille { flex-direction: column; }

/* md — tablette : deux colonnes */
@media (min-width: 768px) {
  .grille { flex-direction: row; flex-wrap: wrap; }
  .col    { flex: 1 1 calc(50% - 10px); }
}

/* lg — desktop : trois colonnes */
@media (min-width: 992px) {
  .col { flex: 1 1 calc(33.333% - 14px); }
}

Affiner avec des valeurs sur mesure.

Les breakpoints Bootstrap sont un point de départ. Rien n'empêche d'ajouter un breakpoint intermédiaire si votre contenu le nécessite — par exemple à 860px si votre mise en page change de comportement entre md (768px) et lg (992px). La règle d'or reste : placez un breakpoint là où votre contenu en a besoin, en utilisant les valeurs Bootstrap comme ancres de référence.

Évitez de multiplier les breakpoints. Deux ou trois paliers majeurs couvrent la grande majorité des besoins. Chaque breakpoint supplémentaire est du code à maintenir. Si vous vous retrouvez avec six breakpoints différents, c'est souvent le signe que la mise en page de base manque de flexibilité intrinsèque — et qu'il faut retravailler le CSS fondamental plutôt qu'ajouter des media queries.

8.4.2 Adapter la mise en page

Connaître les breakpoints ne suffit pas : il faut savoir quoi adapter à chaque palier. Les changements les plus fréquents concernent cinq domaines.

1. La structure en colonnes.

C'est le changement le plus visible. Une mise en page multi-colonnes sur desktop devient une colonne unique sur mobile. Le passage s'effectue généralement au palier md (768px) pour deux colonnes, et lg (992px) pour trois.

/* xs : une colonne */
.grille {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* md : deux colonnes */
@media (min-width: 768px) {
  .grille       { flex-direction: row; flex-wrap: wrap; }
  .carte        { flex: 1 1 calc(50% - 8px); }
}

/* lg : trois colonnes */
@media (min-width: 992px) {
  .carte        { flex: 1 1 calc(33.333% - 11px); }
}

2. La navigation.

Sur mobile (xs, sm), une navigation horizontale à plusieurs liens est rarement viable. La solution CSS la plus simple est de masquer les liens et de les afficher à partir de md ou lg.

/* xs/sm : navigation masquée */
.nav-liens {
  display: none;
}

/* md : navigation visible */
@media (min-width: 768px) {
  .nav-liens {
    display: flex;
    gap: 4px;
  }
}

3. La typographie.

Les titres, corps de texte et espacements doivent s'adapter. Un titre de 3.5rem parfait sur desktop est oppressant sur un écran de 375px.

.titre-principal {
  font-size: 1.75rem;    /* xs : compact */
}

@media (min-width: 768px) {
  .titre-principal {
    font-size: 2.5rem;   /* md : intermédiaire */
  }
}

@media (min-width: 1200px) {
  .titre-principal {
    font-size: 3.5rem;   /* xl : généreux */
  }
}

4. L'espacement.

Les paddings et marges doivent respirer davantage sur les grands écrans.

.section {
  padding: 40px 16px;    /* xs : compact */
}

@media (min-width: 768px) {
  .section {
    padding: 64px 32px;  /* md : aéré */
  }
}

@media (min-width: 1200px) {
  .section {
    padding: 100px 40px; /* xl : très généreux */
  }
}

5. Afficher et masquer des éléments.

Certains éléments n'ont de sens que sur desktop (une sidebar détaillée, un tableau complet) ou uniquement sur mobile (un résumé condensé).

/* Masqué sur mobile, visible sur desktop */
.detail-desktop { display: none; }

@media (min-width: 992px) {
  .detail-desktop { display: block; }
}

/* Visible sur mobile, masqué sur desktop */
.resume-mobile  { display: block; }

@media (min-width: 992px) {
  .resume-mobile  { display: none; }
}

Masquer du contenu n'est pas une solution d'adaptation. display: none soustrait un élément visuellement, mais son contenu reste chargé par le navigateur et lu par les lecteurs d'écran. Masquer massivement du contenu sur mobile est le symptôme d'un site conçu uniquement pour desktop. La bonne approche est de concevoir un contenu qui fonctionne sur tous les écrans, présenté différemment selon le contexte.

Exercice 27 — La page d'accueil d'une agence créative

Objectif : construire la page d'accueil d'une agence créative fictive en mobile-first, en utilisant les breakpoints Bootstrap comme référence. Sur mobile, la page est une succession de blocs lisibles et directs. Sur tablette (md — 768px), la grille de réalisations passe en deux colonnes et la navigation apparaît. Sur desktop (lg — 992px), le hero s'organise en deux zones côte à côte et la grille passe en trois colonnes.

C'est un exercice ancré dans la réalité : hero, grille de projets et footer sont des composants que vous retrouverez dans tout site vitrine ou portfolio.

Consignes HTML :

  • Créez un fichier agence.html avec la balise <meta name="viewport">.
  • Créez un <header class="header"> contenant une <div class="header-inner"> avec un <span class="logo"> (texte : FORMA) et une <nav class="nav"> avec quatre <a> : Services, Réalisations, Studio, Contact.
  • Créez une <section class="hero"> contenant une <div class="hero-inner"> avec :
    • Une <div class="hero-texte"> avec un <h1>, un <p class="accroche"> et un <a class="btn-hero"> (Voir nos travaux).
    • Une <div class="hero-visuel"> avec trois <div class="forme"> ayant chacune une classe supplémentaire : forme-1, forme-2, forme-3.
  • Créez une <section class="realisations"> contenant une <div class="section-inner">, un <h2> et une <div class="grille"> avec six <article class="carte-projet">. Chaque article contient une <div class="carte-visuel"> avec une classe numérotée (cv-1 à cv-6), un <h3> et un <p class="categorie">.
  • Créez un <footer class="footer"> avec une <div class="footer-inner"> contenant un <span class="logo">, un <p class="baseline"> et un <p class="copyright">.
  • Liez un fichier agence.css.

Consignes CSS :

Avant d'écrire quoi que ce soit, imaginez la page à 375px, puis à 768px, puis à 992px. Notez mentalement ce qui doit changer à chaque palier.

  • CSS de base (palier xs, < 576px) : tout s'empile en colonne. Le header est compact. Le .hero-texte et le .hero-visuel sont l'un sous l'autre. Les .forme sont trois formes géométriques colorées affichées côte à côte à l'intérieur de .hero-visuel. Les six cartes de la grille sont empilées. Identité visuelle sombre : fond #0d0d0d, accent jaune #e8ff47, rose #ff4d6d, turquoise #4dffd4. La navigation est masquée sur mobile.

  • Palier sm (576px) : aucun changement structurel majeur — c'est le moment d'augmenter légèrement l'espacement et de laisser les formes du hero prendre un peu plus de place.

  • Palier md (768px) : la navigation apparaît. La grille passe en deux colonnes. Le titre du hero grandit.

  • Palier lg (992px) : le hero passe en deux zones côte à côte — texte à gauche, formes à droite. La grille passe en trois colonnes. Les espacements deviennent plus généreux.

  • Palier xl (1200px) : ajustements typographiques finaux. Le <h1> atteint sa taille maximale. Les espacements du hero et des sections s'élargissent encore.

Correction détaillée — Exercice 27

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="agence.css">
  <title>FORMA — Agence créative</title>
</head>
<body>

  <header class="header">
    <div class="header-inner">
      <span class="logo">FORMA</span>
      <nav class="nav">
        <a href="#">Services</a>
        <a href="#">Réalisations</a>
        <a href="#">Studio</a>
        <a href="#">Contact</a>
      </nav>
    </div>
  </header>

  <section class="hero">
    <div class="hero-inner">
      <div class="hero-texte">
        <h1>Nous créons des expériences qui marquent.</h1>
        <p class="accroche">Design, motion et stratégie créative pour les marques qui veulent faire la différence.</p>
        <a href="#" class="btn-hero">Voir nos travaux</a>
      </div>
      <div class="hero-visuel">
        <div class="forme forme-1"></div>
        <div class="forme forme-2"></div>
        <div class="forme forme-3"></div>
      </div>
    </div>
  </section>

  <section class="realisations">
    <div class="section-inner">
      <h2>Nos réalisations</h2>
      <div class="grille">

        <article class="carte-projet">
          <div class="carte-visuel cv-1"></div>
          <h3>Identité visuelle Bloom</h3>
          <p class="categorie">Branding</p>
        </article>

        <article class="carte-projet">
          <div class="carte-visuel cv-2"></div>
          <h3>Campagne digitale Vektra</h3>
          <p class="categorie">Motion Design</p>
        </article>

        <article class="carte-projet">
          <div class="carte-visuel cv-3"></div>
          <h3>Site e-commerce Noma</h3>
          <p class="categorie">Web Design</p>
        </article>

        <article class="carte-projet">
          <div class="carte-visuel cv-4"></div>
          <h3>Application mobile Pulse</h3>
          <p class="categorie">UI / UX</p>
        </article>

        <article class="carte-projet">
          <div class="carte-visuel cv-5"></div>
          <h3>Direction artistique Lune</h3>
          <p class="categorie">Branding</p>
        </article>

        <article class="carte-projet">
          <div class="carte-visuel cv-6"></div>
          <h3>Packaging Aroki</h3>
          <p class="categorie">Print & Packaging</p>
        </article>

      </div>
    </div>
  </section>

  <footer class="footer">
    <div class="footer-inner">
      <span class="logo">FORMA</span>
      <p class="baseline">Design avec intention.</p>
      <p class="copyright">&copy; 2024 FORMA Studio. Tous droits réservés.</p>
    </div>
  </footer>

</body>
</html>

Correction CSS

/* agence.css — Mobile-first avec breakpoints Bootstrap */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* =====================
   XS — CSS DE BASE
   < 576px
   ===================== */

body {
  font-family: Arial, sans-serif;
  font-size: 1rem;
  background-color: #0d0d0d;
  color: #ffffff;
}

/* --- Header --- */

.header {
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  padding: 0 16px;
}

.header-inner {
  max-width: 1320px;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 56px;
}

.logo {
  font-size: 1.25rem;
  font-weight: bold;
  letter-spacing: 3px;
  color: #e8ff47;
}

/* Navigation masquée sur xs et sm */
.nav {
  display: none;
}

/* --- Hero --- */

.hero {
  padding: 48px 16px 40px;
}

.hero-inner {
  max-width: 1320px;
  margin: 0 auto;
}

/* Sur xs : texte et visuel s'empilent dans le flux normal */
.hero-texte {
  margin-bottom: 36px;
}

.hero-texte h1 {
  font-size: 2rem;
  line-height: 1.2;
  margin-bottom: 16px;
}

.accroche {
  color: rgba(255, 255, 255, 0.6);
  font-size: 1rem;
  line-height: 1.7;
  margin-bottom: 28px;
}

.btn-hero {
  display: inline-block;
  background-color: #e8ff47;
  color: #0d0d0d;
  padding: 12px 28px;
  text-decoration: none;
  font-weight: bold;
  font-size: 0.9375rem;
  border-radius: 2px;
  transition: background-color 0.2s ease;
}

.btn-hero:hover {
  background-color: #d4eb30;
}

/* Les trois formes côte à côte dans le hero-visuel */
.hero-visuel {
  display: flex;
  gap: 12px;
  justify-content: center;
}

.forme {
  width: 80px;
  height: 80px;
  flex-shrink: 0;
}

.forme-1 { background-color: #e8ff47; border-radius: 50%; }
.forme-2 { background-color: #ff4d6d; border-radius: 12px; }
.forme-3 { background-color: #4dffd4; border-radius: 0; }

/* --- Réalisations --- */

.realisations {
  padding: 48px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.section-inner {
  max-width: 1320px;
  margin: 0 auto;
}

.realisations h2 {
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: rgba(255, 255, 255, 0.4);
  margin-bottom: 24px;
}

/* xs : cartes empilées en une colonne */
.grille {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.carte-projet {
  background-color: #1a1a1a;
  border-radius: 8px;
  overflow: hidden;
}

.carte-visuel {
  height: 180px;
}

.cv-1 { background-color: #1e3a5c; }
.cv-2 { background-color: #3a1a2a; }
.cv-3 { background-color: #1a3a2a; }
.cv-4 { background-color: #3a2a1a; }
.cv-5 { background-color: #2a1a3a; }
.cv-6 { background-color: #3a3a1a; }

.carte-projet h3 {
  padding: 14px 16px 4px;
  font-size: 0.9375rem;
}

.categorie {
  padding: 0 16px 14px;
  font-size: 0.75rem;
  color: #e8ff47;
  text-transform: uppercase;
  letter-spacing: 1px;
}

/* --- Footer --- */

.footer {
  padding: 40px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.footer-inner {
  max-width: 1320px;
  margin: 0 auto;
  text-align: center;
}

.footer .logo {
  display: block;
  margin-bottom: 10px;
}

.baseline {
  color: rgba(255, 255, 255, 0.4);
  font-size: 0.875rem;
  margin-bottom: 6px;
}

.copyright {
  color: rgba(255, 255, 255, 0.2);
  font-size: 0.75rem;
}

/* =====================
   SM — 576px
   Ajustements d'espace,
   pas de changement structurel
   ===================== */

@media (min-width: 576px) {

  .hero {
    padding: 60px 24px 48px;
  }

  .forme {
    width: 110px;
    height: 110px;
  }

  .hero-visuel {
    gap: 16px;
  }

  .realisations {
    padding: 60px 24px;
  }

  .footer {
    padding: 48px 24px;
  }
}

/* =====================
   MD — 768px
   Navigation visible,
   grille en deux colonnes,
   titre qui grandit
   ===================== */

@media (min-width: 768px) {

  .header-inner {
    height: 64px;
  }

  /* La navigation apparaît à partir de md */
  .nav {
    display: flex;
    gap: 4px;
  }

  .nav a {
    display: block;
    padding: 8px 14px;
    color: rgba(255, 255, 255, 0.55);
    text-decoration: none;
    font-size: 0.875rem;
    border-radius: 4px;
    transition: color 0.2s ease, background-color 0.2s ease;
  }

  .nav a:hover {
    color: #ffffff;
    background-color: rgba(255, 255, 255, 0.06);
  }

  .hero-texte h1 {
    font-size: 2.5rem;
  }

  /* Grille en deux colonnes */
  .grille {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .carte-projet {
    flex: 1 1 calc(50% - 8px);
  }

  .realisations {
    padding: 64px 32px;
  }
}

/* =====================
   LG — 992px
   Hero en deux colonnes,
   grille en trois colonnes,
   espacements généreux
   ===================== */

@media (min-width: 992px) {

  .hero {
    padding: 80px 32px;
  }

  /* Hero : texte à gauche, formes à droite */
  .hero-inner {
    display: flex;
    align-items: center;
    gap: 60px;
  }

  .hero-texte {
    flex: 1;
    margin-bottom: 0;
  }

  .hero-texte h1 {
    font-size: 3rem;
  }

  .hero-visuel {
    flex: 0 0 280px;
    flex-direction: column;
    justify-content: center;
    gap: 16px;
  }

  .forme {
    width: 150px;
    height: 150px;
  }

  /* Grille en trois colonnes */
  .carte-projet {
    flex: 1 1 calc(33.333% - 11px);
  }

  .realisations {
    padding: 80px 32px;
  }

  /* Footer en ligne sur desktop */
  .footer-inner {
    display: flex;
    justify-content: space-between;
    align-items: center;
    text-align: left;
  }

  .footer .logo {
    display: inline;
    margin-bottom: 0;
  }
}

/* =====================
   XL — 1200px
   Taille maximale du titre,
   espacements finaux
   ===================== */

@media (min-width: 1200px) {

  .hero {
    padding: 110px 40px;
  }

  .hero-texte h1 {
    font-size: 3.75rem;
    line-height: 1.1;
  }

  .accroche {
    font-size: 1.125rem;
  }

  .hero-visuel {
    flex: 0 0 340px;
  }

  .forme {
    width: 190px;
    height: 190px;
  }

  .carte-visuel {
    height: 220px;
  }

  .realisations {
    padding: 100px 40px;
  }
}

Explications et points de vigilance

Cinq paliers Bootstrap utilisés sur six : xs (CSS de base), sm, md, lg et xl. Le palier xxl (1400px) n'est pas utilisé ici car le contenu n'en a pas besoin — la mise en page est déjà optimale à 1200px. C'est l'application directe du principe énoncé en 8.4.1 : on utilise les paliers dont le contenu a besoin, pas tous les paliers systématiquement.

sm sans changement structurel : le palier 576px n'introduit aucun changement de mise en page — uniquement de l'espace et des formes légèrement plus grandes. C'est un breakpoint mineur au sens de 8.4.1. Les breakpoints majeurs sont md (navigation + grille) et lg (hero en colonnes).

Le hero-visuel change de flex-direction à lg : sur mobile et tablette, les trois formes sont côte à côte horizontalement. À partir de lg, .hero-visuel passe en flex-direction: column pour les empiler verticalement dans leur colonne à droite du texte. La même propriété Flexbox produit deux organisations radicalement différentes selon le breakpoint — c'est un bon exemple de ce que "adapter la mise en page" signifie concrètement.

flex: 1 1 calc(50% - 8px) et calc(33.333% - 11px) : avec un gap: 16px, deux cartes côte à côte ont chacune une largeur de calc(50% - 8px) (la moitié du gap). Pour trois colonnes, chaque carte fait calc(33.333% - 11px) (deux tiers du gap répartis sur trois cartes). Cette arithmétique est systématique et peut être réutilisée dans tout projet avec une grille Flexbox.

Erreur fréquente : appliquer tous les paliers Bootstrap mécaniquement, sans réfléchir à ce que le contenu nécessite réellement à chaque seuil. Bootstrap lui-même ne force pas l'utilisation de ses six breakpoints — il les propose comme un vocabulaire commun. L'objectif est d'avoir un CSS lisible, maintenu par des valeurs reconnues, pas un CSS surchargé de media queries inutiles.

8.5 Images et unités responsives

Les media queries adaptent la structure de la page, mais un site vraiment responsive va plus loin : ses images ne débordent pas et se chargent au bon poids selon l'écran, ses tailles de police respectent les préférences de l'utilisateur, et ses espacements s'adaptent proportionnellement à la fenêtre. Ces trois aspects — images, unités relatives, unités viewport — sont les derniers outils pour rendre une mise en page truly responsive dans tous ses détails.

8.5.1 Images adaptatives

Une image est par nature un élément de taille fixe : un fichier de 1200px de large s'affiche à 1200px, point. Sans instruction CSS, elle déborde de n'importe quel conteneur plus étroit. Rendre les images responsives est donc une étape indispensable.

La règle de base : max-width: 100%.

C'est la règle responsive la plus simple et la plus universelle. Elle dit à l'image de ne jamais dépasser la largeur de son conteneur, tout en lui permettant d'être plus petite si son fichier l'est.

img {
  max-width: 100%;
  height: auto;    /* Préserve le ratio largeur/hauteur */
  display: block;  /* Supprime l'espace blanc sous l'image (comportement inline par défaut) */
}

max-width: 100% et non width: 100% : avec width: 100%, une petite image de 200px serait étirée sur toute la largeur de son conteneur, ce qui la pixelliserait. max-width: 100% dit "au maximum la largeur du conteneur" — l'image ne grandit jamais au-delà de sa taille naturelle.

L'attribut srcset — servir la bonne résolution.

Une image affichée à 400px de large sur mobile n'a pas besoin d'être un fichier de 1600px. Charger ce fichier lourd sur mobile est un gaspillage de bande passante. L'attribut HTML srcset permet de fournir plusieurs versions d'une image, et laisse le navigateur choisir la plus adaptée selon la taille d'affichage et la densité de pixels de l'écran.

<img
  src="photo-800.jpg"
  srcset="
    photo-400.jpg  400w,
    photo-800.jpg  800w,
    photo-1200.jpg 1200w
  "
  sizes="
    (max-width: 576px) 100vw,
    (max-width: 992px) 50vw,
    33vw
  "
  alt="Description de l'image"
>
  • srcset liste les fichiers disponibles avec leur largeur en pixels (w).
  • sizes indique au navigateur la largeur d'affichage prévue selon le contexte. 100vw signifie "toute la largeur de la fenêtre", 50vw signifie "la moitié".
  • src reste présent comme valeur de secours pour les navigateurs qui ne supportent pas srcset.

srcset est une suggestion, pas un ordre. Le navigateur reste libre de choisir selon ses propres critères, notamment la densité de l'écran. Sur un écran Retina (densité ×2), il peut choisir l'image 800px pour un affichage prévu à 400px, afin de maintenir la netteté. Cette intelligence est entièrement gérée par le navigateur.

La balise <picture> — changer d'image selon le contexte.

srcset sert à choisir entre plusieurs résolutions d'une même image. La balise <picture> va plus loin : elle permet de servir une image complètement différente selon le breakpoint — un cadrage différent, un format différent, voire une image entièrement autre sur mobile et desktop.

<picture>
  <!-- Sur les écrans de 992px et plus : image paysage grand format -->
  <source media="(min-width: 992px)" srcset="hero-desktop.jpg">
  <!-- Sur les écrans de 576px à 991px : version intermédiaire -->
  <source media="(min-width: 576px)" srcset="hero-tablette.jpg">
  <!-- Fallback : image portrait pour mobile, toujours présent -->
  <img src="hero-mobile.jpg" alt="Présentation de l'agence">
</picture>

Les sources sont évaluées dans l'ordre. Le navigateur utilise la première dont la condition media est vérifiée. Si aucune ne l'est, il utilise <img>.

Le format WebP.

WebP est un format d'image développé par Google qui offre une compression supérieure au JPEG et au PNG, pour une qualité visuelle équivalente — généralement 25 à 35% plus léger. La balise <picture> permet de servir WebP aux navigateurs qui le supportent, avec un fallback JPEG pour les autres.

<picture>
  <source srcset="photo.webp" type="image/webp">
  <img src="photo.jpg" alt="Description">
</picture>

8.5.2 Utilisation des unités relatives

Les unités relatives définissent une taille en fonction d'une autre valeur — la taille de police du parent, la taille de police racine du document, ou la taille du conteneur. Elles produisent des mises en page qui s'adaptent naturellement sans media queries supplémentaires.

em — relatif à la taille de police du parent.

1em est égal à la taille de police de l'élément parent. Cette unité est utile pour les espacements internes d'un composant, car ils restent proportionnels à sa typographie.

.carte {
  font-size: 1rem;
  padding: 1.5em;    /* 1.5 × 16px = 24px */
  border-radius: 0.5em;
}

.carte.grande {
  font-size: 1.25rem;
  /* padding: 1.5em = 1.5 × 20px = 30px — s'adapte automatiquement */
}

Attention à l'effet de cascade des em. Si vous imbriquez des éléments dont les tailles de police sont en em, les valeurs se multiplient. Un enfant à font-size: 0.8em dans un parent à font-size: 0.8em aura en réalité 0.64em de la taille racine. Pour la typographie, rem est souvent plus prévisible.

rem — relatif à la taille racine du document.

1rem est toujours égal à la taille de police de l'élément <html>, quelle que soit la profondeur d'imbrication. Par défaut, les navigateurs définissent cette taille à 16px, mais l'utilisateur peut la modifier dans ses paramètres d'accessibilité. rem est l'unité recommandée pour la typographie et les espacements globaux.

h1    { font-size: 2.5rem;   } /* 40px */
h2    { font-size: 1.75rem;  } /* 28px */
p     { font-size: 1rem;     } /* 16px */
small { font-size: 0.875rem; } /* 14px */

.section {
  padding: 3rem 1.5rem; /* 48px 24px — reste proportionnel si la racine change */
}

% — relatif à la dimension du parent.

Les pourcentages sont relatifs à la dimension correspondante du parent. Ils sont particulièrement utiles pour les mises en page fluides et les grilles.

.colonne-principale { width: 65%; }
.colonne-secondaire { width: 35%; }

/* Sur un conteneur de 800px : 520px et 280px.
   Sur 1200px : 780px et 420px.
   Les proportions sont préservées quelle que soit la largeur. */

Quand utiliser quelle unité ?

rem

Taille racine <html>

Tailles de police, espacements globaux

em

Taille de police du parent

Padding/margin internes d'un composant

%

Dimension du parent

Largeurs fluides, grilles

px

Pixel absolu

Bordures, ombres, valeurs qui ne doivent pas changer

Unité

Référence

Utiliser pour

8.5.3 Utilisation des unités viewport

Les unités viewport sont relatives aux dimensions de la fenêtre du navigateur, pas du parent. Elles permettent de créer des éléments dont la taille dépend directement de l'espace d'affichage disponible.

vw et vh — largeur et hauteur viewport.

  • 1vw = 1% de la largeur de la fenêtre
  • 1vh = 1% de la hauteur de la fenêtre
  • 100vw = toute la largeur de la fenêtre
  • 100vh = toute la hauteur de la fenêtre
<!-- unites-viewport.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="unites-viewport.css">
  <title>Unités viewport</title>
</head>
<body>

  <section class="hero-fullscreen">
    <h1>Bienvenue</h1>
    <p>Cette section occupe exactement la hauteur de la fenêtre.</p>
  </section>

  <section class="section-normale">
    <h2>Contenu suivant</h2>
    <p>Cette section suit normalement dans le flux.</p>
  </section>

</body>
</html>
/* unites-viewport.css */

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; }

.hero-fullscreen {
  width: 100%;
  height: 100vh; /* Occupe exactement la hauteur de la fenêtre */
  background-color: #1A3A5C;
  color: #ffffff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 0 5vw; /* Padding proportionnel à la largeur de la fenêtre */
}

.hero-fullscreen h1 {
  font-size: 5vw; /* Titre dont la taille suit la largeur de la fenêtre */
  margin-bottom: 1rem;
}

.hero-fullscreen p {
  font-size: 1.25rem;
  color: rgba(255, 255, 255, 0.7);
}

.section-normale {
  padding: 60px 24px;
  max-width: 800px;
  margin: 0 auto;
}

vw pour la typographie fluide.

L'unité vw permet de créer des titres dont la taille s'adapte en continu à la largeur de l'écran, sans breakpoints. Combinée à clamp(), elle devient l'outil moderne de référence pour la typographie responsive.

/* clamp(valeur-min, valeur-préférée, valeur-max) */
h1 {
  /*
    Le titre fait au minimum 1.75rem, au maximum 4rem,
    et idéalement 5vw entre les deux.
    Sur 320px : 5vw = 16px → en dessous du min → 1.75rem.
    Sur 600px : 5vw = 30px → dans la plage → 30px.
    Sur 1400px : 5vw = 70px → au-dessus du max → 4rem.
  */
  font-size: clamp(1.75rem, 5vw, 4rem);
}

p {
  font-size: clamp(1rem, 1.5vw, 1.25rem);
}

clamp() est la technique moderne de typographie fluide. Elle remplace les media queries typographiques à répétition par une seule règle qui s'adapte en continu. Les trois arguments sont : la valeur minimale, la valeur idéale (en vw), et la valeur maximale. Supportée par tous les navigateurs modernes.

dvh — la hauteur viewport dynamique sur mobile.

Sur mobile, la barre d'adresse du navigateur apparaît et disparaît en scrollant. 100vh ne tient pas compte de cette barre et peut masquer une partie du contenu. L'unité dvh (Dynamic Viewport Height) résout ce problème en s'adaptant en temps réel à la hauteur réellement disponible.

.hero {
  height: 100vh;   /* Fallback pour les navigateurs anciens */
  height: 100dvh;  /* S'adapte à la hauteur réelle disponible sur mobile */
}

vmin et vmax.

  • 1vmin = 1% de la plus petite dimension de la fenêtre (largeur ou hauteur)
  • 1vmax = 1% de la plus grande dimension

Utiles pour les éléments qui doivent rester proportionnels quelle que soit l'orientation de l'écran.

/* Un avatar qui reste rond et proportionnel en portrait et en paysage */
.avatar {
  width: 20vmin;
  height: 20vmin;
  border-radius: 50%;
}

Exercice 28 — La page de lancement d'une application

Objectif : construire la page de lancement d'une application météo fictive. Elle combine un hero plein écran avec typographie fluide, une grille de fonctionnalités responsive et une section de téléchargement. L'exercice travaille 100dvh, clamp(), les images responsives avec max-width: 100% et les unités rem et em pour la typographie et les espacements.

C'est un composant directement réutilisable : landing page, page de présentation de projet, portfolio.

Consignes HTML :

  • Créez un fichier lancement.html avec la balise <meta name="viewport">.
  • Créez une <section class="hero"> contenant une <div class="hero-contenu"> avec :
    • Une <div class="hero-texte"> contenant un <p class="label"> (Disponible maintenant), un <h1> avec le nom du produit (Aero) et un <span> contenant un sous-titre (L'application météo qui anticipe votre journée.), un <p class="description"> de deux phrases, et une <div class="hero-actions"> avec deux liens : un <a class="btn-principal"> (Télécharger gratuitement) et un <a class="btn-secondaire"> (En savoir plus).
    • Une <div class="hero-image"> contenant une <img> avec src="mockup.png" et un alt approprié.
  • Créez une <section class="fonctionnalites"> avec une <div class="section-inner">, un <h2> et une <div class="grille-fonc"> contenant quatre <div class="fonc">. Chaque .fonc a une <div class="fonc-icone"> (une lettre), un <h3> et un <p>.
  • Créez une <section class="telechargement"> avec une <div class="section-inner"> contenant un <h2>, un <p> et une <div class="badges"> avec deux <a class="badge"> (App Store et Google Play).
  • Liez un fichier lancement.css.

Consignes CSS :

Avant d'écrire la moindre ligne, posez-vous ces questions : comment ce hero se comporte-t-il quand la barre d'adresse du navigateur mobile apparaît ? Comment ce titre reste-t-il lisible à 320px et imposant à 1400px sans une seule media query ?

  • Hero : la section doit occuper toute la hauteur visible de la fenêtre. Réfléchissez à quelle unité utiliser pour que le contenu ne soit jamais masqué par la barre d'adresse mobile. Prévoyez un fallback pour les anciens navigateurs qui ne supportent pas cette unité. Sur mobile, .hero-texte et .hero-image s'empilent. À partir de md (768px), ils passent côte à côte — le texte doit prendre plus de place que l'image.

  • Typographie fluide : le <h1> et les titres de section doivent s'adapter en continu entre mobile et desktop, sans media query typographique. Définissez une taille minimale lisible sur 320px, une taille maximale pour les grands écrans, et une valeur intermédiaire proportionnelle à la fenêtre. Quelle fonction CSS permet d'exprimer ces trois contraintes en une seule déclaration ?

  • Le <span> dans le <h1> : ce sous-titre doit rester proportionnel au titre, quelle que soit la taille calculée de ce dernier. Quelle unité choisir pour garantir cette relation proportionnelle sans valeur absolue ?

  • Image : l'image mockup.png ne doit jamais déborder de son conteneur ni se déformer, quelle que soit la largeur de l'écran. Quelles propriétés CSS garantissent ces deux comportements ?

  • Grille de fonctionnalités : une colonne sur mobile, deux à sm (576px), quatre à lg (992px). Utilisez les breakpoints Bootstrap.

  • Unités : toutes les tailles de police en rem. Les paddings globaux des sections en rem. Les paddings internes des composants (.fonc, .badge) en em pour rester proportionnels à leur propre typographie.

Correction détaillée — Exercice 28

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="style.css">
  <title>Aero — L'application météo</title>
</head>
<body>

  <section class="hero">
    <div class="hero-contenu">

      <div class="hero-texte">
        <p class="label">Disponible maintenant</p>
        <h1>Aero <span>L'application météo qui anticipe votre journée.</span></h1>
        <p class="description">Aero analyse les données météo en temps réel et vous propose un planning adapté à vos habitudes. Fini les surprises, bonjour les bonnes décisions.</p>
        <div class="hero-actions">
          <a href="#" class="btn-principal">Télécharger gratuitement</a>
          <a href="#" class="btn-secondaire">En savoir plus</a>
        </div>
      </div>

      <div class="hero-image">
        <img src="meteo.png" alt="Capture d'écran de l'application Aero sur smartphone">
      </div>

    </div>
  </section>

  <section class="fonctionnalites">
    <div class="section-inner">
      <h2>Conçu pour votre quotidien</h2>
      <div class="grille-fonc">

        <div class="fonc">
          <div class="fonc-icone">P</div>
          <h3>Prévisions précises</h3>
          <p>Données actualisées toutes les heures depuis 50 000 stations météo mondiales.</p>
        </div>

        <div class="fonc">
          <div class="fonc-icone">A</div>
          <h3>Alertes intelligentes</h3>
          <p>Notifications personnalisées selon vos activités programmées dans votre agenda.</p>
        </div>

        <div class="fonc">
          <div class="fonc-icone">C</div>
          <h3>Carte interactive</h3>
          <p>Visualisez les fronts météo, la pluie et le vent en temps réel sur une carte.</p>
        </div>

        <div class="fonc">
          <div class="fonc-icone">M</div>
          <h3>Mode hors-ligne</h3>
          <p>Les prévisions des 48 prochaines heures sont disponibles sans connexion.</p>
        </div>

      </div>
    </div>
  </section>

  <section class="telechargement">
    <div class="section-inner">
      <h2>Gratuit. Sans publicité. Pour toujours.</h2>
      <p>Disponible sur iOS et Android. Plus de 2 millions d'utilisateurs actifs.</p>
      <div class="badges">
        <a href="#" class="badge">App Store</a>
        <a href="#" class="badge">Google Play</a>
      </div>
    </div>
  </section>

</body>
</html>

Correction CSS

/* lancement.css */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  font-size: 1rem;
  background-color: #f8fafd;
  color: #1a1a2e;
}

/* ======================
   HERO — XS (mobile)
   ====================== */

.hero {
  background: linear-gradient(135deg, #0f2a4a 0%, #1a3a5c 60%, #2e6da4 100%);
  padding: 60px 20px;

  /*
    100vh d'abord comme fallback pour les anciens navigateurs.
    100dvh ensuite : hauteur dynamique qui s'ajuste en temps réel
    à la barre d'adresse mobile — le contenu n'est jamais masqué.
  */
  min-height: 100vh;
  min-height: 100dvh;

  display: flex;
  align-items: center;
}

.hero-contenu {
  max-width: 1140px;
  margin: 0 auto;
  width: 100%;
  /* Sur mobile : .hero-texte et .hero-image s'empilent dans le flux */
}

.hero-texte {
  margin-bottom: 36px;
}

.label {
  display: inline-block;
  background-color: rgba(232, 255, 71, 0.15);
  color: #e8ff47;
  font-size: 0.8125rem;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  padding: 5px 14px;
  border-radius: 20px;
  margin-bottom: 1.25rem;
  border: 1px solid rgba(232, 255, 71, 0.3);
}

h1 {
  /*
    clamp(min, préféré, max) : typographie fluide sans media query.
    Minimum 1.75rem → lisible à 320px.
    5vw → proportionnel à la fenêtre.
    3.75rem → imposant mais pas excessif sur 1400px.
  */
  font-size: clamp(1.75rem, 5vw, 3.75rem);
  line-height: 1.15;
  color: #ffffff;
  margin-bottom: 1rem;
}

h1 span {
  display: block;
  /*
    0.55em : relatif au h1 parent.
    Si h1 = 60px, span = 33px.
    Si h1 = 28px, span = 15.4px.
    La proportion est maintenue automatiquement par clamp().
  */
  font-size: 0.55em;
  color: rgba(255, 255, 255, 0.65);
  font-weight: normal;
  margin-top: 0.5em;
  line-height: 1.4;
}

.description {
  font-size: clamp(1rem, 1.5vw, 1.25rem);
  color: rgba(255, 255, 255, 0.7);
  line-height: 1.75;
  margin-bottom: 2rem;
  max-width: 540px;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.btn-principal {
  display: inline-block;
  background-color: #e8ff47;
  color: #0f2a4a;
  padding: 0.875rem 1.75rem;
  text-decoration: none;
  font-weight: bold;
  font-size: 0.9375rem;
  border-radius: 4px;
  white-space: nowrap;
  transition: background-color 0.2s ease;
}

.btn-principal:hover { background-color: #d4eb30; }

.btn-secondaire {
  display: inline-block;
  color: rgba(255, 255, 255, 0.8);
  padding: 0.875rem 1.75rem;
  text-decoration: none;
  font-size: 0.9375rem;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 4px;
  white-space: nowrap;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.btn-secondaire:hover {
  border-color: rgba(255, 255, 255, 0.7);
  color: #ffffff;
}

.hero-image { text-align: center; }

/*
  max-width: 100% : l'image ne dépasse jamais son conteneur.
  height: auto : les proportions sont toujours préservées.
  display: block : supprime l'espace blanc sous l'image.
*/
.hero-image img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
  max-height: 380px;
  object-fit: contain;
  border-radius: 16px;
  background-color: rgba(255, 255, 255, 0.05);
  min-width: 180px;
  min-height: 180px;
}

/* ======================
   FONCTIONNALITÉS — XS
   ====================== */

.fonctionnalites {
  padding: 4rem 1.25rem;
}

.section-inner {
  max-width: 1140px;
  margin: 0 auto;
}

.fonctionnalites h2 {
  font-size: clamp(1.375rem, 3vw, 2rem);
  color: #1a1a2e;
  text-align: center;
  margin-bottom: 2.5rem;
}

/* xs : une colonne */
.grille-fonc {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.fonc {
  background-color: #ffffff;
  border: 1px solid #e0e8f5;
  border-radius: 10px;
  padding: 1.5em; /* em : proportionnel à la police du composant */
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}

.fonc-icone {
  width: 2.75em;
  height: 2.75em;
  border-radius: 8px;
  background-color: #1a3a5c;
  color: #e8ff47;
  font-size: 1.125rem;
  font-weight: bold;
  line-height: 2.75em;
  text-align: center;
  margin-bottom: 1em;
}

.fonc h3 {
  font-size: 1rem;
  color: #1a1a2e;
  margin-bottom: 0.5em;
}

.fonc p {
  font-size: 0.875rem;
  color: #666;
  line-height: 1.65;
}

/* ======================
   TÉLÉCHARGEMENT — XS
   ====================== */

.telechargement {
  background-color: #1a3a5c;
  padding: 4rem 1.25rem;
  text-align: center;
}

.telechargement h2 {
  font-size: clamp(1.25rem, 3vw, 1.875rem);
  color: #ffffff;
  margin-bottom: 0.75rem;
}

.telechargement p {
  color: rgba(255, 255, 255, 0.6);
  font-size: 1rem;
  margin-bottom: 2rem;
}

.badges {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1rem;
}

.badge {
  display: inline-block;
  background-color: rgba(255, 255, 255, 0.08);
  color: #ffffff;
  padding: 0.875em 2em; /* em : proportionnel à la taille du badge */
  text-decoration: none;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 8px;
  font-size: 0.9375rem;
  font-weight: bold;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.badge:hover {
  background-color: rgba(255, 255, 255, 0.15);
  border-color: rgba(255, 255, 255, 0.5);
}

/* ======================
   SM — 576px
   Grille en deux colonnes
   ====================== */

@media (min-width: 576px) {
  .grille-fonc {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .fonc {
    flex: 1 1 calc(50% - 0.5rem);
  }
}

/* ======================
   MD — 768px
   Hero en deux colonnes
   ====================== */

@media (min-width: 768px) {

  .hero { padding: 80px 32px; }

  .hero-contenu {
    display: flex;
    align-items: center;
    gap: 60px;
  }

  /* Le texte prend 3/5 de l'espace, l'image 2/5 */
  .hero-texte {
    flex: 3;
    margin-bottom: 0;
  }

  .hero-image {
    flex: 2;
  }

  .hero-image img { max-height: 480px; }

  .fonctionnalites { padding: 5rem 2rem; }
  .telechargement  { padding: 5rem 2rem; }
}

/* ======================
   LG — 992px
   Grille en quatre colonnes
   ====================== */

@media (min-width: 992px) {

  .fonc {
    flex: 1 1 calc(25% - 0.75rem);
  }

  .fonctionnalites { padding: 6rem 2rem; }
  .telechargement  { padding: 6rem 2rem; }
}

/* ======================
   XL — 1200px
   Espacements finaux
   ====================== */

@media (min-width: 1200px) {

  .hero { padding: 100px 40px; }
  .hero-image img { max-height: 560px; }
}

Explications et points de vigilance

min-height: 100vh puis min-height: 100dvh : les deux lignes coexistent. Les navigateurs modernes lisent les deux et appliquent la seconde (dvh). Les anciens ignorent dvh et conservent vh. C'est le pattern de fallback progressif standard — toujours écrire l'ancienne valeur en premier, la nouvelle par-dessus.

clamp() sur tous les titres : chaque <h2> et le <h1> utilisent clamp(). Aucune media query typographique n'est nécessaire. La valeur centrale en vw assure la transition fluide, les bornes min et max garantissent la lisibilité et l'équilibre visuel à toutes les tailles.

font-size: 0.55em sur le <span> dans <h1> : en em, le sous-titre est proportionnel au titre calculé par clamp(). Si clamp() retourne 60px sur grand écran, le span fait 33px. Sur mobile à 28px, il fait 15.4px. La proportion est maintenue automatiquement, sans aucune media query supplémentaire.

padding: 1.5em sur .fonc : le padding interne de chaque carte est en em, proportionnel à sa propre font-size. Si on décide un jour d'augmenter la police de .fonc, les espacements internes s'agrandissent avec elle de manière cohérente.

max-width: 100% + height: auto sur l'image : l'image ne déborde jamais de son conteneur et conserve ses proportions quelle que soit la largeur. object-fit: contain s'assure que même contrainte par max-height, elle n'est pas déformée. Le fond translucide visible si mockup.png n'existe pas montre l'espace réservé pendant le chargement.

9. Transformations, transitions et animations CSS

9.1 Transformations CSS

Les transformations CSS permettent de modifier l'apparence visuelle d'un élément — sa position, son orientation, sa taille, son inclinaison — sans affecter le flux du document. Un élément transformé continue d'occuper son espace d'origine dans la mise en page : les éléments voisins ne bougent pas. C'est une différence fondamentale avec margin ou position, qui eux modifient le flux. Les transformations agissent uniquement sur le rendu visuel, ce qui les rend particulièrement performantes et adaptées aux animations.

9.1.1 Transformations 2D

Toutes les transformations s'appliquent via la propriété transform, qui accepte une ou plusieurs fonctions de transformation. Quand plusieurs fonctions sont combinées, elles s'appliquent de droite à gauche.

translate() — déplacement.

Déplace un élément horizontalement et/ou verticalement par rapport à sa position d'origine, sans sortir du flux.

<!-- translate.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="translate.css">
  <title>translate()</title>
</head>
<body>

  <div class="conteneur">
    <div class="boite reference">Position d'origine</div>
    <div class="boite deplacee">Déplacée</div>
    <div class="boite suivante">Élément suivant — non affecté</div>
  </div>

</body>
</html>
/* translate.css */
body { font-family: Arial, sans-serif; padding: 40px; background-color: #f4f4f4; }
.conteneur {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 400px;
}
.boite {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 16px 20px;
  border-radius: 4px;
  font-size: 0.875rem;
  transition: transform 0.8s ease;
}
.reference {
  background-color: #dde3ed;
  color: #555;
}
.deplacee {
  transform: translate(0px, 0px);
}
.deplacee:hover {
  transform: translate(30px, -10px);
}
.suivante {
  background-color: #1A3A5C;
}

rotate() — rotation.

Fait pivoter un élément autour de son point d'origine (par défaut le centre). L'angle s'exprime en degrés (deg), mais aussi en tours (turn) ou en radians (rad).

<!-- rotate.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="rotate.css">
  <title>rotate()</title>
</head>
<body>

  <div class="galerie">
    <div class="carte">Rotation 0°</div>
    <div class="carte rot-15">Rotation 15°</div>
    <div class="carte rot-neg">Rotation −10°</div>
    <div class="carte rot-45">Rotation 45°</div>
    <div class="carte rot-demi">0.5 tour</div>
  </div>

</body>
</html>
/* rotate.css */
body { font-family: Arial, sans-serif; padding: 60px; background-color: #f4f4f4; }
.galerie {
  display: flex;
  gap: 40px;
  align-items: center;
  flex-wrap: wrap;
}
.carte {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 20px 24px;
  border-radius: 4px;
  font-size: 0.875rem;
  font-weight: bold;
  text-align: center;
  width: 120px;
  transform: rotate(0deg);
  transition: transform 0.8s ease;
}

.rot-demi { background-color: #1A3A5C; }

.rot-15:hover   { transform: rotate(15deg); }
.rot-neg:hover  { transform: rotate(-10deg); }
.rot-45:hover   { transform: rotate(45deg); }
.rot-demi:hover { transform: rotate(0.5turn); }

scale() — mise à l'échelle.

Agrandit ou rétrécit un élément. scale(1) est la taille naturelle. scale(2) double la taille. scale(0.5) réduit de moitié. L'élément transformé continue d'occuper son espace d'origine dans le flux — l'espace autour de lui ne change pas.

<!-- scale.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="scale.css">
  <title>scale()</title>
</head>
<body>

  <div class="galerie">
    <div class="boite">scale(1)<br>Normal</div>
    <div class="boite agrandie">scale(1.3)<br>Agrandi</div>
    <div class="boite reduite">scale(0.7)<br>Réduit</div>
    <div class="boite miroir">scale(−1, 1)<br>Miroir horizontal</div>
  </div>

</body>
</html>
/* scale.css */
body { font-family: Arial, sans-serif; padding: 80px; background-color: #f4f4f4; }
.galerie {
  display: flex;
  gap: 40px;
  align-items: center;
  flex-wrap: wrap;
}
.boite {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 20px;
  border-radius: 4px;
  font-size: 0.8125rem;
  text-align: center;
  width: 110px;
  line-height: 1.5;
  transition: transform 0.8s ease;
}

.agrandie { transform: scale(1);    background-color: #1A3A5C; }
.reduite  { transform: scale(1);    background-color: #4a8ac4; }
.miroir   { transform: scale(1, 1); background-color: #C0392B; }

.agrandie:hover { transform: scale(1.3); }
.reduite:hover  { transform: scale(0.7); }
.miroir:hover   { transform: scale(-1, 1); }

skew() — inclinaison.

Incline un élément le long des axes horizontal et/ou vertical, créant un effet de cisaillement.

.element { transform: skewX(20deg);      } /* Inclinaison horizontale */
.element { transform: skewY(10deg);      } /* Inclinaison verticale */
.element { transform: skew(20deg, 10deg);} /* Les deux axes */

transform-origin — changer le point d'ancrage.

Par défaut, toutes les transformations pivotent autour du centre de l'élément. transform-origin permet de déplacer ce point d'ancrage.

<!-- transform-origin.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="transform-origin.css">
  <title>transform-origin</title>
</head>
<body>

  <div class="scene">
    <div class="boite origine-centre">Centre (défaut)</div>
    <div class="boite origine-haut-gauche">Haut gauche</div>
    <div class="boite origine-bas-droite">Bas droite</div>
  </div>

</body>
</html>
/* transform-origin.css */
body { font-family: Arial, sans-serif; padding: 60px; background-color: #f4f4f4; }
.scene {
  display: flex;
  gap: 60px;
  align-items: center;
}
.boite {
  background-color: #2E6DA4;
  color: #ffffff;
  padding: 20px 16px;
  border-radius: 4px;
  font-size: 0.8125rem;
  text-align: center;
  width: 120px;
  transform: rotate(0deg);
  transition: transform 0.8s ease;
}

.origine-centre      { transform-origin: center center; }
.origine-haut-gauche { transform-origin: top left;     background-color: #C0392B; }
.origine-bas-droite  { transform-origin: bottom right; background-color: #1A3A5C; }

.boite:hover { transform: rotate(20deg); }

Combiner plusieurs transformations.

On peut chaîner plusieurs fonctions dans une même déclaration transform. Attention : l'ordre compte. Les transformations s'appliquent de droite à gauche — la dernière écrite est la première appliquée.

/* Déplace d'abord, puis fait pivoter */
.element {
  transform: rotate(45deg) translate(100px, 0);
}

/* Fait pivoter d'abord, puis déplace
   (résultat visuel différent du précédent) */
.element {
  transform: translate(100px, 0) rotate(45deg);
}

/* Combinaison typique pour un effet de bouton au survol */
.btn:hover {
  transform: translateY(-3px) scale(1.02);
}

transform ne crée pas de scroll horizontal. Contrairement à position: relative avec left: -200px, une transformation négative ne génère pas de barre de défilement. Le moteur de rendu gère les transformations après le calcul du flux — elles sont purement visuelles.

9.1.2 Introduction aux transformations 3D

Les transformations 3D ajoutent un troisième axe — la profondeur, noté Z — qui permet de faire basculer, pivoter ou déplacer des éléments dans un espace tridimensionnel simulé. Elles reposent sur deux mécanismes supplémentaires : la perspective et la profondeur de scène.

La perspective.

Sans perspective, les transformations 3D existent mais ne produisent aucun effet de profondeur visible. La propriété perspective définit la distance entre l'observateur et le plan de l'écran. Plus cette valeur est petite, plus l'effet de perspective est prononcé.

/*
  perspective s'applique sur le CONTENEUR parent, pas sur l'élément transformé.
  C'est une règle fondamentale souvent source d'erreur.
*/
.scene {
  perspective: 600px; /* Distance de l'observateur — plus petit = plus fort */
}

.carte {
  transform: rotateY(45deg); /* Rotation 3D autour de l'axe vertical */
}

Les fonctions de rotation 3D.

<!-- rotations-3d.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="rotations-3d.css">
  <title>Rotations 3D</title>
</head>
<body>

  <h2>Avec perspective — effet de profondeur visible</h2>
  <div class="scene avec-perspective">
    <div class="boite">rotateX(40deg)</div>
    <div class="boite rot-y">rotateY(40deg)</div>
    <div class="boite rot-z">rotateZ(40deg)</div>
  </div>

  <h2>Sans perspective — les rotations semblent plates</h2>
  <div class="scene sans-perspective">
    <div class="boite">rotateX(40deg)</div>
    <div class="boite rot-y">rotateY(40deg)</div>
    <div class="boite rot-z">rotateZ(40deg)</div>
  </div>

</body>
</html>
/* rotations-3d.css */
body { font-family: Arial, sans-serif; padding: 40px; background-color: #f4f4f4; }
h2 {
  color: #1A3A5C;
  font-size: 0.9375rem;
  margin-bottom: 24px;
  margin-top: 40px;
}
.scene {
  display: flex;
  gap: 40px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.avec-perspective  { perspective: 500px; }
.sans-perspective  { }

.boite {
  color: #ffffff;
  padding: 24px 20px;
  border-radius: 4px;
  font-size: 0.8125rem;
  text-align: center;
  width: 130px;
  line-height: 1.5;
  transition: transform 0.8s ease;
}

.boite      { transform: rotateX(0deg); background-color: #C0392B; }
.rot-y      { transform: rotateY(0deg); background-color: #2E6DA4; }
.rot-z      { transform: rotateZ(0deg); background-color: #1A3A5C; }

.boite:hover      { transform: rotateX(40deg); }
.rot-y:hover      { transform: rotateY(40deg); }
.rot-z:hover      { transform: rotateZ(40deg); }

translateZ() et perspective-origin.

translateZ() rapproche ou éloigne un élément de l'observateur. Une valeur positive rapproche (l'élément paraît plus grand), une valeur négative éloigne (l'élément paraît plus petit).

perspective-origin déplace le point de fuite de la perspective — l'équivalent du point de vue de l'observateur.

.scene {
  perspective: 600px;
  perspective-origin: center center;
  width: 300px;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.proche {
  transform: translateZ(0px);
  transition: transform 0.8s ease;
  width: 80px;
  height: 80px;
  background: #e74c3c;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

.proche:hover {
  transform: translateZ(100px);
}

.lointain {
  transform: translateZ(0px);
  transition: transform 0.8s ease;
  width: 80px;
  height: 80px;
  background: #3498db;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lointain:hover {
  transform: translateZ(-100px);
}
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Perspective 3D</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <div class="scene">
    <div class="proche">Proche</div>
    <div class="lointain">Lointain</div>
  </div>

</body>
</html>

transform-style: preserve-3d.

Par défaut, les enfants d'un élément transformé en 3D sont aplatis dans le plan de l'écran. transform-style: preserve-3d préserve l'espace 3D pour les éléments enfants, ce qui permet de construire des objets 3D composites comme des cubes ou des cartes retournables.

.scene {
  perspective: 800px;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 200px;
}

.objet-3d {
  transform-style: preserve-3d;
  transform: rotateY(0deg);
  transition: transform 1s ease;
  position: relative;
  width: 150px;
  height: 150px;
}

.scene:hover .objet-3d {
  transform: rotateY(45deg);
}

.face-avant,
.face-arriere {
  position: absolute;
  width: 150px;
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 1.2rem;
}

.face-avant {
  transform: translateZ(50px);
  background: #e74c3c;
}

.face-arriere {
  transform: translateZ(-50px);
  background: #3498db;
}

Les transformations 3D et les performances : les transformations utilisant translateZ() ou translate3d() — même avec une valeur nulle — activent l'accélération matérielle du GPU dans la plupart des navigateurs. C'est pourquoi on voit parfois transform: translateZ(0) utilisé comme hack de performance pour forcer cette accélération sur des animations complexes. Cette technique est moins nécessaire sur les navigateurs modernes, mais reste ancrée dans de nombreuses bases de code.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Transform 3D</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <div class="scene">
    <div class="objet-3d">
      <div class="face-avant">Avant</div>
      <div class="face-arriere">Arrière</div>
    </div>
  </div>

</body>
</html>

Exercice 29 — Les cartes de compétences interactives

Objectif : construire une grille de cartes de compétences techniques avec des effets de transformation au survol. Chaque carte se soulève et s'agrandit légèrement au passage de la souris. L'une d'elles intègre un effet de retournement 3D révélant une face cachée avec une description. L'exercice travaille translate, scale, rotate, transform-origin et l'introduction au 3D avec rotateY et preserve-3d.

C'est un composant directement réutilisable : grille de compétences pour un portfolio, cartes de services pour un site vitrine, présentation de fonctionnalités.

Consignes HTML :

  • Créez un fichier competences.html avec la balise <meta name="viewport">.
  • Créez une <section class="section-comp"> avec un <h1> et une <div class="grille">.
  • Dans .grille, créez six <div class="carte">. Les cinq premières ont deux enfants : une <div class="carte-icone"> (une lettre ou emoji technique) et un <div class="carte-corps"> avec un <h2> et un <p> court.
  • La sixième carte a la classe supplémentaire carte-flip. Elle contient deux faces : une <div class="face face-avant"> (icône + titre) et une <div class="face face-arriere"> (un <h2> et une description plus longue).
  • Liez un fichier competences.css.

Consignes CSS :

Avant d'écrire quoi que ce soit, imaginez ce que l'utilisateur ressent au survol de chaque carte. La transformation doit renforcer l'interaction, pas la parasiter.

  • Mise en page : la grille utilise Flexbox avec flex-wrap. Les cartes font environ 280px de large minimum et s'adaptent à l'espace disponible. Fond de page sombre.

  • Cartes simples (les cinq premières) : au repos, chaque carte a un fond sombre, du padding, une bordure subtile et une ombre légère. Au survol, la carte doit se soulever visuellement et s'agrandir très légèrement. Réfléchissez à quelles fonctions de transformation combiner pour obtenir cet effet. La transition doit être fluide — nous n'avons pas encore étudié les transitions formellement, mais la propriété transition: transform 0.25s ease suffit pour cet exercice.

  • .carte-icone : un grand symbole centré dans un cercle ou carré arrondi coloré. Au survol de la carte, l'icône effectue une légère rotation. Réfléchissez à quel transform-origin utiliser pour que la rotation semble naturelle.

  • Carte retournable (.carte-flip) : la carte se retourne sur l'axe vertical au survol pour révéler sa face arrière. Pour cela : le conteneur .carte-flip a une hauteur fixe et sert de scène avec perspective. Les deux .face sont superposées avec position: absolute et pleine largeur/hauteur. La face arrière est pré-retournée de 180° pour être lisible une fois le retournement effectué. Au survol de .carte-flip, l'élément intérieur effectue une rotation de 180° autour de l'axe Y. backface-visibility: hidden cache chaque face quand elle est dos à l'observateur.

Correction détaillée — Exercice 29

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="competences.css">
  <title>Compétences techniques</title>
</head>
<body>

  <section class="section-comp">
    <h1>Compétences</h1>
    <div class="grille">

      <div class="carte">
        <div class="carte-icone ic-html">H</div>
        <div class="carte-corps">
          <h2>HTML</h2>
          <p>Structure sémantique, accessibilité, formulaires et balises modernes HTML5.</p>
        </div>
      </div>

      <div class="carte">
        <div class="carte-icone ic-css">C</div>
        <div class="carte-corps">
          <h2>CSS</h2>
          <p>Flexbox, Grid, animations, responsive design et architecture CSS scalable.</p>
        </div>
      </div>

      <div class="carte">
        <div class="carte-icone ic-js">J</div>
        <div class="carte-corps">
          <h2>JavaScript</h2>
          <p>ES6+, manipulation du DOM, fetch API, programmation asynchrone.</p>
        </div>
      </div>

      <div class="carte">
        <div class="carte-icone ic-react">R</div>
        <div class="carte-corps">
          <h2>React</h2>
          <p>Composants, hooks, gestion d'état et intégration d'API REST.</p>
        </div>
      </div>

      <div class="carte">
        <div class="carte-icone ic-git">G</div>
        <div class="carte-corps">
          <h2>Git</h2>
          <p>Versioning, branches, pull requests et collaboration en équipe.</p>
        </div>
      </div>

      <!-- Carte retournable -->
      <div class="carte carte-flip">
        <div class="carte-flip-inner">

          <div class="face face-avant">
            <div class="carte-icone ic-figma">F</div>
            <div class="carte-corps">
              <h2>Figma</h2>
              <p>Survolez pour en savoir plus</p>
            </div>
          </div>

          <div class="face face-arriere">
            <h2>Figma</h2>
            <p>Conception d'interfaces, prototypage interactif, création de design systems et collaboration en temps réel avec les équipes produit et développement.</p>
          </div>

        </div>
      </div>

    </div>
  </section>

</body>
</html>

Correction CSS

/* competences.css */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #0d1117;
  color: #e6edf3;
  padding: 60px 24px;
}

.section-comp {
  max-width: 1000px;
  margin: 0 auto;
}

.section-comp h1 {
  font-size: 2rem;
  color: #e6edf3;
  margin-bottom: 40px;
  letter-spacing: -0.5px;
}

/* --- GRILLE --- */

.grille {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

/* --- CARTES SIMPLES --- */

.carte {
  flex: 1 1 280px;
  background-color: #161b22;
  border: 1px solid #30363d;
  border-radius: 10px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  cursor: default;
  /*
    transition sur transform et box-shadow pour anticiper le survol.
    On utilise transition ici de façon pragmatique avant la section 9.2.
  */
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.carte:hover {
  /*
    translateY(-6px) : soulève la carte de 6px vers le haut.
    scale(1.02) : agrandit très légèrement pour accentuer l'effet.
    Combinés, ils donnent une impression de profondeur et d'interactivité.
  */
  transform: translateY(-6px) scale(1.02);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
}

/* --- ICÔNES --- */

.carte-icone {
  width: 52px;
  height: 52px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.375rem;
  font-weight: bold;
  color: #ffffff;
  /*
    transform-origin: bottom center :
    la rotation part du bas de l'icône, comme une aiguille qui pivote.
    L'effet est plus naturel qu'une rotation autour du centre.
  */
  transform-origin: bottom center;
  transition: transform 0.3s ease;
}

/* Au survol de la carte, l'icône tourne légèrement */
.carte:hover .carte-icone {
  transform: rotate(-8deg);
}

/* Couleurs d'icône par technologie */
.ic-html  { background-color: #e34c26; }
.ic-css   { background-color: #264de4; }
.ic-js    { background-color: #f0db4f; color: #0d1117; }
.ic-react { background-color: #61dafb; color: #0d1117; }
.ic-git   { background-color: #f05032; }
.ic-figma { background-color: #a259ff; }

.carte-corps h2 {
  font-size: 1rem;
  color: #e6edf3;
  margin-bottom: 6px;
}

.carte-corps p {
  font-size: 0.8125rem;
  color: #8b949e;
  line-height: 1.6;
}

/* --- CARTE RETOURNABLE --- */

/*
  La carte-flip est la scène 3D : elle définit la perspective
  et les dimensions. Elle ne se transforme pas elle-même.
*/
.carte-flip {
  perspective: 900px;
  /*
    On retire les propriétés de la carte standard
    qui entreraient en conflit avec la structure 3D.
  */
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  min-height: 200px;
}

/* Annuler le hover de .carte sur .carte-flip */
.carte-flip:hover {
  transform: none;
  box-shadow: none;
}

/*
  L'élément intérieur est celui qui se retourne.
  transform-style: preserve-3d : ses enfants (face-avant et face-arriere)
  vivent dans l'espace 3D — sans ça, la face arrière serait invisible.
*/
.carte-flip-inner {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 200px;
  transform-style: preserve-3d;
  transition: transform 0.6s ease;
}

/* Au survol, l'inner fait un demi-tour sur l'axe Y */
.carte-flip:hover .carte-flip-inner {
  transform: rotateY(180deg);
}

/* Les deux faces sont superposées en position absolue */
.face {
  position: absolute;
  inset: 0; /* Équivalent à top:0; right:0; bottom:0; left:0 */
  border-radius: 10px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  /*
    backface-visibility: hidden :
    cache la face quand elle est dos à l'observateur (retournée de 180°).
    Sans ça, on verrait les deux faces en même temps, en miroir.
  */
  backface-visibility: hidden;
}

.face-avant {
  background-color: #161b22;
  border: 1px solid #30363d;
}

.face-arriere {
  background-color: #1a3a5c;
  border: 1px solid #2e6da4;
  /*
    La face arrière est pré-retournée de 180°.
    Quand l'inner fait son demi-tour, elle revient à 0° et devient lisible.
  */
  transform: rotateY(180deg);
  justify-content: center;
}

.face-arriere h2 {
  font-size: 1rem;
  color: #a8c4e0;
  margin-bottom: 4px;
}

.face-arriere p {
  font-size: 0.8125rem;
  color: #c8daf0;
  line-height: 1.65;
}

Explications et points de vigilance

L'ordre des transformations dans .carte:hover : translateY(-6px) scale(1.02) applique d'abord le scale (de droite à gauche), puis le translate. L'ordre inverse — scale(1.02) translateY(-6px) — produirait un déplacement légèrement différent car le scale modifie le référentiel dans lequel translate opère. Dans ce cas précis la différence est imperceptible, mais garder cette règle en tête évite des surprises sur des transformations plus complexes.

transform-origin: bottom center sur .carte-icone : avec le centre par défaut, la rotation de l'icône ressemblerait à une roue qui tourne sur place. Avec le bas comme point d'ancrage, l'icône se balance comme un pendule — ce qui est visuellement plus organique et agréable.

perspective sur .carte-flip, pas sur .carte-flip-inner : c'est l'erreur la plus classique avec les transformations 3D. La perspective définit le point de vue de l'observateur — elle doit être sur le conteneur parent de l'élément qui se transforme. Mise sur l'inner lui-même, elle s'annulerait avec son propre transform et produirait un résultat aplati.

backface-visibility: hidden sur les deux faces : sans cette propriété, pendant le retournement on verrait simultanément les deux faces superposées en miroir. Appliquée sur chaque .face, elle garantit que seule la face orientée vers l'observateur est visible à chaque instant.

inset: 0 : c'est la propriété raccourcie moderne pour top: 0; right: 0; bottom: 0; left: 0. Elle positionne l'élément pour qu'il remplisse exactement son conteneur position: relative. Supportée par tous les navigateurs modernes.

9.2 Transitions CSS

Une transition est le passage animé d'un état CSS à un autre. Sans transition, les changements de propriétés sont instantanés — un bouton change de couleur au survol sans aucune progressivité. Avec une transition, ce changement se produit sur une durée définie, selon une courbe de vitesse choisie. C'est l'outil le plus simple et le plus utilisé pour donner de la fluidité à une interface.

9.2.1 Principe des transitions

Une transition se définit sur l'état de départ de l'élément, pas sur l'état cible. C'est une distinction fondamentale : en écrivant transition sur .btn, vous dites "quand une propriété de cet élément change, anime ce changement". Peu importe d'où vient le changement — survol, focus, classe ajoutée en JavaScript, attribut modifié.

/* La transition est déclarée sur l'état de départ */
.btn {
  background-color: #2E6DA4;
  transition: background-color 0.3s ease;
}

/* L'état cible ne contient que les nouvelles valeurs */
.btn:hover {
  background-color: #1A3A5C;
}

Syntaxe complète.

.element {
  transition: propriété durée timing-function délai;
}

/* Exemples */
.element { transition: opacity 0.3s ease; }
.element { transition: transform 0.5s ease-in-out 0.1s; }

/* Animer plusieurs propriétés : séparées par des virgules */
.element {
  transition:
    background-color 0.3s ease,
    transform 0.2s ease,
    box-shadow 0.3s ease;
}

/* Animer toutes les propriétés modifiées */
.element {
  transition: all 0.3s ease;
  /*
    Pratique mais déconseillé en production : "all" anime aussi
    des propriétés auxquelles on ne pense pas (height, width, font-size),
    ce qui peut produire des effets indésirables et des
    performances dégradées. Préférez toujours cibler les propriétés
    explicitement.
  */
}
<!-- transitions-base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="transitions-base.css">
  <title>Transitions — base</title>
</head>
<body>

  <h2>Sans transition (changement instantané)</h2>
  <button class="btn sans-transition">Survolez-moi</button>

  <h2>Avec transition</h2>
  <button class="btn avec-transition">Survolez-moi</button>

  <h2>Plusieurs propriétés</h2>
  <button class="btn multi-transition">Survolez-moi</button>

</body>
</html>
/* transitions-base.css */

body { font-family: Arial, sans-serif; padding: 40px; background-color: #f4f4f4; }
h2   { color: #1A3A5C; font-size: 0.9375rem; margin: 24px 0 10px; }

.btn {
  display: inline-block;
  padding: 12px 28px;
  border: none;
  border-radius: 6px;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  background-color: #2E6DA4;
  color: #ffffff;
}

/* Aucune transition : le changement est instantané */
.sans-transition:hover {
  background-color: #C0392B;
  transform: scale(1.05);
}

/* Transition sur les propriétés ciblées */
.avec-transition {
  transition: background-color 0.35s ease;
}

.avec-transition:hover {
  background-color: #C0392B;
}

/* Plusieurs propriétés animées simultanément */
.multi-transition {
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
  transition:
    background-color 0.3s ease,
    transform 0.2s ease,
    box-shadow 0.3s ease;
}

.multi-transition:hover {
  background-color: #1A3A5C;
  transform: translateY(-3px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}

9.2.2 Propriétés animables

Toutes les propriétés CSS ne peuvent pas être animées. Pour qu'une transition fonctionne, la propriété doit être interpolable — c'est-à-dire que le navigateur doit pouvoir calculer des valeurs intermédiaires entre l'état de départ et l'état d'arrivée.

Propriétés animables courantes.

/* Couleurs */
color, background-color, border-color, outline-color, box-shadow

/* Dimensions et espacement */
width, height, max-width, max-height
padding, margin
border-width, border-radius

/* Positionnement */
top, right, bottom, left
transform (translate, rotate, scale, skew)

/* Visibilité */
opacity

/* Typographie */
font-size, line-height, letter-spacing, word-spacing

Propriétés non animables.

/*
  Ces propriétés changent instantanément, sans transition possible.
  Le navigateur ne peut pas interpoler entre leurs valeurs.
*/
display       /* block → flex → none : pas d'état intermédiaire */
visibility    /* visible → hidden : discret, pas de dégradé */
position      /* static → absolute : changement structural */
font-family   /* Impossible de "passer" d'une police à une autre */

display ne peut pas être animé — le cas le plus courant de surprise. Si vous voulez faire apparaître un élément avec un fondu, vous ne pouvez pas animer display: none → display: block. La technique classique est de combiner opacity (animable) avec visibility (non animable mais qui peut être différée) ou de manipuler opacity seule sur un élément toujours présent dans le DOM.

/* Mauvaise approche : display ne s'anime pas */
.menu {
  display: none;
  transition: display 0.3s ease; /* Ignoré */
}
.menu.ouvert { display: block; }

/* Bonne approche : opacity + visibility */
.menu {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}

.menu.ouvert {
  opacity: 1;
  visibility: visible;
}

Performances et propriétés à privilégier.

Toutes les propriétés animables ne sont pas égales en termes de performances. Le navigateur doit recalculer la mise en page à chaque image d'animation pour certaines propriétés — ce qui peut produire des ralentissements sur des appareils moins puissants.

/*
  Propriétés les plus performantes — animées par le GPU,
  sans recalcul du layout :
*/
transform: translate(), rotate(), scale()
opacity

/*
  Propriétés à éviter dans les animations — recalculent le layout
  à chaque image :
*/
width, height
padding, margin
top, left, right, bottom
font-size

Règle d'or des animations performantes : pour déplacer un élément, utilisez transform: translate() plutôt que top/left. Pour le faire apparaître/disparaître, utilisez opacity plutôt que display. Ces deux propriétés sont gérées directement par le GPU et n'affectent pas le flux du document.

9.2.3 Gestion de la durée et du timing

La durée et la courbe de vitesse d'une transition sont aussi importantes que la propriété animée. Une transition trop lente est frustrante. Trop rapide, elle est imperceptible. Une courbe de vitesse mal choisie rend l'animation mécanique ou artificielle.

La durée.

La durée s'exprime en secondes (s) ou millisecondes (ms). Elle n'existe pas de valeur universelle parfaite, mais des plages recommandées selon le type d'interaction.

/* Micro-interactions (hover, focus) : 150–300ms */
.btn { transition: background-color 0.2s ease; }

/* Apparitions, disparitions de panneaux : 250–400ms */
.panneau { transition: transform 0.35s ease; }

/* Animations d'entrée de page, modales : 300–500ms */
.modale { transition: opacity 0.4s ease; }

/*
  Au-delà de 500ms, une transition de navigation devient perceptible
  comme un délai — l'utilisateur attend.
  En dessous de 100ms, elle est quasi imperceptible.
*/

Les fonctions de timing (transition-timing-function).

La fonction de timing définit la courbe de vitesse — comment la propriété évolue entre sa valeur de départ et sa valeur d'arrivée.

<!-- timing-functions.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="timing-functions.css">
  <title>Timing functions</title>
</head>
<body>

  <p class="instruction">Survolez la zone pour voir les différences de courbe.</p>

  <div class="scene" id="scene">
    <div class="ligne">
      <span class="label">linear</span>
      <div class="barre"><div class="balle linear">●</div></div>
    </div>
    <div class="ligne">
      <span class="label">ease (défaut)</span>
      <div class="barre"><div class="balle ease">●</div></div>
    </div>
    <div class="ligne">
      <span class="label">ease-in</span>
      <div class="barre"><div class="balle ease-in">●</div></div>
    </div>
    <div class="ligne">
      <span class="label">ease-out</span>
      <div class="barre"><div class="balle ease-out">●</div></div>
    </div>
    <div class="ligne">
      <span class="label">ease-in-out</span>
      <div class="barre"><div class="balle ease-in-out">●</div></div>
    </div>
    <div class="ligne">
      <span class="label">cubic-bezier</span>
      <div class="barre"><div class="balle custom">●</div></div>
    </div>
  </div>

</body>
</html>
/* timing-functions.css */

body { font-family: Arial, sans-serif; padding: 40px; background-color: #0d1117; color: #e6edf3; }

.instruction {
  font-size: 0.875rem;
  color: #8b949e;
  margin-bottom: 30px;
}

.scene { max-width: 700px; }

.ligne {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
}

.label {
  font-size: 0.8125rem;
  color: #8b949e;
  width: 130px;
  flex-shrink: 0;
  font-family: monospace;
}

.barre {
  flex: 1;
  height: 36px;
  background-color: #161b22;
  border: 1px solid #30363d;
  border-radius: 4px;
  position: relative;
  overflow: hidden;
}

.balle {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 18px;
  color: #2E6DA4;
  transition-property: left;
  transition-duration: 1.2s;
}

/* Chaque balle a sa propre courbe de vitesse */
.linear      { transition-timing-function: linear; }
.ease        { transition-timing-function: ease; }
.ease-in     { transition-timing-function: ease-in; }
.ease-out    { transition-timing-function: ease-out; }
.ease-in-out { transition-timing-function: ease-in-out; }

/*
  cubic-bezier(x1, y1, x2, y2) : courbe de Bézier personnalisée.
  Les quatre valeurs définissent les poignées de contrôle de la courbe.
  Outil interactif en ligne : cubic-bezier.com
*/
.custom      { transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); color: #e8ff47; }

/* Au survol de la scène, toutes les balles se déplacent vers la droite */
.scene:hover .balle {
  left: calc(100% - 28px);
}

Description des courbes de timing.

Valeur Comportement Usage typique
linear Vitesse constante du début à la fin Rotations continues, loaders
ease Démarre vite, ralentit à la fin (défaut) Transitions générales
ease-in Démarre lentement, finit vite Éléments qui disparaissent
ease-out Démarre vite, finit lentement Éléments qui apparaissent
ease-in-out Lent au début et à la fin Glissements, panneaux
cubic-bezier() Courbe personnalisée Effets élastiques, rebonds

Le délai (transition-delay).

Le délai diffère le démarrage de la transition. Il s'exprime dans la même unité que la durée et se place en quatrième position dans la propriété raccourcie.

.element {
  /* propriété durée timing délai */
  transition: opacity 0.3s ease 0.1s;
}

/* Délais échelonnés pour des éléments en cascade */
.item:nth-child(1) { transition-delay: 0s;    }
.item:nth-child(2) { transition-delay: 0.05s; }
.item:nth-child(3) { transition-delay: 0.1s;  }
.item:nth-child(4) { transition-delay: 0.15s; }
/*
  Chaque élément démarre sa transition légèrement après le précédent.
  Cet effet "cascade" ou "stagger" est très utilisé pour les listes
  et les grilles qui apparaissent à l'écran.
*/

Exercice 30 — La navigation avec méga-menu

Objectif : construire une barre de navigation avec un méga-menu qui s'affiche en douceur au survol d'un lien. Le méga-menu contient plusieurs colonnes de liens. L'exercice travaille les transitions sur opacity, visibility et transform, le choix des courbes de timing selon la direction de l'interaction (apparition vs disparition), et les délais échelonnés sur les colonnes du menu.

C'est un composant que vous retrouverez dans tout site e-commerce, portail ou application métier.

Consignes HTML :

  • Créez un fichier mega-menu.html avec la balise <meta name="viewport">.
  • Créez un <header class="header"> contenant une <div class="header-inner"> avec :
    • Un <span class="logo"> (texte : Nexus).
    • Un <nav class="nav"> avec quatre <div class="nav-item">. Les trois premiers ont un <a class="nav-lien"> simple. Le quatrième a un <a class="nav-lien nav-lien-actif"> (texte : Solutions) et une <div class="mega-menu">.
    • Un <div class="nav-actions"> avec un <a class="btn-nav"> (Démo gratuite).
  • Dans .mega-menu, créez trois <div class="mega-col">. Chaque colonne contient un <p class="mega-titre"> et trois <a class="mega-lien"> avec un <span class="mega-lien-titre"> et un <span class="mega-lien-desc">.
  • Créez un <main class="page-hero"> avec un <h1> et un <p>.
  • Liez un fichier mega-menu.css.

Consignes CSS :

Avant d'écrire les transitions, réfléchissez à la direction de chaque interaction : le menu apparaît (entrée) ou disparaît (sortie) ? Quelle courbe de timing correspond à chaque cas ?

  • Header : fixe en haut de la page, fond sombre, hauteur définie. Le .nav-item est le déclencheur de positionnement — c'est lui qui doit avoir position: relative pour que le méga-menu se positionne par rapport à lui.

  • Méga-menu au repos : invisible mais présent dans le DOM (pas de display: none). Trouvez la combinaison de propriétés qui le rend invisible et l'empêche d'intercepter les clics, tout en permettant une transition fluide à l'apparition.

  • Méga-menu à l'apparition : lorsque l'utilisateur survole .nav-item, le menu apparaît avec un fondu et un léger glissement vers le bas. Choisissez une courbe de timing adaptée à une entrée. Les trois colonnes apparaissent avec des délais échelonnés — la première immédiatement, les suivantes légèrement après.

  • Méga-menu à la disparition : quand la souris quitte .nav-item, le menu disparaît. La courbe de timing doit être différente de l'apparition. Le délai échelonné s'inverse : sans délai sur le conteneur pour que la disparition soit réactive.

  • Contenu du menu : les .mega-col ont chacune un titre de catégorie et des liens avec un titre et une courte description sur deux lignes. Les liens ont un léger fond au survol, lui-même avec une transition courte.

Correction détaillée — Exercice 30

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="mega-menu.css">
  <title>Mega menu — Nexus</title>
</head>
<body>

  <header class="header">
    <div class="header-inner">

      <span class="logo">Nexus</span>

      <nav class="nav">

        <div class="nav-item">
          <a href="#" class="nav-lien">Produits</a>
        </div>

        <div class="nav-item">
          <a href="#" class="nav-lien">Tarifs</a>
        </div>

        <div class="nav-item">
          <a href="#" class="nav-lien">Ressources</a>
        </div>

        <!-- Élément déclencheur du méga-menu -->
        <div class="nav-item nav-item-menu">
          <a href="#" class="nav-lien nav-lien-actif">
            Solutions
            <span class="fleche">▾</span>
          </a>

          <div class="mega-menu">
            <div class="mega-col">
              <p class="mega-titre">Par secteur</p>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Finance</span>
                <span class="mega-lien-desc">Conformité, reporting et audit automatisés</span>
              </a>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Santé</span>
                <span class="mega-lien-desc">Gestion de données patients et interopérabilité</span>
              </a>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Industrie</span>
                <span class="mega-lien-desc">Suivi de production et maintenance prédictive</span>
              </a>
            </div>

            <div class="mega-col">
              <p class="mega-titre">Par équipe</p>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Direction</span>
                <span class="mega-lien-desc">Tableaux de bord exécutifs en temps réel</span>
              </a>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Développeurs</span>
                <span class="mega-lien-desc">API REST, webhooks et documentation complète</span>
              </a>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Marketing</span>
                <span class="mega-lien-desc">Segmentation, campagnes et analytics intégrés</span>
              </a>
            </div>

            <div class="mega-col">
              <p class="mega-titre">À la une</p>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Nexus AI</span>
                <span class="mega-lien-desc">Intelligence artificielle embarquée dans votre workflow</span>
              </a>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Nexus Connect</span>
                <span class="mega-lien-desc">Plus de 200 intégrations natives avec vos outils</span>
              </a>
              <a href="#" class="mega-lien">
                <span class="mega-lien-titre">Migration guidée</span>
                <span class="mega-lien-desc">Passez d'un autre outil en moins de 48 heures</span>
              </a>
            </div>
          </div>

        </div>
      </nav>

      <div class="nav-actions">
        <a href="#" class="btn-nav">Démo gratuite</a>
      </div>

    </div>
  </header>

  <main class="page-hero">
    <h1>La plateforme qui connecte vos équipes.</h1>
    <p>Scrollez ou survolez "Solutions" dans la navigation pour voir le méga-menu.</p>
  </main>

</body>
</html>

Correction CSS

/* mega-menu.css */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #f0f4f8;
  color: #1a1a2e;
}

/* =====================
   HEADER
   ===================== */

.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background-color: #0d1117;
  border-bottom: 1px solid #30363d;
}

.header-inner {
  max-width: 1140px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  height: 60px;
  padding: 0 24px;
  gap: 32px;
}

.logo {
  font-size: 1.25rem;
  font-weight: bold;
  letter-spacing: 2px;
  color: #e8ff47;
  flex-shrink: 0;
}

/* =====================
   NAVIGATION
   ===================== */

.nav {
  display: flex;
  align-items: stretch; /* Les nav-item s'étirent sur toute la hauteur du header */
  gap: 2px;
  flex: 1;
}

/*
  position: relative sur .nav-item :
  le méga-menu se positionnera par rapport à lui,
  pas par rapport au header ou à la page.
*/
.nav-item {
  position: relative;
  display: flex;
  align-items: center;
}

.nav-lien {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 14px;
  color: rgba(255, 255, 255, 0.65);
  text-decoration: none;
  font-size: 0.9rem;
  height: 100%;
  border-radius: 4px;
  transition: color 0.2s ease, background-color 0.2s ease;
}

.nav-lien:hover,
.nav-lien-actif {
  color: #ffffff;
  background-color: rgba(255, 255, 255, 0.06);
}

.fleche {
  font-size: 0.7rem;
  opacity: 0.6;
  transition: transform 0.2s ease;
}

.nav-item-menu:hover .fleche {
  transform: rotate(180deg);
}

.nav-actions {
  margin-left: auto;
  flex-shrink: 0;
}

.btn-nav {
  display: inline-block;
  background-color: #e8ff47;
  color: #0d1117;
  padding: 8px 18px;
  text-decoration: none;
  font-weight: bold;
  font-size: 0.875rem;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

.btn-nav:hover { background-color: #d4eb30; }

/* =====================
   MÉGA-MENU — ÉTAT DE REPOS
   ===================== */

.mega-menu {
  position: absolute;
  top: calc(100% + 8px); /* 8px sous le bas du nav-item */
  left: 0;
  width: 680px;
  background-color: #161b22;
  border: 1px solid #30363d;
  border-radius: 10px;
  padding: 24px;
  display: flex;
  gap: 0;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);

  /*
    Invisible mais présent dans le DOM — permet la transition.
    opacity: 0 rend invisible.
    visibility: hidden empêche les clics sur les liens masqués.
    transform: translateY(-8px) décale le menu vers le haut
    pour préparer le glissement d'entrée.
  */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);

  /*
    Transitions de SORTIE :
    ease-in pour la sortie — accélère vers la disparition.
    Pas de délai pour que la disparition soit réactive.
  */
  transition:
    opacity 0.2s ease-in,
    visibility 0.2s ease-in,
    transform 0.2s ease-in;
}

/* =====================
   MÉGA-MENU — APPARITION
   ===================== */

.nav-item-menu:hover .mega-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);

  /*
    Transitions d'ENTRÉE :
    ease-out pour l'entrée — démarre vite, ralentit en arrivant.
    Légèrement plus long que la sortie pour que le contenu
    soit lisible avant la fin de l'animation.
  */
  transition:
    opacity 0.25s ease-out,
    visibility 0.25s ease-out,
    transform 0.25s ease-out;
}

/* =====================
   COLONNES DU MÉGA-MENU
   ===================== */

.mega-col {
  flex: 1;
  padding: 0 20px;
  border-right: 1px solid #30363d;

  /*
    État de repos : légèrement décalé vers le haut et transparent.
    Chaque colonne prépare son propre glissement.
  */
  opacity: 0;
  transform: translateY(-6px);
  transition:
    opacity 0.2s ease-in,
    transform 0.2s ease-in;
}

.mega-col:last-child {
  border-right: none;
}

/*
  Au survol : les colonnes apparaissent avec un délai échelonné.
  La première colonne part immédiatement.
  Les suivantes démarrent légèrement après — effet cascade.
*/
.nav-item-menu:hover .mega-col {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 0.3s ease-out,
    transform 0.3s ease-out;
}

.nav-item-menu:hover .mega-col:nth-child(1) { transition-delay: 0.05s; }
.nav-item-menu:hover .mega-col:nth-child(2) { transition-delay: 0.1s;  }
.nav-item-menu:hover .mega-col:nth-child(3) { transition-delay: 0.15s; }

/* =====================
   CONTENU DES COLONNES
   ===================== */

.mega-titre {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: #8b949e;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid #30363d;
}

.mega-lien {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 8px;
  border-radius: 6px;
  text-decoration: none;
  margin-bottom: 2px;
  transition: background-color 0.15s ease;
}

.mega-lien:hover {
  background-color: rgba(255, 255, 255, 0.05);
}

.mega-lien-titre {
  font-size: 0.875rem;
  font-weight: bold;
  color: #e6edf3;
}

.mega-lien-desc {
  font-size: 0.75rem;
  color: #8b949e;
  line-height: 1.4;
}

/* =====================
   PAGE HERO (fond de page)
   ===================== */

.page-hero {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 100px 24px 60px;
  background: linear-gradient(160deg, #f0f4f8 60%, #e8f0fe);
}

.page-hero h1 {
  font-size: clamp(1.75rem, 4vw, 3rem);
  color: #1a1a2e;
  margin-bottom: 16px;
  max-width: 600px;
}

.page-hero p {
  font-size: 1rem;
  color: #666;
}

Explications et points de vigilance

opacity + visibility plutôt que display: none : c'est la combinaison fondamentale pour les menus animés. opacity: 0 seul rend invisible mais l'élément continue d'intercepter les clics — les liens restent cliquables même masqués, ce qui est un problème d'accessibilité et d'UX. visibility: hidden empêche les interactions. La différence avec display: none est que visibility peut être animée (enfin, elle est discrète — elle bascule instantanément) pendant que opacity se déroule en douceur.

Courbes de timing différentes à l'entrée et à la sortie : ease-out à l'apparition (le menu part vite et s'installe doucement), ease-in à la disparition (le menu prend de l'élan pour partir). Ce n'est pas qu'une question d'esthétique — c'est une règle de motion design qui imite les objets physiques. Une disparition ease-out serait frustrante car elle freinerait en sortant, donnant l'impression que le menu "hésite" à partir.

Les délais échelonnés uniquement à l'apparition : les colonnes apparaissent avec 0.05s, 0.1s et 0.15s de délai. À la disparition, aucun délai n'est appliqué — le menu s'efface d'un bloc, rapidement. Ajouter des délais échelonnés à la disparition serait pénible : l'utilisateur devrait attendre que chaque colonne finisse de disparaître avant que le menu soit totalement fermé.

position: relative sur .nav-item, pas sur .nav : le méga-menu se positionne par rapport à son ancêtre le plus proche en position: relative ou absolute. Si la propriété était sur .nav, le menu s'alignerait sur le groupe entier de liens. Sur .nav-item, il s'aligne précisément sous le lien "Solutions".

Erreur fréquente : définir la même transition dans l'état de repos et au survol. En faisant cela, les transitions d'entrée et de sortie seraient identiques. En écrivant deux blocs transition différents — un dans l'état de repos (sortie) et un au survol (entrée) — on contrôle indépendamment les deux directions.

9.3 Animations CSS

Les transitions animent le passage d'un état A vers un état B, déclenchées par une interaction. Les animations CSS vont plus loin : elles peuvent enchaîner un nombre illimité d'étapes intermédiaires, se jouer automatiquement sans interaction, se répéter, alterner, et être contrôlées finement dans leur déroulement. Là où la transition est réactive, l'animation est autonome.

9.3.1 Principe des animations

Une animation CSS fonctionne en deux temps distincts. D'abord, on définit l'animation avec @keyframes — c'est le scénario, indépendant de tout élément. Ensuite, on applique cette animation à un élément avec la propriété animation. Ces deux étapes sont séparées et peuvent être dans n'importe quel ordre dans le fichier CSS.

/* Étape 1 : définir l'animation — le scénario */
@keyframes fondu-entree {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Étape 2 : appliquer l'animation à un élément */
.titre {
  animation: fondu-entree 0.6s ease-out;
}

Syntaxe complète de la propriété animation.

.element {
  animation: nom durée timing-function délai itérations direction fill-mode état;
}

/* Les valeurs les plus courantes */
.element {
  animation-name:            fondu-entree;
  animation-duration:        0.6s;
  animation-timing-function: ease-out;
  animation-delay:           0.2s;
  animation-iteration-count: 1;         /* Nombre de répétitions, ou infinite */
  animation-direction:       normal;    /* normal, reverse, alternate, alternate-reverse */
  animation-fill-mode:       forwards;  /* none, forwards, backwards, both */
  animation-play-state:      running;   /* running ou paused */
}

/* Raccourci équivalent */
.element {
  animation: fondu-entree 0.6s ease-out 0.2s 1 normal forwards;
}

9.3.2 Définition d'animations avec @keyframes

@keyframes définit les étapes de l'animation. Chaque étape est un pourcentage de la durée totale — 0% est le début, 100% est la fin. from est un alias de 0% et to un alias de 100%.

Animation à deux étapes.

/* Forme simple : from / to */
@keyframes apparition {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Forme équivalente avec pourcentages */
@keyframes apparition {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

Animation à plusieurs étapes.

/* Pulsation : grossit puis revient */
@keyframes pulsation {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* Rebond : tombe, rebondit, s'immobilise */
@keyframes rebond {
  0%   { transform: translateY(0);    animation-timing-function: ease-in; }
  60%  { transform: translateY(40px); animation-timing-function: ease-out; }
  80%  { transform: translateY(10px); animation-timing-function: ease-in; }
  100% { transform: translateY(0); }
}

Plusieurs propriétés animées simultanément.

@keyframes entree-gauche {
  from {
    opacity: 0;
    transform: translateX(-40px);
    filter: blur(4px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
    filter: blur(0);
  }
}

Appliquer un timing différent à chaque étape.

animation-timing-function peut être défini dans chaque étape de @keyframes pour contrôler la courbe de vitesse entre cette étape et la suivante.

@keyframes saut {
  0%   {
    transform: translateY(0);
    animation-timing-function: ease-out; /* Monte en ralentissant */
  }
  45%  {
    transform: translateY(-60px);
    animation-timing-function: ease-in;  /* Redescend en accélérant */
  }
  100% { transform: translateY(0); }
}

Voici un exemple HTML+CSS complet illustrant plusieurs animations à étapes multiples :

<!-- keyframes-etapes.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="keyframes-etapes.css">
  <title>@keyframes — étapes multiples</title>
</head>
<body>

  <div class="scene">

    <div class="demo">
      <div class="boite pulsation">Pulsation</div>
    </div>

    <div class="demo">
      <div class="boite rotation-continue">Rotation</div>
    </div>

    <div class="demo">
      <div class="boite saut">Saut</div>
    </div>

    <div class="demo">
      <div class="boite fondu">Fondu</div>
    </div>

  </div>

</body>
</html>
/* keyframes-etapes.css */

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: Arial, sans-serif;
  background-color: #0d1117;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 40px;
}

.scene {
  display: flex;
  gap: 40px;
  flex-wrap: wrap;
  justify-content: center;
}

.demo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.boite {
  width: 100px;
  height: 100px;
  border-radius: 12px;
  background-color: #2E6DA4;
  color: #ffffff;
  font-size: 0.75rem;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* --- Pulsation : scale 1 → 1.15 → 1 en boucle --- */
@keyframes pulsation {
  0%, 100% { transform: scale(1);    background-color: #2E6DA4; }
  50%       { transform: scale(1.15); background-color: #e8ff47; color: #0d1117; }
}

.pulsation {
  animation: pulsation 1.6s ease-in-out infinite;
}

/* --- Rotation continue --- */
@keyframes rotation {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.rotation-continue {
  border-radius: 50%;
  animation: rotation 2s linear infinite;
}

/* --- Saut avec rebond --- */
@keyframes saut {
  0%   { transform: translateY(0);     animation-timing-function: ease-out; }
  40%  { transform: translateY(-50px); animation-timing-function: ease-in; }
  70%  { transform: translateY(0);     animation-timing-function: ease-out; }
  85%  { transform: translateY(-14px); animation-timing-function: ease-in; }
  100% { transform: translateY(0); }
}

.saut {
  background-color: #C0392B;
  animation: saut 1.2s ease infinite;
  animation-delay: 0.4s;
}

/* --- Fondu entrant en boucle --- */
@keyframes fondu {
  0%, 100% { opacity: 0; transform: scale(0.85); }
  50%       { opacity: 1; transform: scale(1); }
}

.fondu {
  background-color: #1a3a5c;
  border: 1px solid #2e6da4;
  animation: fondu 2s ease-in-out infinite;
  animation-delay: 0.8s;
}

9.3.3 Répétition et contrôle des animations

animation-iteration-count — nombre de répétitions.

.element { animation-iteration-count: 1;        } /* Une seule fois (défaut) */
.element { animation-iteration-count: 3;        } /* Trois fois */
.element { animation-iteration-count: infinite; } /* En boucle permanente */
.element { animation-iteration-count: 1.5;      } /* Une fois et demie */

animation-direction — sens de lecture.

.element { animation-direction: normal;           } /* 0% → 100% à chaque cycle */
.element { animation-direction: reverse;          } /* 100% → 0% à chaque cycle */
.element { animation-direction: alternate;        } /* Alterne : 0%→100%, 100%→0%... */
.element { animation-direction: alternate-reverse;} /* Alterne en commençant à 100% */

alternate est particulièrement utile pour les animations de va-et-vient fluides : l'animation joue en avant, puis en arrière, en boucle, sans saccade au point de raccord.

/* Oscillation douce avec alternate — bien plus fluide qu'un keyframe 0%→50%→100% */
@keyframes oscillation {
  from { transform: translateX(0); }
  to   { transform: translateX(30px); }
}

.pendule {
  animation: oscillation 0.8s ease-in-out infinite alternate;
}

animation-fill-mode — état avant et après.

Par défaut, un élément revient à son état CSS d'origine une fois l'animation terminée. animation-fill-mode contrôle ce comportement.

.element { animation-fill-mode: none;      } /* Retour à l'état original après (défaut) */
.element { animation-fill-mode: forwards;  } /* Reste sur le dernier keyframe après la fin */
.element { animation-fill-mode: backwards; } /* Applique le premier keyframe pendant le délai */
.element { animation-fill-mode: both;      } /* Combine forwards et backwards */
/*
  Cas typique : animation d'entrée.
  Sans forwards, l'élément redeviendrait opacity: 0 après l'animation.
  Avec forwards, il reste visible à l'état du dernier keyframe.
*/
@keyframes entree {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

.element {
  opacity: 0; /* État initial avant l'animation */
  animation: entree 0.5s ease-out forwards;
}

animation-play-state — pause et reprise.

.element {
  animation: rotation 2s linear infinite;
}

.element:hover {
  animation-play-state: paused; /* L'animation se fige au survol */
}

Délais échelonnés sur des éléments en liste.

@keyframes entree-liste {
  from { opacity: 0; transform: translateX(-20px); }
  to   { opacity: 1; transform: translateX(0); }
}

.item {
  opacity: 0;
  animation: entree-liste 0.4s ease-out forwards;
}

.item:nth-child(1) { animation-delay: 0s;    }
.item:nth-child(2) { animation-delay: 0.08s; }
.item:nth-child(3) { animation-delay: 0.16s; }
.item:nth-child(4) { animation-delay: 0.24s; }
.item:nth-child(5) { animation-delay: 0.32s; }

Combiner plusieurs animations.

.element {
  animation:
    fondu-entree 0.5s ease-out forwards,
    pulsation    2s ease-in-out 0.5s infinite;
  /*
    L'élément apparaît d'abord (fondu-entree, une fois),
    puis pulse en boucle (pulsation, après 0.5s de délai).
  */
}

Respect des préférences utilisateur.

@keyframes pulsation {
  0%, 100% { transform: scale(1); }
  50%       { transform: scale(1.1); }
}

.badge {
  animation: pulsation 1.5s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .badge {
    animation: none;
  }
}

Exercice 31 — La vitrine d'un jeu vidéo

Objectif : construire la page de présentation d'un jeu de science-fiction fictif. Le titre du jeu apparaît lettre par lettre à l'entrée. Les cartes de personnages se retournent en 3D au survol pour révéler leur fiche de statistiques. Une barre de chargement s'anime en boucle. Des étoiles scintillent en fond. Tout est réalisé en CSS pur, sans une seule ligne de JavaScript. Cet exercice est le récapitulatif de toute la partie 9 : transformations 2D et 3D, transitions avec courbes de timing différenciées, animations @keyframes à plusieurs étapes, fill-mode, délais échelonnés et respect de prefers-reduced-motion.

Consignes HTML :

  • Créez un fichier jeu.html avec la balise <meta name="viewport">.
  • Créez une <section class="hero"> contenant :
    • Une <div class="etoiles"> avec dix <div class="etoile"> numérotées (etoile-1 à etoile-10).
    • Une <div class="hero-contenu"> avec un <div class="titre-jeu"> contenant cinq <span> — un par lettre du mot NEXUS — chacun avec une classe lettre et une classe numérotée (l-1 à l-5), puis un <p class="sous-titre"> et un <a class="btn-jouer"> (Jouer maintenant).
    • Une <div class="barre-chargement"> avec un <div class="barre-remplissage"> et un <span class="barre-texte"> (CHARGEMENT...).
  • Créez une <section class="personnages" id="personnages"> avec un <h2> et une <div class="grille-perso"> contenant trois <div class="carte-perso">. Chaque .carte-perso contient un <div class="carte-perso-inner"> avec :
    • <div class="perso-avant"> : une <div class="perso-avatar"> (une lettre), un <h3> (nom), un <p class="perso-classe"> (classe).
    • <div class="perso-arriere"> : un <h3>, quatre <div class="stat-ligne"> — chacun avec un <span class="stat-nom"> et une <div class="stat-barre"> contenant un <div class="stat-remplissage"> avec une largeur définie en style inline — et un <a class="btn-choisir"> (Choisir).
  • Liez un fichier jeu.css.

Consignes CSS — récapitulatif de la partie 9 :

Avant d'écrire quoi que ce soit, classez mentalement chaque élément animé : transformation seule ? Transition ? @keyframes ? Les trois combinés ?

  • Transformations (9.1) :

    • Les .etoile ont des tailles différentes grâce à scale() ou des width/height distinctes. Certaines sont légèrement inclinées avec rotate().
    • Le .btn-jouer se soulève au survol avec translateY et s'agrandit avec scale — combinez les deux.
    • Les cartes .carte-perso utilisent le retournement 3D : perspective sur le conteneur, rotateY(180deg) sur l'inner au survol, backface-visibility: hidden sur chaque face, transform: rotateY(180deg) sur la face arrière pour la pré-retourner.
  • Transitions (9.2) :

    • Le retournement des cartes utilise ease-in-out — un mouvement de A vers B, symétrique.
    • Le .btn-jouer utilise ease-out à l'apparition et ease-in à la disparition. Écrivez deux blocs transition distincts : un dans le CSS de base (sortie), un dans :hover (entrée).
    • Les .stat-remplissage partent de width: 0% et se remplissent vers leur valeur style inline au survol de la carte via une transition ease-out.
  • Animations @keyframes (9.3) :

    • Chaque lettre du titre joue lettre-entree (opacity: 0 + translateY(-30px) → état final) avec un délai échelonné de 0s à 0.4s. Utilisez fill-mode: forwards.
    • Le .sous-titre apparaît avec fondu-entree après les lettres — calculez le délai pour qu'il démarre après la dernière lettre.
    • Le .btn-jouer apparaît aussi avec fondu-entree, puis enchaîne une pulsation douce en boucle. Combinez les deux animations avec des délais calculés.
    • Les .etoile scintillent avec une animation scintillement (opacity + léger scale) en infinite alternate. Donnez à chaque étoile une durée et un délai distincts.
    • La .barre-remplissage s'anime de width: 0% à width: 100% en boucle infinite alternate.
    • Désactivez toutes les animations automatiques avec prefers-reduced-motion: reduce. Rendez immédiatement visibles les éléments qui démarraient avec opacity: 0.

Correction détaillée — Exercice 31

Correction HTML

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="jeu.css">
  <title>NEXUS — Le jeu</title>
</head>
<body>

  <section class="hero">

    <div class="etoiles">
      <div class="etoile etoile-1"></div>
      <div class="etoile etoile-2"></div>
      <div class="etoile etoile-3"></div>
      <div class="etoile etoile-4"></div>
      <div class="etoile etoile-5"></div>
      <div class="etoile etoile-6"></div>
      <div class="etoile etoile-7"></div>
      <div class="etoile etoile-8"></div>
      <div class="etoile etoile-9"></div>
      <div class="etoile etoile-10"></div>
    </div>

    <div class="hero-contenu">

      <div class="titre-jeu">
        <span class="lettre l-1">N</span>
        <span class="lettre l-2">E</span>
        <span class="lettre l-3">X</span>
        <span class="lettre l-4">U</span>
        <span class="lettre l-5">S</span>
      </div>

      <p class="sous-titre">La guerre des mondes a commencé. Choisissez votre champion.</p>
      <a href="#personnages" class="btn-jouer">Jouer maintenant</a>

    </div>

    <div class="barre-chargement">
      <div class="barre-remplissage"></div>
      <span class="barre-texte">CHARGEMENT...</span>
    </div>

  </section>

  <section class="personnages" id="personnages">
    <h2>Choisissez votre personnage</h2>
    <div class="grille-perso">

      <div class="carte-perso">
        <div class="carte-perso-inner">

          <div class="perso-avant av-1">
            <div class="perso-avatar">K</div>
            <h3>Kael</h3>
            <p class="perso-classe">⚔️ Guerrier</p>
          </div>

          <div class="perso-arriere ar-1">
            <h3>Kael</h3>
            <div class="stat-ligne">
              <span class="stat-nom">Force</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 90%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Agilité</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 55%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Magie</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 20%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Endurance</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 80%"></div></div>
            </div>
            <a href="#" class="btn-choisir">Choisir</a>
          </div>

        </div>
      </div>

      <div class="carte-perso">
        <div class="carte-perso-inner">

          <div class="perso-avant av-2">
            <div class="perso-avatar">L</div>
            <h3>Lyra</h3>
            <p class="perso-classe">✨ Mage</p>
          </div>

          <div class="perso-arriere ar-2">
            <h3>Lyra</h3>
            <div class="stat-ligne">
              <span class="stat-nom">Force</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 25%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Agilité</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 65%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Magie</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 95%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Endurance</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 45%"></div></div>
            </div>
            <a href="#" class="btn-choisir">Choisir</a>
          </div>

        </div>
      </div>

      <div class="carte-perso">
        <div class="carte-perso-inner">

          <div class="perso-avant av-3">
            <div class="perso-avatar">Z</div>
            <h3>Zyx</h3>
            <p class="perso-classe">🗡️ Assassin</p>
          </div>

          <div class="perso-arriere ar-3">
            <h3>Zyx</h3>
            <div class="stat-ligne">
              <span class="stat-nom">Force</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 60%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Agilité</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 95%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Magie</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 40%"></div></div>
            </div>
            <div class="stat-ligne">
              <span class="stat-nom">Endurance</span>
              <div class="stat-barre"><div class="stat-remplissage" style="--w: 50%"></div></div>
            </div>
            <a href="#" class="btn-choisir">Choisir</a>
          </div>

        </div>
      </div>

    </div>
  </section>

</body>
</html>

Correction CSS

/* jeu.css */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #03050d;
  color: #e6edf3;
  overflow-x: hidden;
}

/* ============================================================
   ANIMATIONS @KEYFRAMES
   ============================================================ */

/* Apparition d'une lettre depuis le haut avec léger rebond */
@keyframes lettre-entree {
  from {
    opacity: 0;
    transform: translateY(-30px) scale(0.8);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Fondu simple — sous-titre et bouton */
@keyframes fondu-entree {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Scintillement des étoiles */
@keyframes scintillement {
  from { opacity: 0.15; transform: scale(1); }
  to   { opacity: 1;    transform: scale(1.4); }
}

/* Remplissage de la barre de chargement */
@keyframes chargement {
  from { width: 0%; }
  to   { width: 100%; }
}

/* Pulsation douce du bouton après son apparition */
@keyframes pulse-bouton {
  0%, 100% { box-shadow: 0 0 0 0   rgba(232, 255, 71, 0.5); }
  50%       { box-shadow: 0 0 0 14px rgba(232, 255, 71, 0); }
}

/* ============================================================
   HERO
   ============================================================ */

.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 40px 24px 100px;
  background: radial-gradient(ellipse at center, #0d1f3c 0%, #03050d 70%);
  overflow: hidden;
}

/* ============================================================
   ÉTOILES — transformations 2D (9.1) + animations (9.3)
   ============================================================ */

.etoile {
  position: absolute;
  background-color: #ffffff;
  border-radius: 50%;
  /*
    Scintillement en boucle avec alternate (9.3) :
    l'étoile passe de presque invisible à pleinement visible
    puis revient en arrière, sans saccade au raccord.
  */
  animation: scintillement ease-in-out infinite alternate;
}

/* Positions, tailles, durées et délais uniques pour chaque étoile */
.etoile-1  { width: 3px; height: 3px; top: 12%; left: 8%;
             animation-duration: 2.1s; animation-delay: 0s; }
.etoile-2  { width: 2px; height: 2px; top: 25%; left: 20%;
             animation-duration: 1.7s; animation-delay: 0.4s; }
.etoile-3  { width: 4px; height: 4px; top: 8%;  left: 40%;
             animation-duration: 2.8s; animation-delay: 0.9s;
             transform: rotate(45deg); /* Transformation 2D (9.1) */ }
.etoile-4  { width: 2px; height: 2px; top: 35%; left: 65%;
             animation-duration: 1.5s; animation-delay: 0.2s; }
.etoile-5  { width: 5px; height: 5px; top: 15%; left: 80%;
             animation-duration: 2.4s; animation-delay: 0.7s; }
.etoile-6  { width: 2px; height: 2px; top: 55%; left: 10%;
             animation-duration: 1.9s; animation-delay: 1.1s; }
.etoile-7  { width: 3px; height: 3px; top: 70%; left: 30%;
             animation-duration: 2.2s; animation-delay: 0.5s; }
.etoile-8  { width: 4px; height: 4px; top: 60%; left: 75%;
             animation-duration: 1.6s; animation-delay: 0.3s;
             transform: rotate(30deg); }
.etoile-9  { width: 2px; height: 2px; top: 80%; left: 55%;
             animation-duration: 2.6s; animation-delay: 0.8s; }
.etoile-10 { width: 3px; height: 3px; top: 45%; left: 90%;
             animation-duration: 1.8s; animation-delay: 1.3s; }

/* ============================================================
   TITRE LETTRE PAR LETTRE — animations (9.3)
   ============================================================ */

.titre-jeu {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-bottom: 28px;
}

.lettre {
  display: inline-block;
  font-size: clamp(4rem, 12vw, 8rem);
  font-weight: bold;
  color: #e8ff47;
  text-shadow: 0 0 40px rgba(232, 255, 71, 0.5);

  opacity: 0; /* Invisible avant l'animation */

  /*
    cubic-bezier avec y2 > 1 : léger rebond à l'arrivée.
    C'est le cas d'usage typique de la courbe personnalisée (9.2).
    forwards : la lettre reste visible après l'animation.
  */
  animation: lettre-entree 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

/* Délais échelonnés — les lettres arrivent une par une (9.3) */
.l-1 { animation-delay: 0s;   }
.l-2 { animation-delay: 0.1s; }
.l-3 { animation-delay: 0.2s; }
.l-4 { animation-delay: 0.3s; }
.l-5 { animation-delay: 0.4s; }

/* ============================================================
   SOUS-TITRE
   ============================================================ */

.sous-titre {
  font-size: clamp(1rem, 2vw, 1.25rem);
  color: rgba(255, 255, 255, 0.65);
  max-width: 480px;
  line-height: 1.7;
  margin-bottom: 36px;

  opacity: 0;
  /*
    Démarre après la dernière lettre.
    l-5 : délai 0.4s + durée 0.5s = 0.9s.
    On ajoute 0.1s de respiration → délai de 1s.
  */
  animation: fondu-entree 0.6s ease-out 1s forwards;
}

/* ============================================================
   BOUTON JOUER — transitions (9.2) + animations (9.3)
   ============================================================ */

.btn-jouer {
  display: inline-block;
  background-color: #e8ff47;
  color: #03050d;
  padding: 16px 40px;
  text-decoration: none;
  font-size: 1.0625rem;
  font-weight: bold;
  letter-spacing: 1px;
  text-transform: uppercase;
  border-radius: 4px;

  opacity: 0;
  /*
    Deux animations combinées (9.3) :
    1. fondu-entree : apparaît après le sous-titre (délai 1.4s).
    2. pulse-bouton : pulsation en boucle après l'apparition (délai 2s).
       Calcul : 1.4s (délai) + 0.6s (durée) = 2s exactement.
  */
  animation:
    fondu-entree 0.6s ease-out   1.4s forwards,
    pulse-bouton 2s   ease-in-out 2s   infinite;

  /*
    Transition de SORTIE — ease-in (part avec de l'élan) (9.2).
    Le bloc :hover redéfinit avec ease-out pour l'entrée.
  */
  transition: transform 0.2s ease-in, background-color 0.2s ease;
}

.btn-jouer:hover {
  background-color: #d4eb30;
  /* Transformation 2D : soulèvement + mise à l'échelle (9.1) */
  transform: translateY(-4px) scale(1.04);
  /* Transition d'ENTRÉE — ease-out (arrive vite, se pose) (9.2) */
  transition: transform 0.2s ease-out, background-color 0.15s ease;
}

/* ============================================================
   BARRE DE CHARGEMENT — animations (9.3)
   ============================================================ */

.barre-chargement {
  position: absolute;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  width: min(400px, 80vw);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}

.barre-remplissage {
  height: 3px;
  width: 0%;
  background: linear-gradient(to right, #2E6DA4, #e8ff47);
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(232, 255, 71, 0.4);
  /*
    Se remplit puis se vide en boucle grâce à alternate (9.3).
    ease-in-out : symétrique, naturel pour un va-et-vient.
  */
  animation: chargement 2s ease-in-out infinite alternate;
}

.barre-texte {
  font-size: 0.6875rem;
  letter-spacing: 3px;
  color: rgba(255, 255, 255, 0.3);
  text-align: center;
}

/* ============================================================
   SECTION PERSONNAGES
   ============================================================ */

.personnages {
  padding: 80px 24px;
  max-width: 1100px;
  margin: 0 auto;
}

.personnages h2 {
  text-align: center;
  font-size: clamp(1.25rem, 3vw, 1.875rem);
  color: rgba(255, 255, 255, 0.5);
  text-transform: uppercase;
  letter-spacing: 3px;
  margin-bottom: 48px;
}

.grille-perso {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  justify-content: center;
}

/* ============================================================
   CARTES PERSONNAGES — transformations 3D (9.1) + transitions (9.2)
   ============================================================ */

/*
  Scène 3D : perspective sur le conteneur, pas sur l'inner.
  Hauteur fixe nécessaire pour que les deux faces s'alignent.
*/
.carte-perso {
  flex: 1 1 280px;
  max-width: 320px;
  height: 360px;
  perspective: 1000px; /* Plus petit = effet plus prononcé */
  cursor: pointer;
}

/*
  L'inner se retourne. preserve-3d : les faces vivent
  dans l'espace 3D et ne sont pas aplaties.
*/
.carte-perso-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  /*
    ease-in-out : mouvement symétrique de A vers B.
    Ni entrée ni sortie — juste un déplacement (9.2).
  */
  transition: transform 0.7s ease-in-out;
}

.carte-perso:hover .carte-perso-inner {
  transform: rotateY(180deg);
}

/* Les deux faces superposées en position absolue */
.perso-avant,
.perso-arriere {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  /*
    backface-visibility: hidden : cache la face quand
    elle est dos à l'observateur (9.1 — transformations 3D).
  */
  backface-visibility: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 28px;
  gap: 12px;
}

/* --- FACE AVANT --- */

.perso-avant {
  background-color: #0d1f3c;
  border: 1px solid #1e3a5c;
}

.av-1 { border-color: #ff4d6d; }
.av-2 { border-color: #a259ff; }
.av-3 { border-color: #4dffd4; }

.perso-avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  font-size: 2rem;
  font-weight: bold;
  line-height: 80px;
  text-align: center;
  color: #03050d;
  margin-bottom: 8px;
  /* Légère rotation décorative — transformation 2D (9.1) */
  transform: rotate(-5deg);
}

.av-1 .perso-avatar { background-color: #ff4d6d; }
.av-2 .perso-avatar { background-color: #a259ff; transform: rotate(5deg); }
.av-3 .perso-avatar { background-color: #4dffd4; transform: rotate(-8deg); }

.perso-avant h3 {
  font-size: 1.5rem;
  letter-spacing: 2px;
  text-transform: uppercase;
}

.perso-classe {
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.5);
}

/* --- FACE ARRIÈRE --- */

.perso-arriere {
  background-color: #111827;
  border: 1px solid #30363d;
  /*
    Pré-retournée de 180° (9.1).
    Quand l'inner fait son demi-tour, elle revient à 0° et devient lisible.
  */
  transform: rotateY(180deg);
  justify-content: flex-start;
  gap: 0;
}

.ar-1 { border-color: #ff4d6d; }
.ar-2 { border-color: #a259ff; }
.ar-3 { border-color: #4dffd4; }

.perso-arriere h3 {
  font-size: 1.125rem;
  text-transform: uppercase;
  letter-spacing: 2px;
  margin-bottom: 20px;
  align-self: flex-start;
}

/* --- BARRES DE STATISTIQUES --- */

.stat-ligne {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.stat-nom {
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.5);
  width: 70px;
  flex-shrink: 0;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.stat-barre {
  flex: 1;
  height: 6px;
  background-color: rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  overflow: hidden;
}

.stat-remplissage {
  height: 100%;
  background: linear-gradient(to right, #2E6DA4, #4dffd4);
  border-radius: 3px;
  width: 0%; /* Démarre à 0 */
  /*
    Transition (9.2) : la barre se remplit à l'apparition de la face arrière.
    ease-out : rapide au départ, se pose doucement.
    Le délai de 0.35s laisse le retournement se terminer avant que
    les barres commencent à se remplir.
  */
  transition: width 0.6s ease-out 0.35s;
}

/*
  Au survol de la carte, les barres se remplissent vers la valeur
  définie par la variable CSS --w en style inline dans le HTML.
*/
.carte-perso:hover .stat-remplissage {
  width: var(--w, 50%);
}

.btn-choisir {
  display: block;
  margin-top: 16px;
  padding: 10px 28px;
  background-color: transparent;
  border: 1px solid #e8ff47;
  color: #e8ff47;
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: bold;
  letter-spacing: 1px;
  text-transform: uppercase;
  border-radius: 4px;
  text-align: center;
  width: 100%;
  /* Transition courte sur background et transform (9.2) */
  transition: background-color 0.2s ease, transform 0.2s ease-out;
}

.btn-choisir:hover {
  background-color: #e8ff47;
  color: #03050d;
  transform: scale(1.02);
}

/* ============================================================
   ACCESSIBILITÉ — prefers-reduced-motion (9.3)
   ============================================================ */

@media (prefers-reduced-motion: reduce) {

  /* Rendre visibles immédiatement les éléments masqués par opacity: 0 */
  .lettre,
  .sous-titre,
  .btn-jouer {
    animation: none;
    opacity: 1;
    transform: none;
  }

  /* Supprimer les animations automatiques de fond */
  .etoile {
    animation: none;
    opacity: 0.5;
  }

  /* Barre statique plutôt qu'animée */
  .barre-remplissage {
    animation: none;
    width: 65%;
  }

  /* Conserver le retournement des cartes — il est déclenché
     par l'utilisateur, pas automatique */
}

Explications et points de vigilance

Les lettres et cubic-bezier(0.34, 1.56, 0.64, 1) : la valeur 1.56 dépasse 1, ce qui crée un léger rebond : la lettre dépasse légèrement sa position finale puis revient. C'est exactement le cas d'usage de la courbe personnalisée évoqué en 9.2.3. ease-out aurait simplement freiné — cette courbe ajoute du caractère.

Les barres de stats et les variables CSS --w : les largeurs cibles sont définies via des propriétés personnalisées (--w: 90%) dans le style inline du HTML, puis lues en CSS avec var(--w). Au survol, la transition anime width de 0% vers var(--w). C'est une technique CSS pur qui évite tout JavaScript pour des données variables par élément.

Le délai 0.35s sur les transitions de barres : sans ce délai, les barres commenceraient à se remplir pendant que la carte est encore en train de se retourner — l'animation serait visible sur la face avant pendant le flip. En attendant 0.35s (soit environ la moitié de la durée du retournement de 0.7s), les barres ne démarrent que lorsque la face arrière est en bonne position.

Deux animations combinées sur .btn-jouer : fondu-entree (une fois, forwards) puis pulse-bouton (infinie). Le délai de pulse-bouton est 2s = 1.4s (délai de fondu-entree) + 0.6s (sa durée). La séquence entière est orchestrée sans JavaScript, uniquement avec des délais calculés.

prefers-reduced-motion et le retournement des cartes : on désactive les animations automatiques (étoiles, barre, entrées des lettres) mais on conserve la transition de retournement des cartes. Pourquoi ? Parce que cette interaction est déclenchée volontairement par l'utilisateur — ce n'est pas un mouvement subi. La recommandation WCAG est de supprimer les animations autonomes, pas les animations réactives à une action.

LES FONDAMENTAUX DU CSS (PARTIE 1)

By nicolas_lamy

LES FONDAMENTAUX DU CSS (PARTIE 1)

  • 14