CSS Flexbox et la dimension des boîtes
Nous allons explorer le dimensionnement en Flexbox, qui est souvent un casse-tête : comment Flexbox décide-t-il de la taille de nos items ?
NdT : Les articles précédents ont été traduits dans la Cascade :
- Que se passe-t-il quand on crée un flexbox flex-container ?
- CSS Flexbox et l'alignement, guide complet.
Affichage initial des items flex
Si j’ai un ensemble d’items ayant des contenus de longueur variable et que je règle leur parent sur display: flex
, ils s’afficheront sous forme de ligne et s’aligneront au début de cet axe. Dans l'exemple ci-dessous, mes trois items ont un contenu réduit et ils peuvent afficher ces contenus sous forme de ligne ininterrompue. Il y a un espace à la fin du conteneur flex que les items n'occupent pas car la valeur initiale de flex-grow
est 0
, c'est à dire pas d'expansion.
Si j'ajoute du texte à ces éléments, ils finissent par remplir le conteneur et des retours à la ligne apparaissent. Les boîtes se voient attribuer un espace correspondant à la longueur de texte qu'elles contiennent — un item avec beaucoup de texte a plus d'espace. Du coup, on ne se retrouve pas avec une colonne maigrichonne contenant beaucoup de texte lorsque l’item suivant ne contient qu’un seul mot.
Ce comportement vous paraîtra sans doute familier si vous avez déjà utilisé Flexbox, et peut-être vous êtes-vous déjà demandé comment le navigateur effectuait ces redimensionnements, d'autant que si vous comparez les navigateurs modernes, vous pouvez constater qu'ils font tous la même chose. La raison en est que des détails comme celui-ci sont définis dans la spécification même, afin de s'assurer que quel que soit le navigateur on sache toujours comment fonctionne ce calcul. Nous pouvons utiliser la spécification pour trouver cette information par nous-mêmes.
Spécification de dimensionnement CSS intrinsèque et extrinsèque
Lorsqu'on examine le dimensionnement dans la spécification Flexbox, on découvre assez vite qu'une grande partie des informations dont on a besoin se trouve dans une autre spécification — CSS Intrinsic et Extrinsic Sizing. En effet, les concepts de dimensionnement que nous utilisons ne sont pas propres à Flexbox, de la même manière que les propriétés d’alignement ne sont pas propres à Flexbox, comme nous l'avons vu dans l'article précédent. Cependant, pour savoir comment ces dimensionnement sont utilisées dans Flexbox, il faut revenir à la spécification Flexbox. Pour vous éviter d'avoir à faire des allers-retours dans les specs, je vais donc vous donner quelques définitions-clés, que je vais utiliser dans la suite de l’article.
Taille préférée
La taille préférée d'une boîte est la taille définie par une width
ou une height
, ou par les alias logiques inline-size
et block-size
pour ces propriétés. Lorsque vous utilisez :
.box {
width: 500px;
}
ou l'alias logique inline-size
:
.box {
inline-size: 500px;
}
...vous déclarez que vous souhaitez que votre boîte ait une largeur de 500 pixels ou 500 pixels dans la direction inline
.
min-content size
La taille minimale du contenu, min-content
, est la plus petite taille possible pour une boîte sans créer d'overflow. Si votre boîte contient du texte, toutes les opportunités possibles de retour à la ligne (soft) seront utilisées.
max-content size
La taille maximale du contenu, max-content
, est la plus grande taille que la boîte puisse prendre afin de contenir le contenu. Si la boîte contient du texte sans retour à la ligne, elle apparaîtra sous la forme d'une longue chaîne de caractères ininterrompue.
Taille principale d'un item flex
La taille principale d'un élément flexible est la taille qu'il a dans la dimension principale (main). Si vous travaillez dans une rangée — en français — alors la taille principale est sa largeur. Dans une colonne en français, la taille principale est sa hauteur.
Les items ont également une taille principale minimale et maximale définie par leur min-width
ou leur min-height
dans la dimension principale.
Comprendre la taille d'un élément flexible
Maintenant que certains termes sont définis, nous pouvons examiner la façon dont nos éléments flex sont dimensionnés. La valeur initiale des propriétés flex
est la suivante:
flex-grow: 0
flex-shrink: 1
flex-basis: auto
Le calcul des dimensions part de flex-basis
. Si nous donnons à flex-basis
la valeur 0
, et à flex-grow
la valeur 1
, alors nos boîtes n'ont pas de largeur initiale et par conséquent l'espace à l'intérieur du conteneur flex est partagé de manière égale.
Si par contre flex-basis
a pour valeur auto
et flex-grow: 1
, alors l'espace disponible est distribué en prenant en compte la taille du contenu.
Lorsqu'il n'y a pas d'espace disponible, par exemple quand notre contenu ne tient pas sur une ligne unique, alors il n'y a pas d'espace à distribuer.
Il est donc important de bien comprendre auto
si nous voulons savoir comment Flexbox détermine la taille des boîtes. La valeur d'auto
sera notre point de départ.
Définir auto
Lorsque auto
est la valeur d'une propriété CSS, il a une signification spécifique à ce contexte. Le CSS Working Group a passé beaucoup de temps à clarifier la signification de auto
selon les contextes, comme l'explique cette présentation.
Nous pouvons trouver le sens de auto
utilisé en tant que flex-basis
dans la spécification. Les termes définis tout à l'heure devraient nous permettre d'analyser cette phrase:
Lorsque spécifié sur un item flex, le mot-clé
auto
va chercher la valeur de la propriété de taille principale pour l'utiliser commeflex-basis
. Si cette valeur est elle-mêmeauto
, alors la valeur utilisée estcontent
.
Autrement dit, si notre flex-basis
est auto
, Flexbox examine la propriété de taille principale définie. Par exemple, nous aurions une taille principale si nous avions donné une largeur à l’un au moins de nos articles flexibles. Dans l'exemple ci-dessous, les éléments ont tous une largeur de 110 px, cette taille est donc utilisée comme taille principale, car la valeur initiale de flex-basis
est auto
.
Cependant, notre exemple a des items sans width
ce qui signifie que leur dimension principale est auto
, et nous passons donc à la suite de la phrase "si cette valeur est elle-même auto
, alors la valeur utilisée est content
".
Nous devons maintenant regarder ce que dit la spec à propos du mot-clé content
. Voici une autre valeur que nous pouvons utiliser pour notre flex-basis
(dans les navigateurs compatibles), par exemple :
.item {
flex: 1 1 content;
}
La spécification définit content
ainsi :
Il indique un dimensionnement automatique basé sur le contenu du flex item. Typiquement, il est équivalent à la dimension max-content mais avec des ajustements permettant de traiter les ratios d'aspect, les contraintes de dimensionnement intrinsèque et les flux orthogonaux"
Dans notre exemple, avec des items flex contenant du texte, nous pouvons ignorer certains ajustements complexes et considérer content
comme étant la dimension max-content
.
Ceci explique pourquoi, lorsque nous avons peu de texte dans chaque item, nous n'avons pas de retour à la ligne. Les items flex sont dimensionnés automatiquement, Flexbox cherche leur dimension max-content
, les items s'ajustent à cette taille dans leur conteneur, et le tour est joué.
L'histoire ne s'arrête pas là, lorsque nous ajoutons du contenu les boîtes ne restent pas à la dimension max-content
. Sinon, elles sortiraient du conteneur avec un overflow. Une fois qu'il a rempli le container, le contenu retourne à la ligne et les items prennent des tailles différentes en fonction de leur contenu.
Résolution des tailles flexibles
À partir d'ici, la spécification devient raisonnablement complexe. Voici les prochaines étapes.
D'abord, ajouter la taille principale de tous les items et voir si elle est supérieure ou inférieure à l'espace disponible dans le container. Si la taille du conteneur est supérieure, nous passons à flex-grow
puisque nous avons de l'espace pour nous étendre.
Par contre, si la taille du conteneur est inférieure au total, nous nous tournons vers flex-shrink
car nous devons nous serrer.
Il est possible de choisir une taille pour certains items, qui deviennent ainsi "inflexibles". Si nous utilisons flex-grow
, cela inclurait tous les éléments qui ont flex-grow: 0
. C'est le scénario que nous avons lorsque nos articles flexibles ont de la place dans le conteneur. La valeur initiale de flex-grow
est 0
, nos items deviennent donc aussi grands que leur largeur maximale et ne grandissent plus à partir de leur taille principale.
Si nous utilisons flex-shrink
, cela inclura tous les éléments avec flex-shrink: 0
. Nous pouvons voir ce qui se passe dans cette étape si nous donnons à notre ensemble d’items flex un facteur de flex-shrink
de 0
. Les éléments sont figés dans leur état max-content
et ainsi ne peuvent pas s'organiser eux-mêmes de manière flexible pour tenir dans le conteneur.
Dans notre cas, avec leur valeurs initiales d'items flex, nos items peuvent se réduire. Nous continuons donc, et l'algorithme entre dans une boucle lui permettant de calculer l'espace à attribuer ou à réduire. Nous utilisons flex-shrink
puisque la taille totale de nos items est supérieure à la taille du conteneur — nous devons donc réduire l'espace.
Le facteur flex-shrink
est multiplié par la taille de base interne, ici c'est la taille max-content
. Cela donne une valeur avec laquelle réduire l'espace. Si les éléments retirent de l'espace uniquement en fonction du facteur de flex-shrink
, les éléments de petite taille risquent en principe de disparaître car tout leur espace a été supprimé, tandis que l'élément le plus volumineux peut encore être réduit.
Dans cette boucle, il existe une étape supplémentaire pour rechercher les éléments qui deviendraient plus petits ou plus grands que leur taille principale cible. Dans ce cas, l'élément cesse de croître ou de se réduire. Encore une fois, ceci permet d'éviter que certains objets ne deviennent minuscules ou énormes par rapport au reste des objets.
Tout cela a été simplifié du point de vue des spécifications, car je n'ai pas examiné certains des scénarios les plus limites. Vous pouvez vous en passer si vous souhaitez laisser Flexbox agir à votre place ou si vous ne recherchez pas la perfection au pixel près. Garder à l'esprit les deux mécanismes suivants vous aidera dans la plupart des cas :
- Si vous êtes en mode
grow
depuisauto
, alorsflex-basis
sera traitée comme n'importe quelle largeur ou hauteur de l'item ou comme son max-content. L'espace sera ensuite attribué en fonction du facteurflex-grow
en utilisant cette dimension comme point de départ. - Si vous êtes en mode
shrink
depuisauto
, alorsflex-basis
sera traitée comme n'importe quelle largeur ou hauteur de l'item ou comme son max-content. L'espace sera alors rétréci ou supprimé en fonction de la taille deflex-basis
multipliée par le facteurflex-shrink
et donc proportionnellement à la taille max-content des items.
Contrôler grow et shrink
J'ai passé la plus grande partie de cet article à décrire ce que fait Flexbox lorsqu'il se débrouille tout seul. Vous pouvez bien sûr exercer un meilleur contrôle sur vos items flexibles en utilisant les propriétés flex
. J'espère qu'elles vous paraîtront maintenant plus prévisibles grâce à une bonne compréhension de ce qui se passe en coulisses.
Si vous définissez votre propre flex-basis
ou si vous attribuez à l'élément lui-même une taille ensuite utilisée comme flex-basis
, vous reprenez le contrôle de l'algorithme, en indiquant à Flexbox que vous souhaitez augmenter ou réduire cette taille. Vous pouvez désactiver complètement la croissance ou la réduction en réglant flex-grow
ou flex-shrink
sur 0
.
Un dernier point sur cette question du contrôle : lorsque vous sentez monter le désir de mieux contrôler les éléments flexibles, prenez un moment pour vérifier que vous utilisez la bonne méthode de layout : si vous essayez d’aligner des éléments flexibles en deux dimensions, vous feriez sans doute mieux de choisir Grid Layout.
Débogage des problèmes liés à la taille
Si vos items flex atteignent une dimension inattendue, c'est généralement dû au fait que votre flex-basis
est auto
et que quelque chose donne à votre item une largeur, qui est ensuite utilisée comme flex-basis
. L'inspection de l'élément dans DevTools peut aider à identifier la provenance de la dimension. Vous pouvez également essayer de définir une flex-basis
de 0
, ce qui obligera Flexbox à traiter l'élément comme s'il avait une largeur nulle. Même si vous n'obtenez pas un résultat souhaité, cela vous aidera à identifier la valeur de flex-basis
utilisée, laquelle est peut-être la cause de vos problèmes de dimensionnement.
Espaces ou gouttières flex
Une fonctionnalité très demandée de Flexbox serait la possibilité de spécifier des espaces ou des gouttières entre les éléments flex comme on peut le faire avec CSS Grid Layout. Cette fonctionnalité est spécifiée pour Flexbox dans le cadre de Box Alignment et la première mise en œuvre du navigateur est en route. Pour Firefox ça devrait sortir avec Firefox 63. L'exemple suivant peut être visualisé dans Firefox Nightly.
Comme pour Grid Layout, la taille de la gouttière est prise en compte avant que l'espace ne soit distribué aux items flex.
On récapitule
Dans cet article, j’ai essayé d’expliquer en détail comment Flexbox définit la taille des éléments flexibles. Cela peut sembler un peu théorique, cependant, prendre un peu de temps pour comprendre le fonctionnement de cette solution peut vous faire gagner un temps considérable lors de l’utilisation de Flexbox dans vos layouts. Je trouve très utile de revenir au fait que, par défaut, Flexbox essaie de vous donner la présentation la plus judicieuse d’un ensemble d'items de tailles variables. Si un élément a plus de contenu, il dispose de plus d'espace. Si votre design et vous n'êtes pas d'accord avec ce que Flexbox estime préférable, vous pouvez reprendre le contrôle en définissant votre propre flex-basis
.