Ce tutoriel a pour but de présenter dans les détails une technique de conception de grille de mise en forme responsive à l’aide du positionnement CSS3 Flexbox Layout, actuellement parfaitement adapté à ce genre de fonctions en attendant une meilleure implémentation de Grid Layout.
En février 2016 est sorti mon livre entièrement dédié à Flexbox. Il se nomme "CSS3 Flexbox : plongez dans les CSS modernes" et je vous recommande bien évidemment sa lecture afin de comprendre tous les rouages de ce positionnement révolutionnaire, et d'en maîtriser tous les aspects.
Le concept de grille produite permettra de gérer les espaces inter-colonnes (gouttières), les décalages (“offsets”), les différentes tailles d’écran et d’être automatisable. Le tout en un minimum de code et un maximum de propreté HTML.
Voir le résultat final en ligne
Le fondement : Flexbox Layout
Notre grille sera construite grâce au module Flexbox, aujourd’hui compatible sur une bonne majorité des navigateurs (95% en France selon les stats Caniuse en fin 2014).
Si vous souhaitez assurer un maximum de compatibilité envers les anciens navigateurs, vous devrez vous rabattre sur les techniques float
, table-cell
ou inline-block
avec tous les détails et exemples dans cet article consacré aux grilles de mise en forme.
Note : Flexbox nécessitant encore certaines précautions en terme de préfixes et de syntaxes, je vous conseille vivement de vous équiper de l’outil Autoprefixer afin de vous décharger de cette tâche fastidieuse une fois pour toutes.
Les bases de la grille
Mon souhait est que seul le conteneur sera affublé d’une classe, et ses enfants (directs) en bénéficieront automatiquement, sans avoir à les nommer spécifiquement.
Par exemple, le code imaginaire suivant pourrait générer une grille de 4 colonnes où tous les enfants directs occuperaient 1/4 de l’espace disponible s’il n’y a pas de gouttière :
.container-grid-4 {
/* container's styles */
}
.container-grid-4 > * {
/* direct children's styles */
}
Ébauche de grille en flexbox
Commençons par une grille de 4 éléments via flexbox et sans définir de gouttière :
/* moving to box-sizing (always) */
* { box-sizing: border-box; }
/* styles du conteneur */
/* hozizontal display and wrap enabled */
.container-grid-4 {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
/* direct children's styles */
/* width defined to 25% */
.container-grid-4 > * {
flex: 0 0 auto;
width: 25%;
}
Gérer la gouttière
À l’heure actuelle, gérer l’espace entre les éléments d’une grille n’est toujours pas aussi intuitif qu’il devrait l’être, nous sommes encore obligés de passer par des bidouilles pour cela.
L’une des astuce devenue un grand classique, le combo margin-left
négatif sur le parent + padding-left
sur chaque enfant, est certainement la plus prisée. Cependant, grâce au modèle particulier de Flexbox, nous allons pouvoir nous affranchir du padding
et ne conserver comme critère que le margin
.
Voici comment elle opère… Supposons que je souhaite créer trois colonnes espacées de 30px. Le principe de cette astuce est de :
-
conférer une largeur (
width
) de 33.33% à chacun des enfants moins la gouttière :width: calc(33.33% - 30px);
-
d’attribuer un
box-sizing: border-box
pour être sûr que les boîtes ne s’agrandiront pas en ajoutant du padding ou une bordure, -
appliquer un
margin-left
de 30px sur chaque enfant, -
appliquer une
margin-left
de valeur négative (-30px) sur le conteneur, ce qui aura pour effet d’étirer ses enfants et d’absorber le margin du premier enfant.
.container-grid-3 {
margin-left: -30px;
}
.container-grid-3 > * {
width: calc(33.33% - 30px);
margin-left: 30px;
}
Voir la grille avec gouttières
Automatiser avec un préprocesseur
Parvenu à ce stade, un nouveau pallier pourrait être franchi : celui d’automatiser la construction de la grille quel que soit le nombre de colonnes souhaitées et quelle qu’en soit la valeur de gouttière.
C’est à présent le boulot des préprocesseurs tels que LESS ou Sass. Pour ma part, j’ai choisi LESS pour ce tutoriel.
Créons un mixin .grid(x,y)
que l’on pourrait appliquer à n’importe quel élément HTML conteneur, et donc les paramètres x
et y
représenteraient respectivement le nombre de colonnes et la largeur de la gouttière.
La largeur des enfants en pourcentage serait calculée selon le nombre de colonnes via la formule calc(100% * 1 / @{number} - @{gutter});
:
[class*="grid-"] {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
margin-left: -@gutter;
}
[class*="grid-"] > * {
flex: 0 0 auto;
display: block; /* IE fix */
width: ~'calc(100% * 1 / @{number} - @{gutter})';
margin-left: @gutter;
}
.grid(@number:@number, @gutter:@gutter) {
& > * {
width: ~'calc(100% * 1 / @{number} - @{gutter})';
}
}
Bonus : meilleure “sémantique” HTML
L’apport d’un mixin LESS, outre le fait d’automatiser la création de grilles, est d’épurer le code HTML et CSS en développement.
Ainsi, il ne sera plus nécessaire d’ajouter des classes supplémentaires dans notre “markup” puisque ce genre de syntaxe sera rendu possible :
ici je veux une grille de 6 colonnes
// section crée une grille de 6 colonnes espacées de 1em (défaut) chacune
.grid-perso {
.grid(6);
}
Bonus : éléments double ou triple taille
Certains éléments doivent être mis en exergue et occuper le double d’espace que leurs voisins ? Rien de plus simple avec flexbox : il suffit d'adapter la valeur de width
voire de l’automatiser avec LESS pour que cette valeur s’adapte quel que soit le nombre de colonnes.
.flexitem-double {
width: ~'calc(100% * 2 / @{number} - @{gutter})';
}
.flexitem-triple {
width: ~'calc(100% * 3 / @{number} - @{gutter})';
}
Bonus : “à la Une”
À présent, pourquoi ne pas continuer avec les bonnes choses ? Grâce à la propriété order
du positionnement flexbox, il est très simple d’intervertir l’ordre d’affichage des éléments et faire remonter un bloc avant les autres.
Il suffit pour cela de lui attribuer une valeur de order
inférieure à la valeur par défaut qu’est 0
:
// will be displayed first
.flexitem-first {
order: -1;
}
// will be displayed last
.flexitem-last {
order: 2;
}
Des Media Queries pour toutes les surfaces
Enfin, n’oublions pas le bouquet final qu’est l’adaptation à toutes les surfaces.
Là encore il est possible d’automatiser un maximum de fonctionnalités à l’aide de variables et de mixins.
Par exemple, pour forcer une grille à s’afficher en deux colonnes lorsque l’écran est réduit, voilà ce qui est envisageable :
@media (min-width: (@tiny-screen + 1)) and (max-width: @small-screen) {
& > * {
width: ~'calc(100% * 1 / 2 - @{gutter})';
}
& > .flexitem-double {
width: ~'calc(100% - @{gutter})';
}
Résultat final
Nous voici donc arrivés à la fin de notre (agréable) périple. Notre grille est prête et fonctionnelle.
Concrètement, voici le récapitulatif des codes HTML et CSS / LESS employés tout au long de ce tutoriel.
J’espère que le résultat (et sa simplicité) vous convaincront !
HTML
<div class="grid-4">
<div>1.</div>
<div>2.</div>
<div>3.</div>
<div>4.</div>
<div class="flexitem-double flexitem-first">5.</div>
<div>6.</div>
<div>7.</div>
<div>8.</div>
<div>9.</div>
<div style="margin-left: auto;">10.</div>
</div>
CSS / LESS (styles de travail)
C’est le fichier LESS qui nous servira de base de travail pour modifier les styles de notre projet.
Pour notre plus grand plaisir, il est réduit au strict minimum.
Exemple de grille de 6 colonnes espacées de 1em (défaut) chacune, appliquée sur l'élément de classe ".grid-container" :
.grid-container {
.grid(6);
}
Exemple de grille de 12 colonnes espacées de 10px chacune, appliquée sur l'élément de classe ".grid-perso-douze" :
.grid-perso-douze {
.grid(12, 10px);
}
CSS / LESS (mixins LESS)
Ces mixins sont définis une fois pour toute, il devrait être inutile d’y toucher ou de les modifier une fois qu’ils sont en place.
// grid styles for container wich has a .grid(n,g) class
// n = number of columns (default = 4)
// g = gutter value (default = 1em)
// example : .grid-container { .grid(12, 10px); }
[class*="grid-"] {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
margin-left: -@gutter;
}
[class*="grid-"] > * {
flex: 0 0 auto;
display: block; /* IE fix */
width: ~'calc(100% * 1 / @{number} - @{gutter})';
margin-left: @gutter;
}
.grid(@number:@number, @gutter:@gutter) {
& > * {
width: ~'calc(100% * 1 / @{number} - @{gutter})';
}
& > .flexitem-double {
width: ~'calc(100% * 2 / @{number} - @{gutter})';
}
& > .flexitem-first {
order: -1;
}
@media (min-width: (@tiny-screen + 1)) and (max-width: @small-screen) {
& > * {
width: ~'calc(100% * 1 / 2 - @{gutter})';
}
& > .flexitem-double {
width: ~'calc(100% - @{gutter})';
}
}
@media (max-width: @tiny-screen) {
& > * {
width: ~'calc(100% - @{gutter})';
}
& > .flexitem-double {
width: ~'calc(100% - @{gutter})';
}
}
}
Voir le résultat final en ligne
Retrouvez l'intégralité de ce tutoriel en ligne sur Alsacreations.com