La Cascade

Un peu de tout sur CSS, HTML, SVG et JS,traduit du web anglophone
Rechercher

Comprendre flex-grow, flex-shrink et flex-basis

par Robin Rendle, 25 octobre 2022, css, flexbox, article original paru le 18 octobre 2022 dans CSS Tricks


Lorsque nous appliquons une propriété CSS à un élément, il se passe beaucoup de choses en coulisses. Par exemple, disons que nous avons ce HTML :

<div class="parent">
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
</div>

...et nous ajoutons ce CSS :

.parent {
  display: flex;
}

Techniquement, ce ne sont pas les seuls styles que nous appliquons lorsque nous écrivons la ligne de CSS ci-dessus. En fait, tout un tas de propriétés seront appliquées aux éléments .child ici, comme si nous avions écrit ces styles nous-mêmes :

.child {
  flex: 0 1 auto; /* valeurs flex par défaut */
}

Étrange ! Pourquoi ces éléments ont-ils ces styles supplémentaires qui leur sont appliqués alors que nous n'avons pas écrit ce code ? Eh bien, c'est parce que certaines propriétés ont des valeurs par défaut qui sont ensuite destinées à être remplacées par nous. Et si nous ne savons pas que ces styles sont appliqués lorsque nous écrivons le CSS, nos mises en page peuvent devenir assez déroutantes et difficiles à gérer.

La propriété flex ci-dessus est ce que l'on appelle une propriété CSS abrégée (ou raccourcie : shorthand). En fait, ce que ça fait c'est qu'on définit trois propriétés CSS distinctes en même temps. Ce que nous avons écrit ci-dessus revient donc à écrire ceci :

.child {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
}

Une propriété raccourcie est donc une façon de regrouper un tas de propriétés CSS différentes pour faciliter l'écriture de plusieurs propriétés à la fois, précisément comme la propriété background où nous pouvons écrire quelque chose comme ceci :

body {
  background: url(sweettexture.jpg) top center no-repeat fixed
    padding-box content-box red;
}

J'essaie d'éviter les propriétés raccourcies car elles peuvent être assez déroutantes et j'ai souvent tendance à écrire les versions longues à la main, simplement parce que mon cerveau ne parvient pas à analyser de longues lignes de valeurs de propriétés. Mais il est recommandé d'utiliser le raccourci lorsqu'il s'agit de flexbox, ce qui semble... bizarre... c'est-à-dire jusqu'à ce que vous compreniez que la propriété flex fait beaucoup de travail et que chacune de ses sous-propriétés interagit avec les autres.

En outre, les styles par défaut sont une bonne chose car nous n'avons pas besoin de savoir ce que font ces propriétés flexbox dans 90 % des cas. Par exemple, lorsque j'utilise flexbox, j'ai tendance à écrire quelque chose comme :

.parent {
  display: flex;
  justify-content: space-between;
}

Je n'ai même pas besoin de me soucier des éléments enfants ou des styles qui leur ont été appliqués, et c'est super ! Dans ce cas, nous alignons les éléments enfants côte à côte, puis nous les espaçons de manière égale les uns des autres. Deux lignes de CSS nous donnent beaucoup de pouvoir ici et c'est ce qu'il y a de mieux avec flexbox et ces styles hérités — nous n'avons pas besoin de comprendre toute la complexité sous le capot si nous voulons simplement faire la même chose 90 % du temps. C'est remarquablement intelligent car toute cette complexité est cachée.

Mais qu'en est-il si nous voulons comprendre le fonctionnement réel de flexbox, y compris les propriétés flex-grow, flex-shrink et flex-basis ? Et quelles choses sympas pouvons-nous faire avec elles ?

Il suffit de consulter l'Almanach CSS-Tricks. Et c'est bon !

Nan, je plaisante. Commençons par un aperçu rapide et un peu simplifié, et revenons aux propriétés flex par défaut qui sont appliquées aux éléments enfants :

.child {
  flex: 0 1 auto;
}

Ces styles par défaut indiquent à cet élément enfant comment s'étirer et se développer. Mais chaque fois que je vois qu'ils sont utilisés ou remplacés, je trouve utile de penser à ces propriétés abrégées comme ceci :

/* C'est ainsi que je vois la règle ci-dessus dans ma tête */

.child {
  flex: [flex-grow] [flex-shrink] [flex-basis];
}

/* ou bien comme ça... */

.child {
  flex: [max] [min] [taille idéale];
}

La première valeur est flex-grow et elle est réglée sur 0 car, par défaut, nous ne voulons pas que nos éléments s'étendent (la plupart du temps). Nous voulons plutôt que chaque élément dépende de la taille du contenu qu'il contient. Voici un exemple :

.parent {
  display: flex;
}
voir flex-grow-default de robinrendle dans CodePen

J'ai ajouté la propriété contenteditable à chaque élément .child ci-dessus pour que vous puissiez cliquer dessus et saisir encore plus de contenu. Vous voyez comment ça réagit ? C'est le comportement par défaut d'un élément flexbox : flex-grow est défini sur 0 car nous voulons que l'élément grandisse en fonction du contenu qu'il contient.

Mais ! Si nous devions changer la valeur par défaut de la propriété flex-grow de 0 à 1, comme ceci...

.child {
  flex: 1 1 auto;
}

...alors, tous les éléments prendraient une part égale de l'élément .parent, mais seulement si les longueurs de leurs contenus sont les mêmes.

voir flex-grow-default-1 de robinrendle dans CodePen

C'est exactement comme si j'avais écrit :

.child {
  flex-grow: 1;
}

...en laissant de côté les autres valeurs parce que celles-ci ont été définies par défaut de toute façon. Je pense que cela m'a longtemps perturbé lorsque j'ai commencé à travailler avec des mises en page flexibles. Je voyais du code qui ajoutait juste flex-grow et je me demandais d'où venaient les autres styles.

Maintenant, si nous voulions qu'un seul de ces éléments croisse plus que les autres, il nous suffirait de faire :

.child-three {
  flex: 3 1 auto;
}

/* ou bien nous pourrions simplement écrire... */

.child-three {
  flex-grow: 3;
}
voir flex-grow-default-2 de robinrendle dans CodePen

Ce code est-il étrange à regarder, même dix ans après l'arrivée de Flexbox dans les navigateurs ? Il l'est certainement pour moi. J'ai besoin d'une puissance cérébrale supplémentaire pour dire "Ah oui, max, min, taille idéale" lorsque je lis le raccourci, mais ça s'améliore avec le temps. Quoi qu'il en soit, dans l'exemple ci-dessus, les deux premiers éléments enfants occuperont proportionnellement la même quantité d'espace, mais le troisième élément essaiera de croître jusqu'à trois fois l'espace des autres.

C'est là que les choses deviennent étranges, car tout dépend du contenu des éléments enfants. Même si nous avons défini flex-grow à 3, comme nous l'avons fait dans l'exemple ci-dessus, si nous ajoutons ensuite plus de contenu, la mise en page fera quelque chose d'étrange comme ceci :

voir flex-grow-3 de robinrendle dans CodePen

Cette deuxième colonne prend maintenant trop d'espace ! Nous y reviendrons plus tard, mais pour l'instant, il est juste important de se rappeler que le contenu d'un élément flex a un impact sur la façon dont flex-grow, flex-shrink et flex-basis fonctionnent ensemble.

OK, passons maintenant à flex-shrink. Rappelez-vous que c'est la deuxième valeur dans le raccourci :

.child {
  flex: 0 1 auto; /* flex-shrink = 1 */
}

flex-shrink indique au navigateur quelle doit être la taille minimale d'un élément. La valeur par défaut est 1, ce qui revient à dire : "Prenez la même quantité d'espace à tout moment". Cependant, si nous définissons cette valeur à 0 comme ceci :

.child {
  flex: 0 0 auto;
}

...alors nous disons à cet élément de ne pas rétrécir du tout maintenant. Reste à la même taille, maudit élément ! c'est en substance ce que dit cette CSS, et c'est précisément ce qu'elle va faire. Nous reviendrons sur cette propriété dans un instant, une fois que nous aurons examiné la valeur finale de ce raccourci.

flex-basis est la dernière valeur ajoutée par défaut dans le raccourci flex, et c'est la façon dont nous indiquons à un élément de s'en tenir à une taille idéale. Par défaut, elle est définie sur auto, ce qui signifie "Utilisez ma hauteur ou ma largeur". Ainsi, lorsque nous définissons un élément parent pour display: flex...

.parent {
  display: flex;
}

.child {
  flex: 0 1 auto;
}

Nous aurons ceci par défaut dans le navigateur :

voir flex-basis example 1 de robinrendle dans CodePen

Vous avez remarqué que tous les éléments ont la largeur de leur contenu par défaut ? C'est parce qu'auto dit que la taille idéale de notre élément est définie par son contenu. Pour que tous les éléments occupent tout l'espace du parent, nous pouvons régler les éléments enfants sur width : 100%, ou nous pouvons régler flex-basis sur 100%, ou nous pouvons régler flex-grow sur 1.

Est-ce que ça fait sens pour vous ? C'est bizarre, hein ! En fait, oui, lorsqu'on y pense. Chacune de ces valeurs abrégées a un impact sur les autres et c'est pourquoi il est recommandé d'écrire ce raccourci en premier lieu plutôt que de définir ces valeurs indépendamment les unes des autres.

Bon, passons à autre chose. Lorsque nous écrivons quelque chose comme ceci...

.child-three {
  flex: 0 1 1000px;
}

...ce que nous disons ici au navigateur, c'est de régler flex-basis sur 1000px ou "s'il vous plaît, essayez d'occuper 1000px d'espace". Si ce n'est pas possible, l'élément occupera cet espace proportionnellement aux autres éléments.

voir flex-basis example 2 de robinrendle dans CodePen

Vous remarquerez peut-être que sur les petits écrans, ce troisième élément n'est pas réellement de 1000px ! C'est parce qu'il s'agit en fait d'une suggestion. Nous avons toujours l'option flex-shrink appliquée, qui indique à l'élément de se réduire à la même taille que les autres éléments.

De même, l'ajout de contenu aux autres enfants aura toujours un impact ici :

voir flex-basis example 2 de robinrendle dans CodePen

Maintenant, si nous voulions empêcher cet élément de rétrécir, nous pourrions écrire quelque chose comme ceci :

.child-three {
  flex: 0 0 1000px;
}

Rappelez-vous, flex-shrink est la deuxième valeur ici et en la réglant à 0, nous disons, "Ne rétrécis jamais, grand idiot." Et ce n'est pas le cas. L'élément se détachera même de l'élément parent parce qu'il ne sera jamais plus petit que 1000px de large :

voir flex-basis example 3 de robinrendle dans CodePen

Maintenant, tout cela change si nous définissons flex-wrap sur l'élément parent :

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child-three {
  flex: 0 0 1000px;
}

Nous verrons quelque chose comme ça :

voir flex-basis example 4 de robinrendle dans CodePen

En effet, par défaut, les éléments flexibles essaieront de tenir sur une seule ligne, mais flex-wrap : wrap l'ignorera complètement. Maintenant, si ces éléments flex ne peuvent pas tenir dans le même espace, ils se sépareront sur une nouvelle ligne.

Quoi qu'il en soit, il ne s'agit là que de quelques-unes des façons dont les propriétés flex s'entrechoquent et de la raison pour laquelle il est si précieux de comprendre comment ces propriétés fonctionnent sous le capot. Chacune de ces propriétés peut affecter l'autre, et si vous ne comprenez pas le fonctionnement d'une propriété, alors d'une certaine façon vous ne comprenez pas du tout comment tout cela fonctionne — ce qui m'a certainement déconcerté avant que je ne commence à creuser ce sujet !

Mais pour résumer :

👉🏿 Essayez d'utiliser le raccourci flex
👉🏿 Souvenez-vous de la taille maximale, minimale et idéale lorsque vous le faites
👉🏿 N'oubliez pas que le contenu d'un élément peut également avoir un impact sur la façon dont ces valeurs fonctionnent ensemble.

Autres ressources externes

Articles de Robin Rendle traduits dans La Cascade

Voir la page de Robin Rendle et la liste de ses articles dans La Cascade.
Article original paru le 18 octobre 2022 dans CSS Tricks
Traduit avec l'aimable autorisation de CSS Tricks et de Robin Rendle.
Copyright CSS Tricks © 2022