Chapitre 8 : Gestion de la croissance et du rétrécissement des items Flex
Concepts clés : flex-grow, flex-shrink, flex-basis
L'un des aspects les plus puissants, mais aussi les plus déroutants de Flexbox, est la manière dont les éléments s'adaptent à l'espace disponible. Contrairement au modèle de boîte traditionnel où une largeur (width) est souvent fixe, Flexbox introduit un système de négociation d'espace.
Dans ce chapitre, nous allons décomposer les trois propriétés qui permettent à un élément de grandir, de rétrécir ou de maintenir une taille de base : flex-grow, flex-shrink et flex-basis.
Le concept de Flexibilité
1. Quoi
La flexibilité d'un item Flex est déterminée par trois propriétés distinctes qui travaillent ensemble pour calculer la taille finale de l'élément sur l'axe principal (horizontal par défaut).
flex-basis: Définit la taille initiale de l'élément avant que l'espace restant ne soit distribué. C'est le point de départ du calcul.flex-grow: Définit la capacité d'un élément à s'étendre pour occuper l'espace vide restant dans le conteneur.flex-shrink: Définit la capacité d'un élément à se réduire lorsque l'espace total requis par les items dépasse la taille du conteneur.
2. Pourquoi
Dans le développement web moderne, nous créons des interfaces "responsives". Un design ne peut pas reposer uniquement sur des pixels fixes. Par exemple :
- Dans une barre de navigation, vous voulez que le logo garde sa taille, mais que la barre de recherche occupe tout l'espace restant.
- Dans une grille de cartes, vous voulez que les cartes se réduisent proportionnellement sans déborder du cadre sur un petit écran.
- Dans un pied de page, vous voulez répartir trois colonnes de manière égale, peu importe la largeur du navigateur.
Sans ces propriétés, nous devrions utiliser des calculs complexes avec calc() ou des pourcentages rigides qui ne gèrent pas bien les contenus de tailles variables (comme un texte plus ou moins long).
3. Comment
A. Syntaxe de base
Ces propriétés s'appliquent aux enfants du conteneur flex (les flex-items), et non au conteneur lui-même.
.item {
/* Syntaxe raccourcie : flex: [grow] [shrink] [basis]
- grow et shrink sont des nombres SANS unité (facteur de proportion : 0, 1, 2…)
- basis est une taille AVEC unité (px, %, rem, auto…) */
flex: 1 1 200px;
}
B. Cas concret : La barre d'outils intelligente
Imaginons une barre d'outils avec un bouton "Accueil", un champ de recherche et un bouton "Profil". Nous voulons que le champ de recherche soit "élastique" et occupe tout l'espace disponible, tandis que les boutons restent à leur taille naturelle.
<div class="toolbar">
<button class="btn">Accueil</button>
<input type="text" class="search-bar" placeholder="Rechercher..." />
<button class="btn">Profil</button>
</div>
.toolbar {
display: flex;
gap: 1rem;
padding: 1rem;
background-color: #f4f4f4;
border-radius: 8px;
}
.btn {
/* Les boutons ne grandissent pas, ne rétrécissent pas,
et prennent leur taille de contenu */
flex: 0 0 auto;
padding: 0.5rem 1rem;
}
.search-bar {
/*
flex-grow: 1 -> Prend tout l'espace restant
flex-shrink: 1 -> Peut rétrécir si l'écran est très petit
flex-basis: 0 -> On ignore la taille initiale du contenu pour
laisser flex-grow gérer la distribution
*/
flex: 1 1 0;
padding: 0.5rem;
}
C. Limitations et subtilités
- L'influence du contenu : Par défaut,
flex-basis: autoregarde la largeur (width) de l'élément. S'il n'y a pas de largeur définie, il regarde la taille du contenu. Cela signifie qu'un item avec beaucoup de texte sera "plus grand" au départ qu'un item avec un seul mot, même s'ils ont le mêmeflex-grow. - Le minimum intrinsèque : Un élément flex ne rétrécira jamais en dessous de la taille de son contenu le plus long (comme un mot très long ou une image) à moins que vous ne fixiez
min-width: 0. C'est un piège classique où un item "casse" le layout malgré unflex-shrink: 1.
4. Zone de Danger
❌ Erreur commune : Utiliser width: 100% sur un item flex pour qu'il occupe tout l'espace.
- Problème :
width: 100%force l'élément à faire la taille du parent, ce qui pousse souvent les autres éléments hors du conteneur ou crée un débordement horizontal. - ✅ Bonne pratique : Utiliser
flex-grow: 1ouflex: 1. Cela dit au navigateur : "Prends l'espace qui reste après avoir placé les autres éléments", ce qui est beaucoup plus sûr.
❌ Erreur commune : Oublier que flex-shrink: 0 empêche totalement le rétrécissement.
- Problème : Si vous avez un élément avec
flex-shrink: 0et que l'écran devient trop petit, l'élément refusera de rétrécir et créera un scroll horizontal moche. - ✅ Bonne pratique : N'utilisez
flex-shrink: 0que pour des éléments qui ne doivent absolument pas être déformés (comme des icônes).
Algorithme de calcul de la taille des items Flex
Le raccourci flex
En pratique, on utilise rarement les trois propriétés séparément. On utilise la propriété raccourcie flex.
Syntaxe : flex: [flex-grow] [flex-shrink] [flex-basis];
| Valeur | Équivalence | Effet |
|---|---|---|
flex: initial | 0 1 auto | (Défaut) Ne grandit pas, peut rétrécir, taille basée sur le contenu. |
flex: auto | 1 1 auto | Grandit et rétrécit, taille basée sur le contenu. |
flex: none | 0 0 auto | Rigide. Ne grandit pas, ne rétrécit pas. |
flex: 1 | 1 1 0 | Grandit et rétrécit, ignore la taille du contenu pour une distribution égale. |
Note cruciale : Quand vous écrivez flex: 1, le navigateur transforme automatiquement le flex-basis en 0. C'est pour cela que flex: 1 crée des colonnes de tailles parfaitement égales, alors que flex: auto crée des colonnes proportionnelles à leur contenu.
Questions clés
Q1 : Quelle est la différence fondamentale entre flex-grow: 1 et width: 100% ?
Découvrir la réponse
width: 100% impose une taille fixe égale à la largeur du parent, ignorant les autres éléments présents. flex-grow: 1 demande à l'élément d'occuper l'espace restant après que tous les autres items ont pris leur place.
Q2 : Pourquoi mon élément ne rétrécit-il pas alors que j'ai mis flex-shrink: 1 ?
Découvrir la réponse
C'est probablement dû à la taille minimale intrinsèque du contenu. Par défaut, un item flex a un min-width: auto. Si le contenu (un mot long, une image) est plus large que l'espace disponible, l'élément ne rétrécira pas davantage. La solution est d'ajouter min-width: 0.
Q3 : Si j'ai deux éléments, l'un avec flex-grow: 1 et l'autre avec flex-grow: 2, comment l'espace est-il réparti ?
Découvrir la réponse
L'espace restant est divisé en "parts". Ici, il y a $1 + 2 = 3$ parts au total. Le premier élément recevra $1/3$ de l'espace vide, et le second recevra $2/3$. Attention : cela s'ajoute à leur flex-basis initiale.
Q4 : Que se passe-t-il si je ne définis pas de flex-basis ?
Découvrir la réponse
La valeur par défaut est auto. Le navigateur regarde alors la propriété width (ou height sur l'axe vertical). Si aucune largeur n'est définie, il utilise la taille naturelle du contenu.
Mise en pratique
Exercice 1 : Reproduction guidée
Objectif : Créer un header avec un logo à gauche, un menu centré et un bouton de connexion à droite. Le menu doit occuper tout l'espace central.
- Le logo et le bouton de connexion ne doivent pas changer de taille.
- Le menu doit être flexible.
HTML de départ :
<header class="header">
<div class="logo">Logo</div>
<ul class="nav-menu">
<li><a href="#">Accueil</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
<button class="login-btn">Connexion</button>
</header>
Découvrir la solution commentée
.header {
display: flex;
align-items: center;
gap: 20px;
padding: 10px;
background: #eee;
}
.logo, .login-btn {
/* Ne grandit pas, ne rétrécit pas, taille auto */
flex: 0 0 auto;
}
.nav-menu {
/* Grandit pour remplir le vide, peut rétrécir, basis 0 pour centrage propre */
flex: 1;
display: flex;
justify-content: center;
list-style: none;
gap: 15px;
}
Exercice 2 : Adaptation
Objectif : Vous avez un dashboard avec une barre latérale (sidebar) et un contenu principal.
- La sidebar doit toujours faire
250pxet ne jamais rétrécir. - Le contenu principal doit occuper tout le reste de l'espace.
- Si l'écran devient très petit, le contenu principal doit pouvoir rétrécir, mais la sidebar doit rester fixe.
HTML de départ :
<div class="dashboard">
<aside class="sidebar">
<p>Navigation</p>
</aside>
<main class="main-content">
<p>Contenu principal</p>
</main>
</div>
Découvrir la solution commentée
.dashboard {
display: flex;
}
.sidebar {
/*
grow: 0 (ne grandit pas)
shrink: 0 (ne rétrécit JAMAIS)
basis: 250px (taille fixe)
*/
flex: 0 0 250px;
background: #2c3e50;
color: white;
}
.main-content {
/*
grow: 1 (prend tout le reste)
shrink: 1 (peut rétrécir)
basis: 0 (on ignore la taille du contenu pour le calcul)
*/
flex: 1 1 0;
background: #ecf0f1;
}
Exercice 3 : Conception
Objectif : Créer une rangée de 3 cartes de produits.
- Chaque carte doit essayer de faire
300pxde large. - Si l'écran est large, elles doivent se répartir l'espace équitablement pour remplir la ligne.
- Si l'écran est étroit, elles doivent rétrécir proportionnellement pour ne pas déborder du conteneur.
- Les cartes doivent avoir exactement la même largeur, même si l'une a un titre plus long que l'autre.
HTML de départ :
<div class="product-grid">
<div class="card">
<h3>Produit 1</h3>
<p>Description courte.</p>
</div>
<div class="card">
<h3>Produit avec un titre beaucoup plus long que les autres</h3>
<p>Description.</p>
</div>
<div class="card">
<h3>Produit 3</h3>
<p>Description.</p>
</div>
</div>
Découvrir la solution commentée
.product-grid {
display: flex;
gap: 20px;
padding: 20px;
}
.card {
/*
grow: 1 -> Pour remplir l'espace si le parent fait > 900px
shrink: 1 -> Pour rétrécir si le parent fait < 900px
basis: 300px -> La taille cible idéale
*/
flex: 1 1 300px;
/*
SÉCURITÉ : On ajoute min-width: 0 pour éviter que
un texte trop long ne bloque le rétrécissement
*/
min-width: 0;
border: 1px solid #ccc;
padding: 15px;
}