Chapitre 925 minDébutant
Flexbox et CSS Grid : la mise en page
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- choisir entre Flexbox et Grid pour une mise en page ;
- aligner et répartir des éléments avec Flexbox ;
- construire une grille avec
fr,grid-template-areasetsubgrid; - créer une grille de cartes responsive sans media query.
5 exercices corrigés2 schémas
Flexbox et Grid sont les deux systèmes de mise en page du CSS moderne : Flexbox aligne et répartit des éléments sur une seule dimension, une ligne ou une colonne ; Grid les place sur deux dimensions, en lignes et en colonnes à la fois. Ce chapitre explique les propriétés de chacun avec des exemples tirés du site de la médiathèque (menu, cartes, mise en page complète), et comment choisir entre les deux.
Flexbox ou Grid : lequel choisir ?
| Flexbox | Grid | |
|---|---|---|
| Dimensions | une : ligne ou colonne | deux : lignes et colonnes |
| Logique | partir du contenu : les éléments prennent la place dont ils ont besoin, puis on répartit le reste | partir de la mise en page : on dessine la grille, puis on y range les éléments |
| Typiquement | une barre de navigation, une rangée de boutons, le contenu d'une carte (image, texte, bouton poussé en bas), un centrage | la structure de la page (en-tête, menu, contenu, pied), une galerie, une grille de cartes de même largeur, un formulaire en deux colonnes |
Les deux ne s'opposent pas : on les combine. Grid pour la structure de la page et la grille de cartes ; Flexbox à l'intérieur de chaque carte et dans le menu. Une règle simple : si tu as besoin que des éléments s'alignent aussi d'une rangée à l'autre, c'est Grid ; s'ils doivent juste se suivre sur une ligne, c'est Flexbox.
Flexbox : le conteneur et ses éléments
On active Flexbox sur le conteneur ; ses enfants directs deviennent des éléments flexibles (flex items).
<ul class="menu">
<li><a href="index.html">Accueil</a></li>
<li><a href="agenda.html">Agenda</a></li>
<li><a href="catalogue.html">Catalogue</a></li>
</ul>
.menu {
display: flex;
gap: 1rem;
list-style: none;
padding: 0;
}
Les trois éléments se placent côte à côte, espacés de 1rem. Flexbox raisonne avec deux axes :
- l'axe principal (main axis), celui dans lequel les éléments se suivent : horizontal par défaut ;
- l'axe secondaire (cross axis), perpendiculaire.
Toutes les propriétés d'alignement de Flexbox se rapportent à ces axes : justify-content agit le long de l'axe principal, align-items sur l'axe secondaire. Change la direction, et les deux propriétés changent de sens à l'écran.
flex-direction et flex-wrap
| Propriété | Valeurs | Effet |
|---|---|---|
flex-direction | row (par défaut), column, row-reverse, column-reverse | sens de l'axe principal |
flex-wrap | nowrap (par défaut), wrap | wrap autorise les éléments à passer à la ligne quand la place manque |
Par défaut (nowrap), Flexbox comprime les éléments pour tout faire tenir sur une ligne, quitte à les écraser. Avec flex-wrap: wrap, ils passent à la ligne suivante : c'est ce qu'il faut pour une rangée d'étiquettes ou de boutons de filtre.
justify-content et align-items
justify-content répartit les éléments le long de l'axe principal :
| Valeur | Effet (en row) |
|---|---|
flex-start (par défaut) | serrés à gauche |
flex-end | serrés à droite |
center | groupés au centre |
space-between | le premier à gauche, le dernier à droite, l'espace réparti entre eux |
space-around | un espace égal de part et d'autre de chaque élément (donc deux fois moins aux bords) |
space-evenly | des espaces tous égaux, bords compris |
align-items les aligne sur l'axe secondaire :
| Valeur | Effet (en row) |
|---|---|
stretch (par défaut) | les éléments s'étirent sur toute la hauteur du conteneur : toutes les cartes d'une rangée ont la même hauteur |
flex-start, flex-end | en haut, en bas |
center | centrés verticalement |
baseline | alignés sur la ligne de base de leur texte |
Un élément peut déroger à align-items avec align-self. Et une margin: auto dans un conteneur flex absorbe tout l'espace libre de ce côté : margin-left: auto pousse un élément, et ceux qui le suivent, tout à droite.
gap : espacer les éléments
gap fixe l'espace entre les éléments, jamais avant le premier ni après le dernier. C'est plus simple et plus juste que des marges, qui laissent toujours une marge en trop au bout de la rangée. Avec deux valeurs, gap: 1rem 2rem règle l'espace entre lignes, puis entre colonnes. gap fonctionne en Flexbox comme en Grid.
flex-grow, flex-shrink et flex-basis
Trois propriétés, posées sur les éléments, règlent leur taille :
| Propriété | Question | Défaut |
|---|---|---|
flex-basis | quelle taille de départ, avant répartition ? | auto (la taille du contenu, ou width) |
flex-grow | quelle part de l'espace libre l'élément prend-il ? | 0 : il ne grandit pas |
flex-shrink | quelle part du manque de place absorbe-t-il ? | 1 : il rétrécit |
On les écrit presque toujours avec le raccourci flex :
| Écriture | Équivaut à | Usage |
|---|---|---|
flex: 1 | 1 1 0% | les éléments se partagent toute la place, à parts égales |
flex: 2 | 2 1 0% | cet élément prend deux parts quand les autres en prennent une |
flex: auto | 1 1 auto | grandit et rétrécit à partir de la taille de son contenu |
flex: none | 0 0 auto | taille fixe, ne bouge pas (un logo, une icône) |
flex: 0 0 15rem | exactement 15rem de large, ni plus, ni moins |
/* Un champ de recherche qui prend toute la place à côté de son bouton */
.recherche {
display: flex;
gap: 0.5rem;
}
.recherche input {
flex: 1;
min-width: 0; /* voir l'encadré */
}
.recherche button {
flex: none;
}
Calcul : un conteneur de 600 px contient deux éléments de flex-basis 100 px et 200 px, avec un gap de 20 px. L'espace libre vaut 600 − 100 − 200 − 20 = 280 px. Avec flex-grow: 1 et flex-grow: 3, le premier reçoit 280 × 1/4 = 70 px (il fait 170 px), le second 280 × 3/4 = 210 px (il fait 410 px).
Exemple : une barre de navigation en Flexbox
<header class="entete">
<a href="index.html" class="logo"><img src="images/logo.svg" alt="Médiathèque des Tilleuls – accueil" width="180" height="48"></a>
<nav aria-label="Menu principal">
<ul class="menu">
<li><a href="agenda.html">Agenda</a></li>
<li><a href="catalogue.html">Catalogue</a></li>
<li><a href="infos.html">Infos pratiques</a></li>
</ul>
</nav>
<a href="compte.html" class="bouton">Mon compte</a>
</header>
.entete {
display: flex;
flex-wrap: wrap;
align-items: center; /* logo, menu et bouton centrés verticalement */
gap: 1rem 2rem;
padding: 0.75rem 1rem;
}
.entete nav {
margin-left: auto; /* pousse le menu et le bouton à droite */
}
.menu {
display: flex;
flex-wrap: wrap;
gap: 0.25rem 1rem;
list-style: none;
margin: 0;
padding: 0;
}
.menu a {
display: inline-block;
padding: 0.5rem 0.25rem;
}
Deux conteneurs flex imbriqués : l'en-tête place ses trois enfants (logo, nav, bouton), et la liste du menu place ses <li>. Grâce à flex-wrap: wrap, sur un écran étroit, le menu passe sous le logo au lieu de déborder.
Exemple : une carte dont le bouton reste en bas
Dans une rangée de cartes, les textes ont des longueurs différentes : on veut que les boutons « S'inscrire » s'alignent quand même en bas.
.carte {
display: flex;
flex-direction: column; /* image, titre, texte et bouton empilés */
gap: 0.5rem;
}
.carte .bouton {
margin-top: auto; /* absorbe l'espace libre au-dessus : le bouton descend en bas */
align-self: flex-start; /* le bouton garde sa largeur au lieu de s'étirer */
}
Les cartes ont la même hauteur grâce au stretch par défaut de leur conteneur (Flexbox ou Grid) ; margin-top: auto fait le reste.
CSS Grid : lignes, colonnes et unité fr
On active Grid sur le conteneur, puis on décrit ses pistes (tracks), colonnes et lignes :
.galerie {
display: grid;
grid-template-columns: 200px 1fr 2fr;
gap: 1rem;
}
L'unité fr (fraction) partage l'espace restant. Dans un conteneur de 800 px, avec un gap de 1rem (16 px) entre les trois colonnes : l'espace restant vaut 800 − 200 − 2 × 16 = 568 px, réparti en trois fractions de 189,33 px ; la deuxième colonne en prend une, la troisième deux (378,67 px).
| Écriture | Résultat |
|---|---|
repeat(3, 1fr) | trois colonnes égales (1fr 1fr 1fr) |
repeat(4, 10rem) | quatre colonnes de 10rem |
minmax(15rem, 1fr) | une colonne d'au moins 15rem, qui grandit sinon |
16rem 1fr | une barre latérale fixe et un contenu qui prend le reste |
grid-auto-rows: minmax(8rem, auto) | les lignes créées automatiquement font au moins 8rem |
Il n'est pas nécessaire de décrire les lignes : Grid en crée autant qu'il faut pour placer tous les éléments (les lignes implicites), chacune à la hauteur de son contenu le plus grand.
Placer les éléments dans la grille
Par défaut, les éléments remplissent les cellules dans l'ordre, ligne par ligne. Pour en placer un précisément, on utilise les numéros de lignes de grille : une grille de 3 colonnes a 4 lignes verticales, numérotées de 1 à 4 (et de −1 à −4 en partant de la fin).
.mise-en-avant {
grid-column: 1 / 3; /* de la ligne 1 à la ligne 3 : deux colonnes */
}
.bandeau {
grid-column: 1 / -1; /* de la première à la dernière ligne : toute la largeur */
}
.grande-carte {
grid-column: span 2; /* occupe deux colonnes, là où elle tombe */
grid-row: span 2; /* et deux lignes */
}
grid-template-areas : dessiner la mise en page
Pour la structure d'une page, la façon la plus lisible est de nommer des zones et de dessiner la grille en texte :
body {
display: grid;
grid-template-columns: 16rem 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"entete entete"
"lateral contenu"
"pied pied";
min-height: 100dvh;
gap: 1rem;
}
body > header { grid-area: entete; }
body > aside { grid-area: lateral; }
body > main { grid-area: contenu; }
body > footer { grid-area: pied; }
Chaque chaîne est une ligne de la grille, chaque mot une cellule. Un nom répété sur plusieurs cellules adjacentes crée une zone qui les couvre (elle doit rester rectangulaire) ; un point . laisse une cellule vide. Avec grid-template-rows: auto 1fr auto et min-height: 100dvh, le pied de page reste en bas de la fenêtre même quand le contenu est court.
L'intérêt devient évident sur mobile : il suffit de redessiner la grille dans une media query (chapitre 10), sans toucher au HTML.
@media (width < 48rem) {
body {
grid-template-columns: 1fr;
grid-template-areas:
"entete"
"contenu"
"lateral"
"pied";
}
}
Une grille de cartes responsive sans media query
.grille-ateliers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
gap: 1.5rem;
}
Cette ligne, à connaître par cœur, dit : « mets autant de colonnes d'au moins 16rem que la largeur le permet, et partage l'espace restant entre elles ». Sur un téléphone, une colonne ; sur une tablette, deux ; sur un grand écran, quatre ou cinq. Aucune media query n'est nécessaire.
auto-fill | auto-fit | |
|---|---|---|
| Quand il y a peu d'éléments | garde des colonnes vides : les cartes gardent leur largeur | supprime les colonnes vides : les cartes s'étirent pour remplir la ligne |
subgrid : aligner l'intérieur des cartes
Dans une grille de cartes, chaque carte est une grille indépendante : les titres de longueurs différentes décalent les textes d'une carte à l'autre. Avec subgrid, reconnu par tous les navigateurs actuels depuis 2023, une carte réutilise les lignes de la grille parente :
.grille-ateliers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
gap: 1.5rem;
}
.grille-ateliers .carte {
display: grid;
grid-row: span 3; /* titre, description, bouton */
grid-template-rows: subgrid; /* les mêmes lignes que les cartes voisines */
}
Les titres, les descriptions et les boutons de toutes les cartes d'une même rangée s'alignent alors entre eux.
Exercices
Exercice 1 sur 5 Flexbox ou Grid ? Réussi À revoir
Choisis Flexbox ou Grid pour chaque mise en page, et justifie en une phrase.
- Le menu principal : cinq liens côte à côte.
- La page : en-tête, colonne latérale, contenu, pied de page.
- Une galerie de 24 photos de même taille, en rangées bien alignées.
- Une rangée d'étiquettes de longueurs variées (« Jeunesse », « Numérique », « Gratuit »…), qui passent à la ligne.
- Un formulaire où toutes les étiquettes sont dans une colonne et tous les champs dans une autre, alignés.
Voir la réponseMasquer la réponse de l'exercice 1
- Flexbox : une seule dimension, des éléments de largeurs différentes qui se suivent.
- Grid : deux dimensions, une structure dessinée à l'avance (
grid-template-areas). - Grid : les photos doivent s'aligner en lignes et en colonnes.
- Flexbox avec
flex-wrap: wrap: chaque étiquette garde sa largeur naturelle, il n'y a pas de colonnes à aligner. - Grid, avec
grid-template-columns: auto 1fr: les étiquettes s'alignent d'une ligne à l'autre, ce que Flexbox ne sait pas faire (chaque ligne flex est indépendante).
Tu avais trouvé ?
Exercice 2 sur 5 Calculer avec flex-grow Réussi À revoir
Un conteneur flex de 900 px contient trois éléments, sans gap ni marge. Leurs flex-basis sont 200 px, 100 px et 100 px. Calcule la largeur finale de chaque élément :
- si leurs
flex-growvalent 1, 1 et 1 ; - s'ils valent 0, 1 et 3 ;
- si tous ont
flex: 1(doncflex-basis: 0%).
Voir la réponseMasquer la réponse de l'exercice 2
L'espace libre vaut 900 − (200 + 100 + 100) = 500 px.
- Chacun reçoit 500 / 3 ≈ 166,67 px : 366,67 px, 266,67 px et 266,67 px. Ils ne sont pas égaux, parce qu'ils partent de tailles différentes.
- 4 parts au total. Le premier ne grandit pas : 200 px. Le deuxième reçoit 500 × 1/4 = 125 px : 225 px. Le troisième reçoit 500 × 3/4 = 375 px : 475 px. Total : 200 + 225 + 475 = 900.
- Avec
flex-basis: 0%, tout l'espace (900 px) est partagé à parts égales : 300 px chacun. C'est pour ça queflex: 1donne des colonnes égales, quel que soit leur contenu (tant qu'aucun contenu n'est plus large que 300 px).
Tu avais trouvé ?
Exercice 3 sur 5 Lire une grille Réussi À revoir
.grille {
display: grid;
grid-template-columns: 10rem 1fr 1fr;
gap: 1rem;
}
.a { grid-column: 2 / 4; }
.b { grid-column: 1 / -1; }
- Combien la grille a-t-elle de lignes verticales ? Quels sont leurs numéros positifs ?
- Quelles colonnes l'élément
.aoccupe-t-il ? - Et l'élément
.b? - Le conteneur fait 1 000 px de large et 1rem vaut 16 px. Quelle est la largeur de chaque colonne ?
Voir la réponseMasquer la réponse de l'exercice 3
- Trois colonnes, donc quatre lignes verticales, numérotées 1, 2, 3 et 4.
- De la ligne 2 à la ligne 4 : les deuxième et troisième colonnes.
- De la ligne 1 à la dernière (−1, c'est-à-dire 4) : toute la largeur.
- Colonne 1 : 10rem = 160 px. Espace restant : 1 000 − 160 − 2 × 16 (deux
gap) = 808 px, partagé en deux : 404 px pour chacune des deux autres colonnes.
Tu avais trouvé ?
Exercice 4 sur 5 La mise en page de la médiathèque Réussi À revoir
Écris le CSS Grid de la page d'agenda : sur grand écran, un en-tête sur toute la largeur, un contenu principal à gauche et une colonne « À ne pas manquer » de 18rem à droite, un pied de page sur toute la largeur. Sous 48rem de large, tout passe sur une seule colonne, dans l'ordre en-tête, contenu, colonne, pied. Le HTML contient header, main, aside et footer, enfants directs de body.
Voir la réponseMasquer la réponse de l'exercice 4
body {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"entete"
"contenu"
"lateral"
"pied";
gap: 1.5rem;
min-height: 100dvh;
grid-template-rows: auto 1fr auto auto;
}
@media (width >= 48rem) {
body {
grid-template-columns: 1fr 18rem;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"entete entete"
"contenu lateral"
"pied pied";
}
}
body > header { grid-area: entete; }
body > main { grid-area: contenu; }
body > aside { grid-area: lateral; }
body > footer { grid-area: pied; }
On écrit d'abord la version mobile, puis on l'enrichit pour les grands écrans : c'est l'approche mobile first du chapitre 10. L'ordre du HTML (header, main, aside, footer) est aussi l'ordre de lecture sur mobile : aucune incohérence pour le clavier ou les lecteurs d'écran.
Tu avais trouvé ?
Exercice 5 sur 5 Déboguer une rangée qui déborde Réussi À revoir
Une rangée de trois cartes en display: flex avec flex: 1 sur chaque carte déborde de l'écran sur mobile : la deuxième carte contient l'adresse https://www.mediatheque-tilleuls.fr/agenda/ateliers-numeriques-initiation. Explique la cause et donne deux corrections.
Voir la réponseMasquer la réponse de l'exercice 5
Un élément flex ne rétrécit pas sous la largeur de son plus long contenu insécable (min-width: auto par défaut) : l'URL, sans espace, impose sa largeur à la carte, et la rangée déborde.
Corrections, à combiner :
min-width: 0sur les cartes, pour les autoriser à rétrécir, plusoverflow-wrap: anywheresur le texte pour que l'URL puisse se couper ;flex-wrap: wrapsur le conteneur (avec par exempleflex: 1 1 16remsur les cartes), pour que les cartes passent à la ligne sur un petit écran ; ou une grillerepeat(auto-fill, minmax(16rem, 1fr)).
Et le mieux : ne pas afficher l'URL brute, mais un texte de lien explicite (« Ateliers numériques : initiation »).
Tu avais trouvé ?