Le modèle CSS3 flex-box

13/07/2014

Que ce soit sur téléphone, tablette ou bureau, un des casse-têtes quotidiens des intégrateurs HTML est la position des boites et leur étirement. En gros, on cherche toujours à ce qu’une image soit positionné à coté d’un bloc de texte, le tout dans un conteneur qui s’étire de la même manière que le reste… basé sur le contenu de ce qui l’entoure. En bref, l’arrachage de cheveux dans les règles. Mais en fait, il existe une série de directives CSS3 qui soulage fortement le monteur, bien que mal connue, le système de “flex”.

Flex, comme flexible, étirable. A ne pas confondre avec Flex pour Flash. Ainsi, au lieu de parler de “flex” qui, dans Google, remonte des articles parlant de cette techno Flash, il vaut mieux parler de “flex-boxes CSS3”.

Ce qu’il y a de sympa avec ces directives, c’est qu’elle marche presque partout. De IE à Chrome en passant par Firefox… ça se passe pas mal. Bon ok, souvent il faudra préfixer la directive pour les version un peu anciennes et permettre d’avoir le comportement attendu. Si vous utilisez Less ou Sass, ça ira très vite.

Je ne vais pas vous faire un cours magistral sur toutes les possibilités des flex boxes, mais vous montrer deux ou trois exemples qui m’ont permis de monter une interface HTML qui tourne sur pratiquement toutes les plateformes que je cible. A savoir, une interface simple sur Android, Firefox et Chrome. Je n’ai pas testé sur IE, mais je pense que ça se passera bien.

Avant tout, c’est quoi le système de flex ?

Des boites élastiques

Les flex boxes permettent de faire en sorte qu’un conteneur (div, main, body…) s’étire et place son contenu en fonction du reste. Pour que votre boite gère le principe flexible, il suffit de donner donner la valeur “flex” à la directive “display”. Ca donne ça:

div {
    display: flex;
}

Me dites pas que c’était compliqué ! Bon ok, là on a fait que le début.

Ensuit va venir les attributs de flexibilité, on va permettre à une boite de gérer son contenu en colonne, en ligne, de se placer avant ou après le reste, et surtout de savoir combien de place par rapport au reste elle va prendre.

Les directives:

  • display: flex; avec des prefix -ms -webkit -moz… pour les vieux navigateurs, ça se passera bien.
  • flex: G S B; G et S => flex-grow (entier), S => flex-shrink (entier) et B => flex-basis (auto, ou taille), par défaut la valeur est “0 1 auto”
  • flex-direction: column|row|row-reverse…; y’en a plein
  • order: N; Ordre d’affichage. Permet de placer une boite avant un autre, sans bouger le DOM (ça c’est cool hein)

Pour clarifier:

  • flex-grow: force l’espacement par rapport aux autres élément dans le conteneur. Si vous avez 3 boites, et que vous donner la valeur 1 à deux boites, et 3 à une boite, cette dernière sera 3 fois plus grande que les deux autres…
  • flex-shrink: par défaut à 1, permet de réduire la taille de la boite si nécessaire
  • flex-basis: défini la taille avant que l’espacement soit distribué aux boites. “auto” par défaut.

Ça peut paraître un peu compliqué comme ça, mais en fait c’est super simple… surtout quand on le met en pratique. Et on va le faire :)

Mise en pratique: le layout cardinal

Le layout cardinal est simple. On veut 5 boites:

  • une entête (bannière, titre…)
  • 3 colonnes pour la navigation, le contenu principal et une barre d’info à droite
  • un footer.

Le DOM va être bêtement:

<header>HEADER</header>
<div id="content">
    <main>MAIN CONTENT</main>
    <nav>NAV LEFT</nav>
    <aside>ASIDE RIGHT</aside>
</div>
<footer>FOOTER</footer>

Vous remarquez que j’ai placé le bloc “main” avant la navigation et le bloc aside, alors qu’on veut l’avoir visuellement après la navigation. On va gérer ça facilement avec la directive “order”.

Ce qu’on va faire, pour commencer, c’est définir que nous gérons le mode “flex” pour tout nos blocs définis. Allez c’est pas compliqué (et je le fais en pur CSS):

header, #content, footer {
    display: -webkit-box;
    display: -moz-box;
    display: -ms-flexbox;
    display: -webkit-flex;
    display: flex;
}

Ca c’est fait… Ca donne ça:

Bon j’ai ajouté de la couleur et une marge pour qu’on se rendre mieux compte des boites.

On remarque donc, d’entrée de jeu, que les boites contenu “#content” se trouvent en ligne, côte à côte, mais jusque là, rien d’impressionnant. On pourrait le faire à base de float ou de “inline-block”. Voyons la suite.

Ce qu’on doit faire, maintenant, c’est forcer le fait que le bloc “main” s’étire. On ajoute simplement “flex: 1”:

main {
    flex: 1
}

Et ça donne:

Bon, pas mal :), le bloc main est bien étiré, quelque soit le padding, le margin des conteneurs, tout se cale correctement et c’est déjà un grand pas !

Mais je sais ce que vous allez me dire… oui, ok mais si on met du contenu dans “main”, ça étire pas les bloc de navigation et aside dans la hauteur. La preuve que si:

Avouez le, ça vous éclate :) Et on a presque pas codé de CSS, on a pas une ligne de JS.

Reste le dernier point, le truc qui fâche, comment faire pour que la taille totale colle à 100% de la hauteur de la page ? Ou d’un conteneur qui l’englobe.

Et bien c’est super simple. On va juste forcer le fait que “body” soit un model de boite, on lui donne une taille de 100% en hauteur. Ensuite on demande à ce que “#content” fasse aussi 100% de la hauteur. Là où ça devient intéressant, c’est que le modèle de boite se débrouille pour que “100%” ne soit pas destructif. Il calcule son espace par rapport aux autres boites. Et du coup, ça colle parfaitement, quelque soit la taille de la fenêtre, quelque soit le contenu…

Comme on fait des lignes (header au dessus de #content qui est au dessus de footer), le body est une grosse colonne.

On va donc faire ça (j’ai viré les valeurs préfixées sinon ça fait trop de choses à lire):

/* On force la taille de la page... et on modèle de boite global */
body, html {
    display: flex;
    flex-direction: column;
    height: 100%
}

/* et on s'arrange pour que le contenu (ligne 2) prenne toute la place */
#content {
    height: 100%
}

J’ai ajouté aussi un overflox pour la balise “main” car le texte peut déborder si vous réduisez la fenêtre. Bref, sans trop forcer, j’arrive à ça:

Notez bien que j’ai enlevé des paragraphes pour vous prouver que la taille des blocs s’étirent en hauteur.

Aucun calcul, pas de JS, tout s’automatise… si c’est pas génial ça !

Et le responsive dans tout ça ?

Ce qui est sidérant, c’est que ce modèle de boite permet de faire des trucs vraiment puissant. Et quand on parle de responsive design on se rend compte que tout est là pour nous rendre la vie douce est mélodieuse.

Reprenons, on a “aside”, “main” et “nav” cote à cote. Mais quand on va descendre en résolution, on va avoir bobo… Il suffit simplement de dire à notre modèle qu’en dessous de 600px de largeur, notre bloc “#content” est un modèle en colonnes. Comment ? simplement:

@media all and (max-width: 600px) {
    #content {
        flex-flow: column
    }
}

Mais en plus, pour l’exemple, je veux que le bloc “nav” apparaisse après le bloc main. Avant on devait se taper un JS terrible de déplacement d’objet dans le DOM. Avec CSS et le modèle de boite… comment dire… c’est d’un con…

@media all and (max-width: 600px) {
    #content {
        flex-flow: column
    }

    nav, aside {
        order: 2
    }
    main {
        order: 1
    }
}

Et là, la magie:

Je vous laisse tenter le coup, ouvrez cet exemple: https://examples.metal3d.org/flex/index4.html et amusez vous à redimensionner la fenêtre. Vous allez voir les boites bouger en fonction de la taille du navigateur. C’est tout simplement pratique, facile, clair et efficace.

Alors, pourquoi personne n’y pense ?

Honnêtement, je ne sais pas pourquoi la communication est mal passée sur ce principe de boite. Je l’ai découvert il y a peu de temps et j’ai l’impression qu’on est encore peu nombreux à avoir analysé et testé ce modèle CSS.

Pourtant, il répond pratiquement à tous les soucis existants. Il permet de caler parfaitement du contenu en bas de bloc (par exemple un bloc de produit donc le prix doit apparaître en bas de div), de faire des colonne flexibles, de résoudre des problèmes de largeur et de hauteurs. Ca marche bien sur mobile et les prefix vendor sont apparemment présents de puis des années…

Aujourd’hui, le mode boite est validée, et je suis persuadé qu’on va en entendre parler, et surtout qu’il va être fortement utilisé par la suite. Fini les table-cell et autre calc() CSS, terminé les padding avec des bloc flottants là où on en a pas besoin. Le modèle flexbox va fleurir.

Je ne vous ai montré qu’un infime partie de ce que permet ce modèle, je vous conseille d’aller voir deux trois pages que j’ai trouvé intéressantes:

Voilà pour aujourd’hui :)

Ça peut vous intéresser aussi


CSS transformations transitions animations

Les animations dans une page web, Dieu sait à quel ...


Quand le style tue le style

A force de voir un background, des arrondis, etc… on ...


CSS 3 et tant pis pour les autres

Un truc me titillent les neurones depuis quelques temps, et ...


CSS bottom div

On m’a posé la question de savoir comment poser ...

Merci de m'aider à financer mes services

Si vous avez apprécié cet article, je vous serai reconnaissant de m'aider à me payer une petite bière :)

Si vous voulez en savoir plus sur l'utilisation de flattr sur mon blog, lisez cette page: Ayez pitié de moi

Commentaires

Ajouter un commentaire

Ajouter un commentaire

(*) Votre e-mail ne sera ni revendu, ni rendu public, ni utilisé pour vous proposer des mails commerciaux. Il n'est utilisé que pour vous contacter en cas de souci avec le contenu du commentaire, ou pour vous prévenir d'un nouveau commentaire si vous avez coché la case prévue à cet effet.