Mise en page flexible

Alexandre Niveau
GREYC — Université de Caen

Présentation

  • CSS Flexible Box Layout Module, introduit au début des années 2010 à CSS3
  • Objectif : faciliter la mise en page (se passer des flottants et autres bricolages), et s'adapter à l'ère du responsive design
  • Utilisable en prod depuis au moins 2015 (voir stats actuelles), avant l'arrivée de Grid
  • première fois qu'on pouvait faire de la vraie mise en page en CSS ⇒ utilisé un peu à toutes les sauces
  • Moins fondamental depuis l'arrivée de Grid , qui résout davantage de problèmes usuels de mise en page, mais toujours utile car complémentaire sur

Principe

  • Flexbox permet de faire de la mise en page sur une seule dimension, mais avec possibilité de retour à la ligne
  • un peu comme les mots dans un texte
  • Les deux dimensions sont complètement asymétriques, contrairement à Grid
  • On va plutôt se tourner vers flexbox
    • quand on a des blocs de dimensions variées
    • quand l'alignement des « lignes » entre elles n'a pas d'importance

Conteneur flex

Fonctionnement : comme pour Grid, un conteneur en display: flex contrôle la façon dont ses enfants vont se comporter (position, alignement, largeur…)

.container {
    display: flex;
}
  • Tous les fils (directs !) du conteneur deviennent des flex items
  • Résultat : un peu comme si on les avait tous mis en inline-block (démo)
on peut aussi utiliser inline-flex, pour un conteneur flex qui se comporte comme un inline de l'extérieur

Direction

  • Il est trivial de changer la direction dans laquelle les items sont placés (démo)
  • .container {
        display: flex;
        flex-direction: column;  /* ou row, row-reverse, column-reverse */
    }
  • Par défaut la direction est row
  • Cette flexibilité dans l'orientation va compliquer un peu les propriétés : on ne peut pas parler de vertical/horizontal ou de haut/bas/gauche/droite, on parle d'axe principal et d'axe secondaire, et de début/fin.
Principes de flexbox

Alignement dans l'axe perpendiculaire

  • Tous les items n'ont pas la même taille sur l'axe perpendiculaire
  • La propriété align-items gère leur alignement sur cet axe (démo)
    Alignement sur l'axe secondaire
  • On peut aligner un item particulier en lui appliquant la propriété align-self (démo)
    .container {
    	display: flex;
    	align-items: flex-start;
    }
    .mon-item {
    	align-self: stretch;
    }
    

Alignement dans l'axe principal

  • Si le conteneur a une taille fixée suivant l'axe principal, il peut y avoir de l'espace en trop
  • La propriété justify-content gère la répartition de cet espace (démo)
    Justification du contenu + space-evenly, plus récent (voir démo)
  • Contrairement à l'alignement sur l'axe secondaire, pas de propriété spéciale pour séparer un item particulier des autres… (justify-items et donc justify-self, qui existent pour les grilles, n'ont aucun sens pour flexbox)
  • … mais on peut utiliser margin:auto : dans une flexbox, une marge auto absorbe tout l'espace disponible (démo)
  • En particulier, utile pour centrer horizontalement et verticalement un bloc dans son parent (démo)
  • Attention : on pourrait imaginer que la valeur stretch fonctionne, pour que les items prennent tout l'espace disponible, mais ça n'existe pas pour flexbox : justify-content ne modifie jamais la taille des items
  • on va voir plus tard comment obtenir ce résultat, en utilisant une propriété sur les items (pas sur le conteneur)

Retour à la ligne

  • S'il y a trop d'items sur l'axe principal, il est possible de demander au conteneur de passer à la ligne (ou « à la colonne »…)
  • propriété flex-wrap contrôle le retour à la ligne
  • Par défaut, sa valeur est nowrap : on reste sur une seule ligne
  • Mais on peut changer ça et passer en multi-lignes (démo)
    .container {
    	display: flex;
    	flex-wrap: wrap;  /* ou wrap-reverse */
    }
    
  • La propriété flex-flow est un raccourci pour flex-direction et flex-wrap

Espacement des lignes

  • Si un conteneur multi-lignes a une taille fixée suivant l'axe perpendiculaire, il peut rester de l'espace en trop
  • La propriété align-content gère la répartition de cet espace
  • Comme justify-content, mais sur l'axe secondaire (démo)
Alignement des lignes de contenu

Flex items

On a vu toutes les propriétés qu'on peut appliquer au conteneur pour contrôler le positionnement de ses enfants (appelés flex items)

L'autre aspect fondamental de flexbox est qu'il y a aussi des propriétés pour les flex items, qui permettent de contrôler leur taille et leur ordre

Taille des items : propriété flex

  • On peut donner aux flex items une taille flexible, qui va s'adapter en fonction de l'espace disponible (trop ou pas assez)
  • propriété flex (attention : porte sur les items et pas sur le conteneur !)
  • Cas d'utilisation le plus simple :
    .container {
    	display: flex;
    }
    
    #mon-item {
    	flex: 1;
    }
  • l'item mon-item va grossir autant que possible pour que tout l'espace soit occupé sur l'axe principal (démo)

Valeurs de la propriété flex

  • La propriété flex est un raccourci de trois propriétés de base :
    • flex-grow, qui gère comment l'item doit grossir s'il y a trop d'espace (0 pour qu'il ne grossisse pas)
    • flex-shrink, qui gère comment l'item doit rétrécir s'il n'y a pas assez d'espace (0 pour qu'il ne rétrécisse pas)
    • flex-basis, qui indique la taille de base (« préférée ») de l'item
  • La valeur par défaut est 0 1 auto : l'item utilise sa taille naturelle, ou celle donnée par width/height, mais rétrécit si besoin
  • Quand on met flex:1, c'est comme si on mettait 1 1 0% : l'item a une taille de base nulle, va grossir si besoin, et rétrécir si besoin
  • Spécification assez complexe, interaction entre les propriétés pas évidente. On va voir l'essentiel

Donner une taille proportionnelle aux items

  • Pour que les items se répartissent équitablement l'espace disponible, il suffit de tous les mettre en flex:1 (démo, et précisions sur les aspects piégeux)
    .container { display: flex; }
    .item { flex: 1; }
  • En donnant une valeur plus grande à l'un des items, on le rend plus grand par rapport aux autres (démo) :
    .container { display: flex; }
    .item { flex: 1; }
    #mon_item_favori { flex: 3; }

Item flexible avec taille de base

  • On peut donner une taille de base à l'un des items (démo) :
    .container { display: flex; }
    .item { flex: 1; }
    #mon_item_favori { flex: 3 200px; }
    Il fera 200px de largeur par défaut, et grossira 3 fois plus vite que les autres lors de la distribution d'espace supplémentaire
  • Enfin, on peut empêcher notre item de rétrécir (démo) :
    .container { display: flex; }
    .item { flex: 1; }
    #mon_item_favori { flex: 3 0 200px; }
  • Le reste est moins utile, voir spécification et tutos si ça vous intéresse (un article avec des précisions sur les calculs)
Voir ci cette démo apporte qqch

Ordre des items

  • Flexbox permet de contrôler l'ordre des items ⇒ on peut le rendre complètement indépendant du code source
  • La propriété order sur un item permet de changer son ordre d'affichage
    <ul style="display: flex; list-style: none">
    	<li style="order: 2">Premier</li>
    	<li style="order: 1">Deuxième</li>
    	<li style="order: 3">Troisième</li>
    </ul>
    
    • Premier
    • Deuxième
    • Troisième
  • Fonctionnement très simple:
    • plus la valeur est haute, plus l'item est placé vers la fin
    • à valeur égale c'est l'ordre du code qui prévaut
    • valeur par défaut : 0

Limites de flexbox

  • Flexbox ne permet pas de faire n'importe quoi : en particulier, la gestion des conteneurs multi-lignes est limitée
  • si on a besoin de plus, c'est qu'on fait de la mise en page sur deux dimensions ⇒ c'est Grid qu'il faut !
  • Ne permet pas de faire grossir les items en largeur, sauf sur la dernière ligne s'il n'y a pas assez d'items (démo)
    ⇒ on peut le faire avec Grid
  • Ne permet pas de faire occuper toute la hauteur restante à une ligne (démo)
    ⇒ rajouter des div, ou utiliser Grid et flex ensemble

Remarque

Si vous avez envie d'utiliser justify-content:stretch, rappelez-vous bien que ça ne marche pas avec flexbox

Pour obtenir cet effet il est nécessaire de modifier les flex items, pas le conteneur

Une façon de le faire en ne mentionnant que le conteneur est d'utiliser le sélecteur d'enfants directs avec le sélecteur universel :
.conteneur {
    display: flex;
}

.conteneur > * {
    flex: 1;
}
En fonction de ce que vous voulez exprimer dans le CSS, ça peut être plus clair.

NB: justify-content:stretch a été introduit pour Grid, mais ne fait rien pour les boîtes flexibles, qui ont un mécanisme spécifique bien plus fin pour gérer leur étirement (la propriété flex)

Autre remarque

N'oubliez pas que seuls les fils directs d'un conteneur en display:flex sont des flex items, pas tous les descendants !

Exemple typique : on veut faire un menu horizontal et on met le nav en display:flex
<nav style="display:flex; background:gold;">
    <ul>
        <li>toto</li>
        <li>titi</li>
        <li>tata</li>
    </ul>
</nav>
Ça ne marche pas car seul le ul est un flex item (ce qui ne se voit pas puisque c'est le seul fils de son parent) !