Accueil

Traduction

Tutoriel CSS3 - sommaire

Tutoriel CSS3 - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

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

Préambule

Le positionnement des boites a toujours été, en développement web, un point délicat.
Les développeurs web ont longtemps utilisé les tableaux HTML pour mettre en forme les pages web.
Puis le CSS a proposé différentes techniques de positionnement via la propriété POSITION (voir chapitre relatif à cette propriété).
Mais beaucoup de développeurs WEB utilisaient la propriété float pour positionner les grandes divisions de la page alors qu'elle était prévue à l'origine pour disposer des éléments inline dans un conteneur.
Nous avons vu par ailleurs qu'en transformant des éléments "block" ou "inline" en "inline-block", ceux-ci se positionnaient de front.

Je vous annonce une excellente nouvelle : CSS3 introduit deux nouvelles spécifications pour le positionnement des boites : flexbox & grid layout
L'outil flexbox (boites flexibles) est déjà bien implémenté sur les navigateurs.
Concernant l'outil grid layout (positionnement en grille), est connu de la dernière version de MS Edge sortie en 2020 et bien entendu de Chrome et Firefox.
Donc ces deux outils peuvent être utilisés en production.

Le positionnement en grille : "grid layout"

Je débute par l'outil "grid layout" (positionnement en grille).
Comme je disais plus haut, cet outil est implémenté par tous les navigateurs récents.

Une grille avec 6 cellules

Le code HTML

Le code CSS pour 2 lignes et 3 colonnes donc 6 cellules toutes identiques

	body {
		width : 90%; min-height : 100vh; border : 1px solid red; margin :auto;
		display : grid;
		/* 2 colonnes et 3 lignes donc 6 zones */
		grid-template-rows  : auto auto auto ; 
		grid-template-columns : auto auto ;
		grid-row-gap : 2vh; 
		grid-column-gap : 2vw; 
		}
	.boite {background : yellow; }
	h3 {text-align : center; }

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).
CSS : les unités de mesure dont vh,vw,rmn, etc.

Le rendu

Le premier DIV occupe implicitement la première cellule, le deuxième DIV la deuxième cellule et ainsi de suite.
Les trois colonnes ont la même largeur, les deux lignes ont la même hauteur donc les six cellules sont identiques !
Pour les que les lignes et les colonnes aient la même hauteur / largeur il suffit de leur attribuer la valeur "auto".

Version 2 : colonne du milieu deux fois plus large que la première et dernière

Le code HTML est inchangé mais le code CSS devient alors :

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

Notez les valeurs des propriétés grid-template-rows & grid-template-columns !
Le raccourci grid-gap : esp. lig. esp. col. remplace avantageusement les propriétés grid-row-gap & grid-column-gap.

Le rendu

Comme les colonnes ne doivent pas avoir la même largeur, j'ai utilisé une nouvelle unité de mesure : "fr".
Pour les lignes j'ai aussi utilisé cette nouvelle unité de mesure à la place de "auto".

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

Le code HTML correspondant

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.

La feuille de style

body {width : 90%;min-height : 100vh;  margin :auto;
		display : grid ;
		grid-template-rows: 1fr 7fr 2fr;
		grid-template-columns: 3fr 7fr; 
		grid-gap :2vh 2vw;
		}
		
	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 ; }
h3 {text-align : center; }

On définit d'abord une grille de trois lignes et deux colonnes donc 6 cellules.
Chaque "fr" représente ici le dixième de la hauteur /largeur disponibles.

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-row.

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).
Personnellement, j'aime pas trop cette technique ; je la trouve peu claire.

Le rendu

Le rendu est strictement identique ; donc je ne le présente pas.

Troisième solution : nommer les cellules

On nomme chaque cellule de la grille (propriété grid-template-area) ; plusieurs cellules pouvant porter le même nom. Puis on affecte à chaque division de la page un nom (propriétégrid-area)

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

Je vous communique toute la feuille de style

body {min-height : 100vh; width : 90%; margin :auto;
		display : grid ;
		grid-template-rows: 1fr 7fr 2fr;
		grid-template-columns: 3fr 7fr; 
		grid-template-areas: "h h"   "n a"  "f f";
		grid-gap :2vh 2vw;
 }

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

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

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 grande division de la page :

Les propriétés grid-row & grid-column disparaissent !

Le rendu

Nommer les cellules - syntaxe

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.

Thème

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 se contente de structure la page en grandes divisions.

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: 2fr 6fr 2fr; 
  grid-template-rows: 1fr 1fr 7fr 1fr; 
  grid-template-areas: "h h h"   "n n n "  "a a s"  "f f f";
  grid-gap : 10px;
 }
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 ;}
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; }
} 

L'espacement entre rangées et colonnes est le même : 10px.
Une grille de 3 colonnes et 4 lignes.

Par défaut la largeur de BODY est égale à 800 pixels mais si largeur de la fenêtre est inférieure à 801px alors la largeur de la page = 100% et la boite ASIDE est masquée (display : none); la division "article" occupe alors toute la troisième ligne (nouvelles valeurs pour grid-template-areas).

Le rendu

Le rendu dans un nouvel onglet

Le rendu sera fonction de la largeur de l'écran. Sur votre PC réduisez progressivement la largeur de la fenêtre d'affichage jusqu'à ce que la boite ASIDE disparaisse.
Sur un petit écran les quatres boites (puisque ASIDE est masqué) sont les unes en dessous des autres.

L'outil "flexbox" : les boites flexibles

Lorsqu'on travaille avec l'outil "flexbox", deux axes interviennent : l'axe principal et l'axe secondaire.
L'axe principal est défini par la propriété flex-direction et l'axe secondaire est alors l'axe qui lui est perpendiculaire.
La valeur par défaut de flex-direction est row (ligne). Ce qui signifie que les items du conteneur seront positionnés sur une seule ligne.

Découvrez dans les exemples qui suivent la syntaxe relative à l'outil "flexbox".
Pour des raisons pratiques les exemples'affichent à chaque fois dans un nouvel onglet.
Pensez à fermer ces onglets.

Exemple 1 : l'axe principal est l'axe horizontal

Les enfants ("flex items") du "flex container" sont donc disposés sur une ligne (row).

La feuille de style

	body {width : 80vw; margin : 20px auto ;   }  
	.conteneur {border : 1px solid red; display : flex;  flex-direction : row; gap : 10px;}
	.enfant {background : lime ; padding : 10px ;

Notez la règle de style relative à la classe "conteneur" : display : flex; flex-direction : row; gap : 10px Ce qui signifie que l'élément de type bloc bénéficiant de cette classe est le "flex container" et les enfants de ce conteneur seront des "flex-items".
Remarquez que pour les trois "flex-items", je n'ai défini ni hauteur ni largeur, la dimension est flexible (en fonction du contenu).

Le code HTML

Le "flex container" contient trois "flex items".

Le rendu dans un onglet

Les "flex-items" occupent toute la largeur disponible et ont tous la même hauteur par contre ils n'ont pas la même largeur ; celle-ci est fonction du contenu.
Réduisez la largeur de l'onglet et observez ; la hauteur du "flex container" s'ajuste.

Exemple 2 : l'axe principal est l'axe vertical

Donc les "flex items" sont positionnés sur une seule colonne (column).

Le code

La feuille de style :

	body {width : 80vw; margin : 20px auto ;  }  
	.conteneur {border : 1px solid red;  display : flex;  flex-direction : column; gap : 10px;}
	.enfant {background : lime ; padding : 10px ; }

La feuille de style est identique à l'exemple 1 sauf que la valeur de flex-direction qui est ici à "column".

Le rendu dans un onglet

Réduisez la largeur de l'onglet ; la hauteur de chaque "flex item" s'ajuste.

Fixer une largeur/hauteur à chaque item

Le conteneur contient cinq "flex items" dont la largeur est fixée à 250px.

Mauvaise solution

Le code CSS :

	body {width : 80vw; margin : 20px auto ;   }  
	.conteneur {border : 1px solid red; display : flex;  flex-direction : row; gap : 10px;}
	.enfant {background : lime ; padding : 10px ; width : 250px; }

Notez bien la largeur de chaque "flex-item" règlée à 250px.

Le rendu

Réduisez la largeur de l'onglet. La largeur fixée à 250px n'est alors plus respectée. Il peut même y avoir débordement. C'est pas joli !

La bonne solution

Il faut rendre possible un saut de ligne / saut de colonne.

Le code CSS :

	body {width : 80vw; margin : 20px auto ;  }  
	.conteneur {border : 1px solid red; display : flex;  
		flex-direction : row ; gap : 10px; flex-wrap : wrap;}
	.enfant {background : lime ; padding : 10px ; width : 250px;}

flex-wrap : wrap : un saut (ici de ligne) est possible.

Le rendu dans un onglet

Réduisez la largeur de l'onglet jusqu'à ce que des items passent à la ligne.

Gérer l'alignement des blocs sur l'axe principal

Pour un rendu esthétique de la page il faut une distribution équilibrée des blocs sur l'axe principal. C'est l'affaire de la propriété justify-content.

Exemple de belle distribution des blocs

Le "flex-container" contient six "flex-items" ; la répartition des blocs selon l'axe principal doit être équilibrée ; un saut de ligne doit être possible.

Le code CSS
	body {width : 80vw; margin : 20px auto ;   }  
	.conteneur {border : 1px solid red; display : flex;  
		flex-direction : row ;
		flex-wrap : wrap; justify-content : space-around;}
	.item {background : lime ; padding : 10px ; margin: 10px; width : 250px; }

Notez la règle de style justify-content : space-around dans le sélecteur .conteneur

Le code HTML

Six DIV affectés de la classe "item".

Le rendu

Selon la largeur de l'onglet, les six boites sont de front ou sur deux lignes voire sur 3 lignes avec à chaque fois des marges entre les items et les extrémités.

Centrer horizontalement et verticalement un "flex item" unique 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 horizontal et vertical des "flex-items" dans leur conteneur devient un jeu d'enfant. Il suffit de demander un centrage sur les deux axes.

Pour gérer le positionnement selon l'axe secondaire il faut utiliser la propriété align-items.

Le code HTML

La feuille de style

	#parent{border : 1px solid red; display : flex;  justify-content : center ; align-items : center; 
			 height : 600px ; width : 80vw ; margin : 10px auto; }
	#enfant {background : pink ; width : 60vw; min-height : 200px; } 

J'utilise donc les propriétés justify-content & align-items avec à chaque fois la valeur "center".
Notez l'emploi de l'unité de mesure vw.

Le rendu

Centrer horizontalement et verticalement plusieurs flex items

Le code

Qu'il y ait un ou plusieurs enfants à centrer selon les deux axes, le code CSS est identique !

Le code HTML : vous devez être capable de le reconstituer.

Le rendu dans un onglet

Quand les "flex-items" sont des images

Les "flex-items" peuvent être des images.

Le code correspondant

flex-flow : row wrap est un raccourci qui remplace flex-direction : row & flex-wrap : wrap
Notez une des règles de style du sélecteur section img : object-fit : cover

Le rendu

Cliquez ici !

Certaines images sont un peu rognées c'est à cause de object-fit : cover. Voir l'encadré plus bas.

Variante

Testez le rendu avec object-fit : contain.
Bordurez les images d'un contour rouge et observez ...

Les images ne sont pas rognées et n'occupent pas forcément toute la zone dédiée.

Le rendu avec object-fit : contain

Conseil : dans la mesure du possible utilisez pour votre galerie photos des images qui ont des ratios largeur/hauteur proches.