Mar 07, 2016 CSS

Découverte de Flexbox

Les flexbox sont devenus de plus en plus populaires depuis quelques années et leur utilisation grandit de plus en plus. Mais de quoi s’agit-il ? Avec le CSS on a un modèle de boîtes (de type block ou inline) qui permet la mise en page. Vous avez sans doute l’habitude du positionnement flottant qui permet de jouer avec la position des blocs. Avec les flexbox les boîtes deviennent flexibles. On a un conteneur et des éléments, et le conteneur a la capacité de modifier les dimensions, positions et ordre des éléments pour qu’ils s’adaptent aux contraintes d’affichage. En fait exactement ce qui manque cruellement avec le modèle de boîtes classique, on peut même dire rigide par rapport à ce modèle flexible.

Les recommandations

Si on va faire un petit tour du côté du W3C on trouve cette recommandation :

img68

Ce n’est pas parce qu’il est marqué Level 1 que ça ne fait pas partie du CSS3. Chaque module a son propre niveau d’avancement.

C’est la référence la plus sérieuse du module et elle est plutôt bien rédigée.

Si on regarde maintenant la prise en charge par les navigateur avec le site Can I use on trouve ceci :

img69

On se rend compte qu’on peut désormais utiliser ce module sans trop de souci au niveau de la prise en charge si on exclut l’éternel mauvais élève IE (de nombreux bugs signalés).

D’autre part je vous conseille d’ouvrir l’onglet Resources où vous pouvez trouver des liens très intéressants :

img70

J’y ajoute le remarquable site Flexbox in 5 minutes qu’il faudrait créer s’il n’existait pas déjà !

J’aime bien aussi ce guide visuel.

Conteneur et éléments

Les premiers concepts à intégrer concernent :

  • le conteneur (flex container) : n’importe quelle balise HTML,
  • les éléments (flex items) : les enfants directs du conteneur.

Pour qu’une balise HTML deviennent un conteneur il faut lui ajouter l’une de ces deux règles :

  • display: flex (il se comporte globalement comme un block)
  • display: inline-flex (il se comporte globalement comme un inline-block)

Dans les deux cas rien ne change quant à la gestion des éléments.

Les balises enfants directs du conteneur deviennent automatiquement des flex items de même que du simple texte sans balise.

Prenons ce premier exemple :

<div style="display:flex">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>

img001

Il suffit d’une seule règle pour transformer une <div> en flex container et toutes les <div> enfants en flex items.

J’ai ajouté quelques règles pour visualiser les blocs, pour information les voici :

div[style*="display:flex"] {
    background-color: lightblue;
    border-style: solid;
    border-color: #C9A992;
}
div[style*="display:flex"] > * {
    background-color: lightyellow;
    border-style: solid;
    border-color: #B0732E;
    padding: 1em;
}

Maintenant qu’on sait comment créer un conteneur et des éléments on va se poser quelques questions.

Direction des éléments

On a vu ci-dessus que les éléments sont affichés horizontalement dans l’ordre du flux, c’est le comportement par défaut, la règle sous-entendue est flex-direction :

<div style="display:flex; flex-direction:row">

Mais on a d’autres possibilités, les voici toutes :

  • row (valeur par défaut)
  • row-reverse
  • column
  • column-reverse

Donc on peut afficher les élements en ligne dans un sens ou dans l’autre, ou en colonne dans un sens ou dans l’autre :

img002

Simple non ?

Wrapping

Voyons maintenant un autre aspect du comportement des éléments. Que se passe-t-il si on reduit la largeur de l’affichage en mode ligne (row) ?

Jusqu’à un certain point les éléments vont se tasser selon leur contenu :

img003

Et puis on va finir par perdre un morceau :

img004

J’ai dit au début de cet article que ce modèle de boîte était parfait pour adapter les éléments ! On va se servir d’une autre règle : flex-wrap.

Par défaut comme on vient de le voir rien n’est fait pour envoyer des éléments sur une autre ligne pour éviter de perdre un morceau. La règle flex-wrap peut prendre une de ces 3 valeurs :

  • nowrap (valeur par défaut)
  • wrap
  • wrap-reverse

Donc si on ne prévoit rien c’est comme si on écrivait :

<div style="display:flex; flex-wrap:nowrap">

Voyons ce que ça donne avec les deux autres valeurs :

img005

Avec cette règle on obtient un passage sur une autre ligne dès qu’il n’y a plus la place suffisante.

Notez qu’on a un règle qui cumule les deux précédentes (flex-direction et flex-wrap), il s’agit de flex-flow. On peut donc écrire :

<div style="display:flex; flex-flow:row wrap">

Et toutes les combinaisons qu’on veut…

Flexibilité

On va aborder maintenant la question importante de la flexibilité ! Il serait bien de demander aux éléments de prendre toute la place disponible et donc d’éviter ce qu’on a vu ci-dessus :

img71

L’espace vide à droite n’est pas très esthétique.

flex-grow

C’est là qu’intervient la propriété flex-grow qui peut augmenter dynamiquement la taille des éléments. Chaque élément est considéré séparément, ce n’est donc pas une propriété appliquée sur le conteneur mais sur chacun des éléments. La valeur représente le facteur de grossissement et va de 0 (aucun grossissement, valeur par défaut) à une valeur quelconque.

Voici un exemple :

<div style="display:flex">
    <div>Element</div>
    <div style="flex-grow:1">Autre élément</div>
    <div>Et un autre élément</div>
</div>

img006

Le deuxième élément s’est donc automatiquement élargi pour occuper tout l’espace libre.

Si on applique cette règle aux deux premiers éléments ils vont se répartir équitablement (puisqu’on applique le même facteur) la place libre :

<div style="flex-grow:1">Element</div>
<div style="flex-grow:1">Autre élément</div>
<div>Et un autre élément</div>

img007

Voici un exemple avec l’espace inégalement réparti :

<div style="flex-grow:0.5">Element</div>
<div style="flex-grow:1">Autre élément</div>
<div>Et un autre élément</div>

img008

Cette propriété n’est évidemment pas limitée à la largeur et dépend uniquement de la direction. Regardez cet exemple :

<div style="display:flex; flex-direction:column; height:300px">
    <div>Element</div>
    <div style="flex-grow:1">Autre élément</div>
    <div>Et un autre élément</div>
</div>

img013

flex-shrink

La propriété flex-shrink est l’inverse de la précédente, elle va réduire un élément pour éviter un débordement (overflow) du conteneur. La valeur par défaut est égale à 1, ce qui signifie que l’élément se réduit automatiquement. Une valeur de 0 signifie que l’élément ne peut pas se réduire.

Voici un exemple :

<div>Element</div>
<div style="flex-shrink:0">Autre élément</div>
<div>Et un autre élément</div>

img009

On voit que le deuxième élément ne se réduit pas alors que le troisième le fait (valeur à 1 par défaut).

Cette propriété comme la précédente dépend de la direction appliquée.

flex-basis

Cette propriété permet de définir la taille d’un élément avant l’application des deux propriétés qu’on a vues ci-dessus (flex-grow et flex-shrink). Les valeurs possibles sont :

  • auto (valeur par défaut)
  • content (dimension du contenu)
  • valeur quelconque (fonctionne comme width et height)

Regardez cet exemple :

<div>Element</div>
<div style="flex-basis:150px; flex-shrink:0">Autre élément</div>
<div>Et un autre élément</div>

img010

La largeur du deuxième élément est fixée de base à 150 pixels et on lui interdit de se réduire.

Voici un autre exemple :

<div>Element</div>
<div style="flex-basis:50%;flex-shrink:0">Autre élément</div>
<div style="flex-basis:40%;">Et un autre élément</div>

img011

Les deux derniers éléments ont leur largeur définie en pourcentage et le deuxième ne peut pas se réduire. Donc à la réduciotn on obtient ceci :

img012

L’élément central occupe imperturbablement 50% de l’espace, ce qui est sa base et qu’on lui interdit de se réduire. Par contre celui de droite n’arrive plus à occuper 40%, il peut se réduire.

flex

Il existe une syntaxe raccourcie pour les trois propriétés (flex-grow, flex-shrink et flex-basis), il s’agit de flex. Avec les valeurs suivantes :

  • initial (équivalent de flex: o 1 auto, donc les valeurs par défaut)
  • auto (équivalent de flex: 1 1 auto)
  • none (équivalent de flex: 0 0 auto)
  • valeurs précisées (par exemple flex: 0.5 2 content)

Le seul but est de raccourcir l’écriture du code et il est préconisé d’utiliser cette syntaxe.

Par exemple si on utilise auto :

<div style="display:flex;">
    <div style="flex:auto">Element</div>
    <div style="flex:auto">Autre élément</div>
    <div style="flex:auto">Et un autre élément</div>
</div>

img014

On a un comportement la plupart du temps adapté avec grossissement et rétrécissement automatique.

Alignement

Voyons maintenant comment aligner les éléments. On va évidemment s’en soucier si la flexibilité appliquée laisse encore de l’espace disponible et qu’il faut le répartir…

Direction principale : justify-content

La principale propriété est justify-content qui permet d’aligner le contenu du conteneur. Les valeurs possibles sont :

  • flex-start (valeur par défaut : tout s’aligne au début)
  • flex-end (tout s’aligne à la fin)
  • center (tout se centre)
  • space-between (l’espace est réparti équitablement mais pas au extrémités)
  • space-around (l’espace est réparti équitablement avec une moitié de valeur aux extrémités)

Le mieux est de visualiser tout ça avec notre exemple :

<div style="display:flex; justify-content:flex-start">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; justify-content:flex-end">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; justify-content:center">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; justify-content:space-between;">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; justify-content:space-around;">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>

img015

On aurait le même effet avec une direction verticale (en colonne).

Direction secondaire

align-items

On vient de voir l’alignement dans la direction principale (horizontale selon les lignes ou verticales selon les colonnes). Nous allons voir maintenant comment aligner dans la direction secondaire.

On utilise pour cela la propriété align-items avec ces valeurs possibles :

  • flex-start (valeur par défaut : tout s’aligne au début)
  • flex-end (tout s’aligne à la fin)
  • center (tout se centre)
  • baseline (alignement sur la ligne de base du texte, généralement comme flex-start)
  • stretch (les éléments s’étirent pour occuper tout l’espace)

Voici un exemple récapitulatif :

<div style="display:flex; align-items:flex-start; height:100px">
    <div style="flex:auto">Element</div>
    <div style="flex:auto">Autre élément</div>
    <div style="flex:auto">Et un autre élément</div>
</div>
<div style="display:flex; align-items:flex-end; height:100px">
    <div style="flex:auto">Element</div>
    <div style="flex:auto">Autre élément</div>
    <div style="flex:auto">Et un autre élément</div>
</div>
<div style="display:flex; align-items:center; height:100px">
    <div style="flex:auto">Element</div>
    <div style="flex:auto">Autre élément</div>
    <div style="flex:auto">Et un autre élément</div>
</div>
<div style="display:flex; align-items:baseline; height:100px">
    <div style="flex:auto">Element</div>
    <div style="flex:auto">Autre élément</div>
    <div style="flex:auto">Et un autre élément</div>
</div>
<div style="display:flex; align-items:stretch; height:100px">
    <div style="flex:auto">Element</div>
    <div style="flex:auto">Autre élément</div>
    <div style="flex:auto">Et un autre élément</div>
</div>

img016

align-self

L’alignement avec align-items s’appliquent à tous les éléments du conteneur. Si on veut surcharger cette règle pour un élément il faut utiliser la propriété align-self. On retrouve évidemment les mêmes valeurs possible avec en plus auto qui est la valeur par défaut et dit juste qu’on applique la règle générale.

Regardez cet exemple :

<div style="display:flex; align-items:flex-start; height:100px">
    <div style="flex:auto">Element</div>
    <div style="flex:auto; align-self:center">Autre élément</div>
    <div style="flex:auto; align-self:flex-end">Et un autre élément</div>
</div>

img017

La règle générale demande un alignement au début mais le second élément a sa propre règle pour se centrer et le dernier a sa propre règle pour s’aligner à la fin.

On a exactement le même comportement en horizontal si la direction principale est verticale (column).

Alignement des lignes : align-content

Maintenant intéressons nous au cas où on a plusieurs lignes dans un même conteneur, autrement dit lorsqu’on utilise flex-wrap. Si on a de l’espace à distribuer il faut définir la règle de distribution, dans ce cas on utilise la propriété align-content avec ces valeurs possibles :

  • flex-start (valeur par défaut : tout s’aligne au début)
  • flex-end (tout s’aligne à la fin)
  • center (tout se centre)
  • space-between (l’espace est réparti équitablement mais pas au extrémités)
  • space-around (l’espace est réparti équitablement avec une moitié de valeur aux extrémités)
  • stretch (les éléments s’étirent pour occuper tout l’espace)

Voici un exemple récapitulatif :

<div style="display:flex; flex-flow:row wrap; align-content:flex-start; height:140px">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; flex-flow:row wrap; align-content:flex-end; height:140px">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; flex-flow:row wrap; align-content:center; height:140px">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; flex-flow:row wrap; align-content:space-between; height:140px">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; flex-flow:row wrap; align-content:space-around; height:140px">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>
<div style="display:flex; flex-flow:row wrap; align-content:stretch; height:140px">
    <div>Element</div>
    <div>Autre élément</div>
    <div>Et un autre élément</div>
</div>

img018

On voit qu’on retrouve toujours les même principes…

Alignement avec auto

Lorsqu’on applique le classique auto pour les marges sur un élément flexbox on obtient des résultats intéressants parce qu’on agit à la fois horizontalement et verticalement.

Regardez ce premier exemple :

<div style="display:flex">
    <div>Element</div>
    <div>Autre élément</div>
    <div style="margin-left:auto">Et un autre élément</div>
</div>

img021

On pousse facilement le troisième élément à droite. On pourrait faire la même chose à gauche avec margin-left.

Là où ça devient encore plus intéressant c’est sur l’autre axe :

<div style="display:flex; flex-direction:column; height: 200px">
    <div>Element</div>
    <div>Autre élément</div>
    <div style="margin-top:auto">Et un autre élément</div>
</div>

img022

On pousse facilement le troisième élément en bas du conteneur.

Ordre des éléments

Une autre possibilité ets de pouvoir ordonner les éléments comme on veut avec la propriété order. La valeur par défaut est 0 et l’ordre va du plus petit nombre jusqu’au plus grand. Voici un exemple :

<div style="display:flex">
    <div style="order:2">Element</div>
    <div>Autre élément</div>
    <div style="order:1">Et un autre élément</div>
</div>

img019

Et ça fonctionne évidemment aussi en colonne :

<div style="display:flex; flex-direction:column">
    <div style="order:2">Element</div>
    <div>Autre élément</div>
    <div style="order:1">Et un autre élément</div>
</div>

img020

On peut imaginer modifier l’ordre de façon dynamique, ou en utilisant les medias queries.

Une grille ?

Si vous utilisez Bootstrap comme framework vous connaissez sa célèbre grille. Dans la version actuelle, la 3, cette grille est fondé sur du positionnement classique avec des éléments flottants.

La version 4 de Bootstrap en est seulement à la version alpha mais est déjà bien avancée. Quand on regarde le code concernant la grille on se rend compte qu’on peut la passer en mode flexbox. Il y a un impact sur :

  • la grille ou le float passe en display: flex
  • les entrées de formulaires où le display: table passe en display: flex
  • le composant media qui passe aussi en disply: flex

Par défaut flexbox sera désactivé mais il suffit de renseigner cette variable dans _variables.scss pour le rendre actif :

$enable-flex:               false !default;

On passe la valeur à true et on recompile !

On retrouve dans les fichiers concernés une condition pour lire la valeur de cette variable et agir en conséquence. par exemple voici le mixin pour créer une rangée :

@mixin make-row($gutter: $grid-gutter-width) {
  @if $enable-flex {
    display: flex;
    flex-wrap: wrap;
  } @else {
    @include clearfix();
  }
  margin-left:  ($gutter / -2);
  margin-right: ($gutter / -2);
}

Si on est en mode flexbox on retrouve ce qu’on a vu dans cet article, sinon on met en place un clearfix pour le fonctionnement correct du flottement.

C’est une évolution intéressante de ce framework, sans parler de toutes les autres avancées qui sortent du cadre de cet article !

Pour ceux qui préfèrent Foundation il est a noter qu’il y a aussi la possibilité de passer en flexbox pour certains composants.

Des exemples

Vous trouverez des exemples à la pelle d’utilisation de flexbox pour le voir en œuvre, parmi eux :

 

Laisser un commentaire