CSS3 : révolution dans le positionnement avec les outils grid-layout et flexbox

Préambule

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 deux nouvelles spécifications : flexbox & grid-layout.
L'outil flexbox (boites flexibles) est déjà bien implémenté sur les navigateurs.
Par contre l'outil grid-layout (positionnement en grille) n'est pas intégré dans un navigateur ; devinez lequel ...

Ces deux nouveaux outils se complètent parfaitement.
L'outi "grid-layout" permet de construire la structure générale de la page alors que l'outil "flexbox" n'est vraiment à l'aise que dans une seule direction ...
Pour prendre une exemple l'outil "grid-layout" sera utilisé pour disposer les grandes divisions de la page (les boites header, nav, article, aside, footer) tandis que l'outil "flexbox" permettra de disposer les différents DIV contenus dans la division ARTICLE, par exemple.

Le positionnement en grille : outil grid-layout

Je débute par l'outil "grid-layout" (positionnement en grille).
Attention cette spécification du W3C n'est pas encore bien implémentée chez certains navigateurs.

Premier exemple : une grille avec 6 cellules

Le code HTML

Aucune difficulté : 6 boites affectées de la même classe : "boite".

Le code CSS : 3 colonnes et 2 lignes

	body {
		min-height : 100vh; border : 1px solid red; width : 90%; margin :auto;
		display : grid;
		/* trois colonnes et 2 lignes donc 6 zones */
		grid-template-columns : 33% 33% 33%;
		grid-template-rows : 50% 50%;
		}
	.boite {border : 1px solid black; margin : 10px; }
	h3 {text-align : center; }

Dans le cadre d'un responsive basique la page a une largeur de 90% de celle de la fenêtre.
La page a une hauteur minimale égale à la hauteur de l'écran (min-height : 100vh). En effet "vh" signifie "viewport height" donc la hauteur minimale de la page est égale à 100% de la hauteur du viewport.

Observez le rendu

Le premier DIV occupe implicitement la première cellule, le deuxième DIV la deuxième cellule et ainsi de suite.

Le code CSS : deux colonnes et trois lignes

6 cases (areas) ça peut être aussi trois lignes et deux colonnes.
Pour le code HTML : aucun changement !
Concernant la feuille de style seules deux propriétés de la règle BODY changent.

body {
		...
		/* trois lignes et 2 colonnes donc 6 zones */
		grid-template-columns : 50% 50%;
		grid-template-rows :33% 33% 33% ;
		}
...

Seules changent les valeurs des propriétés grid-template-columns & grid-template-rows
Observez le rendu

Organiser la structure générale de la page avec "display grid"

Donc le code HTM est simple :

Quatre boites seulement !

Nous devons créer une grille de 2 colonnes et 3 lignes donc 6 cellules alors que nous n'avons que quatre divisions ...
Il n'est plus question d'une affectation implicite des boites aux cellules. L'affectation doit être explicite !
Pour l'affectation explicite des divisions aux différentes cellules de la grille nous avons trois solutions !

Première solution : fusion de cellules

Pour chaque division on précise la première cellule d'affectation et l'éventuelle fusion de cellules.

body {min-height : 100vh; width : 90%; margin :auto;
		display : grid ;
		grid-template-rows: 100px 80% 1fr;
		grid-template-columns: 30% 70%;  
		}
		
	header { grid-row: 1;grid-column: 1  / span 2; }
	nav {grid-row: 2;grid-column: 1;}
	article {grid-row: 2; grid-column: 2;}
	footer { grid-row: 3; grid-column: 1  / span 2;}

header, nav, article, footer {border : 1px solid black ; margin : 5px; }
h3 {text-align : center; }

On définit d'abord une grille de trois lignes et deux colonnes donc 6 cellules.
Notez l'une des valeurs de la propriété grid-template-rows : "1fr" qui signifie "totalité de l'espace disponible".
Il semble que le navigateur Firefox ne sache pas encore interpréter cette nouvelle unité de mesure que représente "fr".

Observez le rendu

La valeur "span ..." a été ici associée à la propriété grid-column donc on fusionne (sur une même ligne) des colonnes.
Mais on peut aussi fusionner sur une même colonne des lignes ; il suffit d'associer la valeur "span ..." à la propriétégrid-rows.

Deuxième solution : préciser les bornes

Pour chaque boite on précise les bornes de ligne et de colonne.
La grille comprend 2 colonnes et trois lignes. Donc les bornes de colonnes sont 1,2,3 (et non pas 0,1,2) et les bornes de lignes sont 1,2,3,4.
Ainsi la première ligne est entre les bornes de ligne 1 et 2 ; la dernière ligne est entre les bornes de ligne 3 et 4 ;
La première colonne est entre les bornes de colonne 1 et 2 ; la deuxième colonne est entre les bornes de colonnes : 2 et 3.
Extrait de la feuille de style :

...
	header {grid-row: 1 / 2 ; grid-column : 1 / 3 ; }
	nav {grid-row: 2 / 3 ; grid-column : 1 / 2 ;}
	article {grid-row: 2 /3 ; grid-column: 2 / 3 ; }
	footer {grid-row: 3 / 4; grid-column: 1 / 3; }
...

Le reste de la feuille de style est inchangé.

Pour chaque sélecteur il y a deux propriétés (grid-row & grid-colunm) et pour chaque propriété il y a deux valeurs séparées par une "/". La première valeur est la borne début et la deuxième valeur est la borne fin.
Ainsi il faut lire pour HEADER : bornes de ligne de 1 à 2 (donc première ligne) et bornes de colonne de 1 à 3 (donc deux colonnes).
Observez le rendu

Troisième solution : nommer les cellules

On nomme chaque cellule de la grille. Plusieurs cellules peuvent le même nom. Puis on affecte à chaque division de la page un nom de cellule.

C'est la technique que je préfère car elle me parait la plus claire au niveau du code CSS.
Extrait de la feuille de style :

body {...
		...
		 grid-template-areas: "h h"  "n a"   "f f";
 }

header {grid-area: h;}
nav {grid-area: n;}
article {grid-area:a;}
footer {grid-area:f;}
...

Apparition de la propriété grid-template-areas qui nomme les six cellules de la grille.
Les deux cellules de la première ligne se nomment "h" ; les cellules de la deuxième ligne se nomment respectivement "n" et "a" ; les deux cellules de la troisième ligne se nomment "f" ;

Apparition de la propriété grid-area qui affecte un nom de cellule à chaque division :

Les propriétés grid-row, grid-column disparaissent et sont remplacées par grid-area
Observez le rendu

Comme vous avez pu l'observer ces trois solutions donnent exactement le même rendu et c'est heureux.
Donc à vous ce choisir la méthode qui vous convient.

L'outil grid-layout et le responsive design

En combinant les media queries et le positionnement en grille on peut obtenir un site responsive c'est à dire qui s'adapte à tous les écrans : de l'écran géant d'un ordinateur de bureau au minuscule écran d'un "ordiphone" (comme disent les québecois).

Structure générale de chaque page d'un site

Sur un petit écran la boite ASIDE est masquée ; chacune des quatre autres boites occupent toute la largeur de l'écran.

Le code de la partie BODY

Il est totalement indépendant de la taille de l'écran.

N'oubliez pas la balise meta viewport dans la partie HEAD!

La feuille de style

body {
  min-height : 100vh; width : 800px; margin :auto;
  display: grid;
  grid-template-columns: 25% 55% 20%; 
  grid-template-rows: 100px 100px 80% 100px;
  grid-template-areas: "h h h"   "n n n "  "a a s"  "f f f";
 }
nav {grid-area: n;}
article {grid-area: a;}
aside {grid-area: s; }
footer {grid-area: f;}

header, nav, article, aside, footer {border : 1px solid black ; margin : 5px; }

h3 {text-align : center; }

@media (max-width: 800px) 
{
  body { 
	width : 100%; 
    grid-template-areas: "h h h "   "n n n"   "a a a"   "f f f"; 
  }
aside {display : none; }
} 

Notez le test introduit par une requête de média : si la largeur de l'écran ne dépasse pas 800 pixels alors la largeur de la page est égale à celle de la fenêtre et chaque division occupe une ligne de la grille sinon la page fait 800 pixels de large et les divisions NAV et ASIDE se partage la troisième ligne de la grille.

Admettez que c'est beaucoup plus simple qu'avec les méthodes traditionnelles.
Observez le rendu sur un PC puis réduisez la fenêtre pour simuler l'affichage sur un petit écran

L'outil flex : les boites flexibles

Dans ce premier exemple les grandes divisions de la page (header, nav, article, footer) sont disposées grâce à l'outil grid-layout. Par contre les boites enfants de la division ARTICLE sont organisées sur une ligne avec l'outil flexbox.

Le code de la partie BODY

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

La feuille de style

body {min-height : 100vh; width : 100%; 
	display : grid ;
		grid-template-rows: 100px 500px 100px ; 
		grid-template-columns: 25% 75%; 
		 grid-template-areas: "h h"  "n a"  "f f";
 }

header {grid-area: h;}
nav {grid-area: n;}
article {grid-area:a; display : flex ; flex-direction :row ; }
footer {grid-area:f;}

header, nav, article, footer {border : 1px solid black ; margin : 5px; }
h3 {text-align : center; }
p{margin : 10px ; font-size : 14pt; text-align : left; }
.sousboite {background : pink ; margin : 2px; overflow : auto; }

La nouveauté est dans la règle de style relative au sélecteur ARTICLE: article {... ; display : flex ; flex-direction :row ;
Ce qui veut dire que l'élément ARTICLE est le conteneur de boites flexibles (ou "flex-container") et que celles-ci sont disposées sur une ligne d'où flex-direction : row.

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

Les trois boites flexibles sont côte à côte.
Ces 3 blocs ont la même hauteur. Par contre la largeur de chaque boite flexible est automatiquement fonction de son contenu.

Distribution selon un axe vertical

Les items de ARTICLE doivent être sur une colone (les unes en dessous des autres).

Le code de la partie BODY : inchangé.

La feuille de style modifiée : un seul changement au niveau de la règle relative au sélecteur ARTICLE : article {... display : flex ; flex-direction :column ; }. La valeur de la propriété flex-direction passe de "row" à "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 en fonction de la nouvelle largeur de l'écran.

Distribution selon un axe vertical - variante

Dans les exemples précédents les boites flexibles sont alignées au début de l'axe (horizontal ou vertical).
C'est pas forcément très joli.
On peut demander que les items soient répartis de façon équilibrée sur l'axe.
Il suffit encore une fois de modifier la règle de style relative au sélecteur ARTICLE.

	article {... display : flex ; flex-direction :column ; justify-content : space-around;}

Nouveauté : la propriété justify-content: space-around
Nouvelle propriété justify-content !
Observez le rendu !

Positionner une boite flexible en haut à gauche du flex-container

Nous voulons qu'un item unique soit positionné en haut et à droite dans son conteneur.

Le code de la partie BODY

Code CSS

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

La propriété justify-content gère l'alignement sur l'axe principal (ici l'axe vertical puisque flex-direction vaut "column")
La propriété align-items gère l'alignement sur l'axe secondaire (ici l'axe horizontal).
Le rendu !

La boite flexible "enfant" est bien positionnée en haut et à gauche dans son flex-container ("parent") compte tenu des valeurs des deux propriétés.

Gérer l'alignement des items selon les deux axes

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

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 la partie BODY

La feuille de style

	#parent{ display : flex; justify-content : center ; align-items : center; 
			border : 1px solid red; height : 600px; width : 600px ; margin : auto;}
	#enfant {background : pink ; width : 60% ;  } 
	div p{margin : 10px ; font-size : 14pt; text-align : justify; }

Donc pour centrer selon les deux axes il suffit que ces deux propriétés "justify-content" & "align-items" aient la valeur : center.
Observez le résultat !

Centrer plusieurs boites flexibles

Inutile de se prendre la tête !

La feuille de style

#parent{ display : flex;   flex-direction : column; justify-content : center ; align-items : center; 
			border : 1px solid red; height : 600px ; width : 600px ; margin : auto;}
.enfant {background : pink ; width : 60% ; margin-top : 10px; } 
div p{margin : 10px ; font-size : 14pt; text-align : justify; }

Code HTML

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

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

Délibéremment j'ai fait l'impasse sur quelques subtilités de l'outil flexbox qui ne me paraissent pas indispensables si vous utilisez en en priorité l'outil grid-layout et de façon secondaire flexbox.

Quid des anciennes techniques de positionnement.

La propriété float

Pour positionner une image ou une lettrine en haut à gauche ou à droite d'une boite avec le texte tout autour, continuez d'utiliser float.
Par contre cette propriété doit être bannie pour positionner les grandes divisions de la page ; l'outil grid-layout est plus approprié.

Position absolute

Pour supersposer des blocs vous serez toujours obligé d'utiliser la propriété "position" avec la valeur "absolute" et conjointement avec "z-index".
Par contre pour centrer verticalement une boite dans son conteneur l'outil flexbox est plus approprié et beaucoup plus simple.

Position fixe

Un menu ancré par rapport à la fenêtre c'est pratique; alors position : fixed & z-index sont incontournables !

Position relative

Je ne vois plus qu'un seul intérêt à position relative affecté à un conteneur : les enfants se positionnent en absolu par rapport au coin haut gauche du conteneur et non pas par rapport au coin haut gauche de l'écran.

Display inline-block

Cette valeur de display reste utile pour disposer de front plusieurs images.
Par contre pour des blocs de texte il faut plutôt recourir à flexbox sur l'axe horizontal.
Retour menu