CSS : l'outil flexbox - boites flexibles avec CSS3

Le positionnement des boites a toujours été en programmation web un point délicat.
Les développeurs web ont longtemps utilisé les tableaux HTML pour positionner textes et images.
Puis le CSS a proposé différentes techniques de positionnement via la propriété POSITION pouvant prendre les valeurs : fixe, absolu, relatif.
Mais beaucoup de développeurs WEB utilisaient la propriété float pour positionner des éléments les boites alors que c'est un mode de positionnement très limité et qui était initialement prévu pour disposer un élément inline (une image par exemple) dans un élément "block".

Nous avons vu par ailleurs qu'en transformant des boites en éléments "inline-block" celles-ci se positionnaient de front (et non plus les unes en dessous des autres) mais avec par défaut un alignement par rapport à la base ...

Excellent nouvelle : CSS3 introduit l'outil flexbox (boites flexibles) qui vise à simplifier la mise en page.

Ce qui est remarquable c'est que tous les navigateurs récents (même Edge de MS) interprètent correctement les propriétés relatives aux boites flexibles sans même avoir besoin de préfixer celles-ci.
Si vous voulez un site qui s'affiche correctement sur tous les écrans (de l'écran du PC de bureau à celui d'un smartphone) utilisez flexbox !!!

Distribution des boites selon un axe

Les boites flexibles peuvent être disposées selon un axe horizontal (par défaut) ou selon un axe vertical).

Distribution selon un axe horizontal

Le code HTML

... <div id ="principale"> <div id="boite1"> <p>Post hanc adclinis Libano monti Phoenice, ... </div> <div id="boite2"> <p>Et quoniam mirari posse ... </div> <div id="boite3"> <p>Nihil morati post haec militares ... </div> </div> </body>

Pour info le texte de la deuxième boite est beaucoup plus important (pratiquement le double) que celui de chacun des deux autres blocs.

Le code CSS

	#principale { display : flex;   flex-direction : row;}
	div#boite1 {background : lime ; }
	div#boite2 {background : pink ; }
	div#boite3 {background : coral ; }
	...

La grande nouveauté est la première règle de style : #principale { display : flex; flex-direction : row;}.
Ce qui veut dire que l'élément identifié principale est le conteneur de boites flexibles (display : flex) et que celles-ci sont disposées sur une ligne d'où flex-direction : row.

Notez que pour les "flex-items" je n'ai à préciser des dimensions ... C'est CSS qui gère ! Observez le rendu !

C'est magique ! Au lieu d'être les unes en dessous des autres les trois boites sont côte à côte.
Les 3 blocs ont la même hauteur. Par contre la largeur de chaque boite flexible est fonction de son contenu.
La largeur du flex-container" n'étant pas précisée, elle est égale à celle de son parent (BODY).

Réduisez la largeur de la fenêtre ; la hauteur commune des trois boite est ajustée automatiquement ; il n'y a jamais débordement du texte !

Remarque 1

J'aurai pu écrire le code CSS plus simplement. En effet la valeur par défaut de la propriété flex-direction est "row" c'est à dire que par défaut les boites flexibles sont disposées côte à côte (sur une seule ligne).
La feuille de style serait alors tout simplement :

	#principale {display : flex;}
	...

Le reste sans changement.

Distribution selon un axe vertical

Le code de la page

Le code HTML est strictement le même que celui de l'exemple précédent.

Dans la feuille de style seule la première règle change !

	#principale {display : flex; flex-direction : column;}

Seule différence avec l'exemple précédent : flex-direction:column.
Ce qui signifie que les blocs flexibles ne sont pas côte à côte mais disposés sur une colonne d'ou le flex-direction:column.
Observez le rendu !

Les trois blocs ont la même largeur (celle du conteneur); la hauteur de chaque boite est ajustée automatiquement à son contenu.
Réduisez la largeur de la fenêtre d'affichage ; la hauteur de chaque boite s'ajuste automatiquement !

Marges entre boites

Vous ne souhaitez pas que les boites se collent.
Il suffit d'introduire des marges externes pour chaque "flex-items".
La feuille de style devient (extrait) :

	...
	div#boite1 {background : lime ; margin : 10px ; }
	div#boite2 {background : pink ; margin : 10px ; }
	div#boite3 {background : coral ; margin : 10px ; }
	...

Observez le rendu !

Distribution selon deux axes

Examinons maintenant la disposition des boites flexibles selon deux axes car les "flex-items" sont à leur tour des conteneurs de sous boites flexibles.
Il y a donc une disposition selon un axe principal (horizontal ou vertical) puis une disposition selon un axe secondaire.

Le code HTML

<body> <h1>Boites flexibles disposées verticalement selon l'axe principal et horizontalement selon l'axe secondaire</h1> <section> <h2>Section 1</h2> <article> <p>Post hanc adclinis Libano ... </article> <article> <p>Et quoniam mirari posse ... </article> <article> <p>Nihil morati post haec militares avidi ... </article> </section> <section> <h2>Section 2</h2> <article> <p>Post hanc adclinis Libano monti Phoenice, ... </article> <article> </article> </section> <section> <h2>Section 3</h2> <article> <p>Post hanc adclinis Libano monti Phoenice, regio plena gratiarum et venustatis, urbibus decorata ... </article> <article> <p>Et quoniam mirari posse quosdam peregrinos existimo haec lecturos ... </article> <article> <p>Nihil morati post haec militares avidi saepe turbarum adorti sunt Montium ... </article> </section> </body>

La page contient n sections et chaque section contient n articles.

Le CSS pour gérer la mise en page

	body { width : 900px ; margin : auto ; display : flex;  flex-direction : column;}
	section {display : flex ; flex-direction : row ; border : 2px solid black; margin : 20px;}
	article {border : 1px solid black; margin : 10px;}

Deux axes : un axe principal orienté "column" car défini au niveau de BODY et un axe secondaire orienté "row" car défini au niveau de SECTION.
Notez les marges. La propriété margin est parfaitement compatible avec display : flex ! Observez le rendu de la distribution selon deux axes

La balise H1 est traitée comme les boites SECTION (disposée verticalement).
Les balises H2 sont à l'intérieur de SECTION donc elles sont traitées comme les boites ARTICLE : des "flex-items" disposés horizontalement.

Les boites doivent avoir la même largeur

Jusqu'à présent dans le cadre d'une distribution "row" c'est le navigateur qui détermine la largeur de chaque "flex-item" en fonction de son contenu.
Imaginons que les boites flexibles doivent avoir toutes la même largeur : 30% de celle du conteneur.

Première solution

Structure de la page :

<div id ="principale"> <div id ="boite1"> <p>Post ... </div> <div id="boite2"> <p>Et quoniam mirari ... </div> <div id ="boite3"> <p>Nihil morati ... </div>

La feuille de style interne :

	#principale {display : flex;  flex-direction : row;}
	div#boite1 {background : lime ; }
	div#boite2 {background : pink ; }
	div#boite3 {background : coral ; }
	#boite1, #boite2, #boite3 {width : 30%;}

Il y a un nouvelle règle de style supplémentaire : celle qui fixe la largeur des trois "flex-items" à 30%. Observez le rendu !

C'est pas très joli car les boites sont calées à gauche ; une répartition équilibrée des blocs serait préférable.
Notez que toutes les boites ont la même hauteur.

Bonne solution

Il suffit de modifier la première règle de style qui devient :

	#principale {display : flex; justify-content : space-between;}

Rajout d'une propriété : justify-content : space-between
flex-direction n'est plus mentionné puisque par défaut la valeur est "row". Observez le résultat !

Les trois blocs sont alors répartis sur toute l'étendue de l'axe principal (marges entre les blocs mais pas aux extrémités).
C'est CSS qui gère la taille des marges !

Effets graphiques

Rappel : vous savez pour le moment que la propriété flex-direction peut prendre deux valeurs : row (valeur par défaut)/ column.
Sachez qu'elle peut prendre deux autres valeurs : row-reverse et column-reverse c'est à dire dans l'ordre inverse que celui indiqué par le code HTML).

Dans l'exemple ci-dessous dès que vous cliquez dans le conteneur l'ordre des boites est inversé par rapport à la situation initiale.

Structure de la page

<div id ="principale"> <div id="bleu"> <h4>Boite bleu</h4> <p>Post hanc adclinis ... </div> <div id="blanc"> <h4>Boite blanche</h4> <p>Et quon iam mirari ... </div> <div id="rouge"> <h4>Boite rouge</h4> <p>Nihil morati ... </div> </div>

Le code CSS

	#principale {display : flex; justify-content : space-around; border : 1px solid red;}
	#principale:active {flex-direction : row-reverse;}
	div#bleu{background : blue ; }
	div#blanc {background : white ; }
	div#rouge {background : red ; }
	#bleu, #blanc, #rouge {width : 30%;}

Je ne précise pas la direction de l'axe donc par défaut c'est horizontal.
Lorsque je clique sur le "flex-container" flex-direction devient "row-reverse".

Remarquez que j'ai utilisé la propriété justify-content avec une autre valeur : space-around (marges entre blocs mais aussi aux extrémités).
Observez le rendu !

Par défaut : drapeau national (bleu, blanc, rouge).
Dès que vous cliquez dans le conteneur "principale" l'ordre des boites est inversé par rapport à la situation initiale.

Le centrage d'une boite flexible dans son conteneur

En CSS2 le centrage horizontal et vertical d'une boite dans son conteneur était un vrai casse-tête.
Avec l'outil flexbox le centrage d'une boite dans son conteneur devient un jeu d'enfant.

Le code de l'exemple

... <style> #parent{ display : flex; justify-content : center ; align-items : center; border : 1px solid red; height : 600px;} #enfant {background : pink ; width : 60% ; } div p{margin : 10px ; font-size : 14pt; text-align : justify; } ... <body> ... <div id ="parent"> <div id ="enfant"> <p>Post hanc adclinis Libano monti Phoenice, ... </div> </div> ...

La propriété justify-content gère l'alignement selon l'axe principal et la propriété align-items gère l'alignement selon l'axe secondaire.
Donc pour centrer selon les deux axes il suffit que ces deux propriétés aient la valeur : center. Observez le résultat !

Variante

Souvenez vous de l'époque où l'on vous disait que margin-left / margin-right : auto ça marchait. Par contre margin-top / margin-bottom : auto ça marchait pas ...

Et bien avec l'outil flexbox c'est résolu !

Reprenons l'exemple précédent. Le code HTML est inchangé. Par contre simplifions le CSS !
La feuille de style devient :

	#parent{ display : flex; flex-direction : row; 	border : 1px solid red; height : 600px ; }
	#enfant {background : pink ; width : 60%;  margin :auto; } 
...

Les propriétés justify-content & align-items disparaissent du sélecteur #parent.
Par contre margin : auto apparait au niveau du sélecteur #enfant.
Donc superbe nouvelle : margin /left /right / top / bottom : auto ça marche dans le cadre du positionnement flexible. Observez le rendu de la variante !

Gérer l'alignement

Revenons sur deux propriétés : justify-content et align-items.

Je rappelle que la propriété justify-content gère l'alignement selon l'axe principal et la propriété align-items gère l'alignement selon l'axe secondaire.

justify-content

align-items

Exemple

Le conteneur fait 600px de hauteur et les trois blocs flexibles doivent être disposés verticalement avec des espaces identiques entre chaque bloc ainsi qu'aux extrémités.

La feuille de style :

	#principale {display : flex;  flex-direction : column ; justify-content : space-around; 
		border :1px solid black; height:600px;}
	div#boite1 {background : lime ; }
	div#boite2 {background : pink ; }
	div#boite3 {background : coral ; }

Réduisez la largeur de la fenêtre ; les espacements entre les boites se réduisent jusqu'à disparaitre. Le rendu !

Positionner une boite flexible dans un coin

Nous voulons qu'une boite soit positionnée en haut et à droite dans son conteneur.

Code CSS

	#parent{ display : flex;   flex-direction : column; justify-content : flex-start ; align-items : flex-end; 
			border : 1px solid red; height : 600px ; }
	#enfant {background : pink ; width : 60% ; margin:10px ;  } 
	

Le Code HTML est identique à celui de l'exemple précédent ! Le rendu !

La boite flexible est bien positionnée en haut et à droite !
C'est parfaitement logique puisque l'axe principal est vertical (flex-direction : column) et que justify-content : flex-start alors que align-items : flex-end donc à la fin sur l'axe horizontal (axe secondaire ici).

Pour que la boite soit positionnée en bas et à gauche dans son conteneur ; quels seraient les règlages ?

Centrer plusieurs boites flexibles

Inutile de se prendre la tête !

Code CSS

...
	#parent{ display : flex;   flex-direction : column; justify-content : center ; align-items : center; 
			border : 1px solid red; height : 600px ; }
	.enfant {background : pink ; width : 80% ; margin :10px; } 

Code HTML

<div id ="parent"> <div class ="enfant"> <p>Post hanc adclinis Libano monti Phoenice, ... </div> <div class ="enfant"> <p>Post hanc adclinis Libano monti Phoenice, ... <p>Post hanc adclinis Libano monti Phoenice, ... </div> </div> ...

Les deux boites sont positionnées selon un axe vertical (flex-direction : column).
La hauteur de chaque boite est ajustée automatiquement en fonction du contenu et de la largeur imposée (80% de celle du conteneur).
Le rendu !

Astuce : les deux blocs ne se touchent pas grâce à la règle margin : 10px associée à la classe enfant.

Retour à la ligne

Problématique

Soit le code suivant (extrait) :

<style> #principale {display : flex; flex-direction : row ; border :1px solid black;} .enfant {width : 30%; margin :1% ; background : yellow;} ... <div id ="principale"> <div class ="enfant"> <p>Post hanc ... </div> ... <div class ="enfant"> <p>Nihil morati post ... </div> </div> ...

Le conteneur identifié "principale" contient 5 blocs de classe "enfant".
Chaque bloc a une largeur de 30% de celle du conteneur. Le rendu !

Il y a un gros problème : les boites "enfant" ne font pas 30% de large.
Pour que les 5 blocs tiennent sur un seul axe horizontal leur largeur a été automatiquement réduite.

Il faut donc forcer un saut ligne !

Solution

Concernant le code HTML aucun changement. Par contre au niveau de la feuille de style et pour le conteneur de boites flexibles, une nouvelle propriété apparait !

	#principale {display : flex;  flex-direction : row ; flex-wrap : wrap ; border :1px solid black;}
	.enfant {width : 30%; background : yellow; margin : 1%;}

J'ai ajouté : flex-wrap : wrap Le rendu !

Les cinq blocs sont désormais sur deux axes horizontaux et font bien 30% de large.

Pour créer un saut de ligne (ou saut de colonne si flex-direction : column) il faut ajouter : flex-wrap : wrap au "flex-container" !
Retour menu