CSS : boites flexibles avec CSS3 ; display flex

Le positionnement des boites a toujours été en CSS le point le plus 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 avec 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 block 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" (un DIV par exemple).

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, regio plena gratiarum et venustatis, urbibus decorata magnis et pulchris; in quibus amoenitate celebritateque nominum Tyros excellit, Sidon et Berytus isdemque pares Emissa et Damascus saeculis condita priscis. </div> <div id="boite2"> <p>Et quoniam mirari posse quosdam peregrinos existimo haec lecturos forsitan, si contigerit, quamobrem cum oratio ad ea monstranda deflexerit quae Romae gererentur, nihil praeter seditiones narratur et tabernas et vilitates harum similis alias, summatim causas perstringam nusquam a veritate sponte propria digressurus. Hoc inmaturo interitu ipse quoque sui pertaesus excessit e vita aetatis nono anno atque vicensimo cum quadriennio imperasset. natus apud Tuscos in Massa Veternensi, patre Constantio Constantini fratre imperatoris, matreque Galla sorore Rufini et Cerealis, quos trabeae consulares nobilitarunt et praefecturae. </div> <div id="boite3"> <p>Nihil morati post haec militares avidi saepe turbarum adorti sunt Montium primum, qui divertebat in proximo, levi corpore senem atque morbosum, et hirsutis resticulis cruribus eius innexis divaricaturn sine spiramento ullo ad usque praetorium traxere praefecti. </div> </div> </body>

Attention notez que le texte de la deuxième boite est beaucoup plus long (pratiquement le double) que celui dans les 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 selon un axe horizontal (flex-direction : row).

Notez que ni pour le "flex-container" ni pour les "flex-items" je n'ai précisé les 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 largeur de chaque boite flexible est fonction de son contenu.
Donc la règle selon laquelle les boites "enfants" ont par défaut la largeur de leur "parent" n'est plus vrai si ce dernier devient un "flex-container".

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.
La feuille de style serait alors :

	#principale {display : flex;}
	...

Le reste sans changement.

Remarque 2

Pour montrer la simplicité de l'outil flexbox par rapport à la situation ancienne, je vous rappelle que dans le cadre d'un positionnement selon la technique "inline-block" la feuille de style serait alors (après maints tâtonnements) :

#principale {border:1px solid red;} #boite1 {background : lime ; } #boite2 {background : pink ; } #boite3 {background : coral ; } #boite1, #boite2, #boite3 {display : inline-block ; width : 30%; vertical-align : top; margin :1%; } p{margin : 10px ; font-size : 14pt; }

Je suis obligé de fixer une largeur aux "enfants" du conteneur "principale" sinon il font par défaut 100% (largeur du "parent").

Le code HTML serait identique.
Quand au rendu ... suivez le lien Observez le rendu !

Admettez que malgré un CSS beaucoup plus lourd le rendu est beaucoup moins joli puisque les "enfants" n'ont pas tous la même hauteur mais une hauteur adapté au contenu (height : auto). Et si l'on fixe une hauteur il y a alors risque de "overflow" (débordement) ...

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 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 les uns en dessous des autres. 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 !

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 monti Phoenice, regio plena gratiarum et venustatis, urbibus decorata magnis et pulchris; in quibus amoenitate celebritateque nominum Tyros excellit, Sidon et Berytus isdemque pares Emissa et Damascus saeculis condita priscis. </article> <article> <p>Et quoniam mirari posse quosdam peregrinos existimo haec lecturos forsitan, si contigerit, quamobrem cum oratio ad ea monstranda deflexerit quae Romae gererentur, nihil praeter seditiones narratur et tabernas et vilitates harum similis alias, summatim causas perstringam nusquam a veritate sponte propria digressurus. </article> <article> <p>Nihil morati post haec militares avidi saepe turbarum adorti sunt Montium primum, qui divertebat in proximo, levi corpore senem atque morbosum, et hirsutis resticulis cruribus eius innexis divaricaturn sine spiramento ullo ad usque praetorium traxere praefecti. </article> </section> <section> <h2>Section 2</h2> <article> <p>Post hanc adclinis Libano monti Phoenice, regio plena gratiarum et venustatis, urbibus decorata magnis et pulchris; in quibus amoenitate celebritateque nominum Tyros excellit, Sidon et Berytus isdemque pares Emissa et Damascus saeculis condita priscis. </article> <article> <p>Et quoniam mirari posse quosdam peregrinos existimo haec lecturos forsitan, si contigerit, quamobrem cum oratio ad ea monstranda deflexerit quae Romae gererentur, nihil praeter seditiones narratur et tabernas et vilitates harum similis alias, summatim causas perstringam nusquam a veritate sponte propria digressurus. </article> </section> <section> <h2>Section 3</h2> <article> <p>Post hanc adclinis Libano monti Phoenice, regio plena gratiarum et venustatis, urbibus decorata magnis et pulchris; in quibus amoenitate celebritateque nominum Tyros excellit, Sidon et Berytus isdemque pares Emissa et Damascus saeculis condita priscis. </article> <article> <p>Et quoniam mirari posse quosdam peregrinos existimo haec lecturos forsitan, si contigerit, quamobrem cum oratio ad ea monstranda deflexerit quae Romae gererentur, nihil praeter seditiones narratur et tabernas et vilitates harum similis alias, summatim causas perstringam nusquam a veritate sponte propria digressurus. </article> <article> <p>Nihil morati post haec militares avidi saepe turbarum adorti sunt Montium primum, qui divertebat in proximo, levi corpore senem atque morbosum, et hirsutis resticulis cruribus eius innexis divaricaturn sine spiramento ullo ad usque praetorium traxere praefecti. </article> </section> </body>

BODY contient n fois SECTION et SECTION contient n fois ARTICLE.

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 !

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

Revenons à la disposition sur un seul axe. Mais désormais nous souhaitons que les boites flexibles aient 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 rendu !

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

La propriété flex-direction 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

Vous savez qu'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 rendu !

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 ç'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 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 forcer le retour à la ligne !

Solution

Concernant le code HTML aucun changement. Par contre au niveau de la feuille de style et concernant 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