Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

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-areas et subgrid ;
  • 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 ?

FlexboxGrid
Dimensionsune : ligne ou colonnedeux : lignes et colonnes
Logiquepartir du contenu : les éléments prennent la place dont ils ont besoin, puis on répartit le restepartir de la mise en page : on dessine la grille, puis on y range les éléments
Typiquementune barre de navigation, une rangée de boutons, le contenu d'une carte (image, texte, bouton poussé en bas), un centragela 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).

html
<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>
css
.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.
flex-direction: row123axe principal : justify-contentaxe secondaire (vertical) : align-itemsflex-direction: column123axe principal : justify-contentaxe secondaire : align-items
justify-content agit sur l'axe principal, align-items sur l'axe secondaire ; flex-direction fait pivoter les deux

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éValeursEffet
flex-directionrow (par défaut), column, row-reverse, column-reversesens de l'axe principal
flex-wrapnowrap (par défaut), wrapwrap 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 :

ValeurEffet (en row)
flex-start (par défaut)serrés à gauche
flex-endserrés à droite
centergroupés au centre
space-betweenle premier à gauche, le dernier à droite, l'espace réparti entre eux
space-aroundun espace égal de part et d'autre de chaque élément (donc deux fois moins aux bords)
space-evenlydes espaces tous égaux, bords compris

align-items les aligne sur l'axe secondaire :

ValeurEffet (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-enden haut, en bas
centercentrés verticalement
baselinealigné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éQuestionDéfaut
flex-basisquelle taille de départ, avant répartition ?auto (la taille du contenu, ou width)
flex-growquelle part de l'espace libre l'élément prend-il ?0 : il ne grandit pas
flex-shrinkquelle 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: 11 1 0%les éléments se partagent toute la place, à parts égales
flex: 22 1 0%cet élément prend deux parts quand les autres en prennent une
flex: auto1 1 autograndit et rétrécit à partir de la taille de son contenu
flex: none0 0 autotaille fixe, ne bouge pas (un logo, une icône)
flex: 0 0 15remexactement 15rem de large, ni plus, ni moins
css
/* 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

html
<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>
css
.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.

css
.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 :

css
.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).

ÉcritureRé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 1frune 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).

css
.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 :

css
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; }
entetelateral16remcontenu1frpied1231234grid-template-columns:16rem 1fr;grid-template-areas:"entete entete""lateral contenu""pied pied";les lignes de grille, en pointillés,sont numérotées à partir de 1
grid-template-areas dessine la page ; les lignes de grille sont numérotées à partir de 1

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.

css
@media (width < 48rem) {
  body {
    grid-template-columns: 1fr;
    grid-template-areas:
      "entete"
      "contenu"
      "lateral"
      "pied";
  }
}

Une grille de cartes responsive sans media query

css
.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-fillauto-fit
Quand il y a peu d'élémentsgarde des colonnes vides : les cartes gardent leur largeursupprime 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 :

css
.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.

  1. Le menu principal : cinq liens côte à côte.
  2. La page : en-tête, colonne latérale, contenu, pied de page.
  3. Une galerie de 24 photos de même taille, en rangées bien alignées.
  4. Une rangée d'étiquettes de longueurs variées (« Jeunesse », « Numérique », « Gratuit »…), qui passent à la ligne.
  5. 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
  1. Flexbox : une seule dimension, des éléments de largeurs différentes qui se suivent.
  2. Grid : deux dimensions, une structure dessinée à l'avance (grid-template-areas).
  3. Grid : les photos doivent s'aligner en lignes et en colonnes.
  4. Flexbox avec flex-wrap: wrap : chaque étiquette garde sa largeur naturelle, il n'y a pas de colonnes à aligner.
  5. 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 :

  1. si leurs flex-grow valent 1, 1 et 1 ;
  2. s'ils valent 0, 1 et 3 ;
  3. si tous ont flex: 1 (donc flex-basis: 0%).
Voir la réponseMasquer la réponse de l'exercice 2

L'espace libre vaut 900 − (200 + 100 + 100) = 500 px.

  1. 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.
  2. 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.
  3. Avec flex-basis: 0%, tout l'espace (900 px) est partagé à parts égales : 300 px chacun. C'est pour ça que flex: 1 donne 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

css
.grille {
  display: grid;
  grid-template-columns: 10rem 1fr 1fr;
  gap: 1rem;
}
.a { grid-column: 2 / 4; }
.b { grid-column: 1 / -1; }
  1. Combien la grille a-t-elle de lignes verticales ? Quels sont leurs numéros positifs ?
  2. Quelles colonnes l'élément .a occupe-t-il ?
  3. Et l'élément .b ?
  4. 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
  1. Trois colonnes, donc quatre lignes verticales, numérotées 1, 2, 3 et 4.
  2. De la ligne 2 à la ligne 4 : les deuxième et troisième colonnes.
  3. De la ligne 1 à la dernière (−1, c'est-à-dire 4) : toute la largeur.
  4. 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
css
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: 0 sur les cartes, pour les autoriser à rétrécir, plus overflow-wrap: anywhere sur le texte pour que l'URL puisse se couper ;
  • flex-wrap: wrap sur le conteneur (avec par exemple flex: 1 1 16rem sur les cartes), pour que les cartes passent à la ligne sur un petit écran ; ou une grille repeat(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é ?

Chapitre terminé !

Fais les 5 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Responsive design : media queries et images responsivesRevoir le sommaire du cours