Niveau : Expert

Une grille responsive avec CSS3 Flexbox et LESS (ou Sass)

Tutoriel par (Intégrateur du Dimanche, Strasbourg)
Créé le , mis à jour le (65446 lectures)
Tags : grid, responsive, sass, less, préprocesseur, flexbox

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.

grille css

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;
}

flex gouttières

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 :

<section>ici je veux une grille de 6 colonnes</section>
// 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.

flexbox grid

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

Commentaires

glow a dit le

Merci pour ce tuto Raphaël.

zemoko a dit le

Le résultat en ligne ne fonctionne pas sur mon Safari (8).... Si je rajoute une propriété "flex-wrap: wrap;", ça marche...

Mais sinon, merci pour le tuto :-D

Raphael a dit le

@zemoko : ah oui tiens, Safari 8 ne reconnaît pas flex-flow. J'ai modifié le Pen, ça devrait fonctionner à présent.

ghassen a dit le

c'est super, merci bq Raphael

Raphael a dit le

@ghassen, @glow : de rien, j'espère que ce sera utile. Je prévois de l'intégrer dans la prochaine version majeure de KNACSS

eviouchka a dit le

Bonjour et merci @raphael pour ce tuto aux p'tits oignons hyper détaillé. J'aime particulièrement le fichier LESS réduit à quelques lignes ;)

bmael a dit le

Ca m'a l'air vraiment très "usefull". J'aurai besoin de ça mais je n'arrive pas à le faire fonctionner.
Que me manque-t-il pour que ça marche ?
--> http://www.lesmulots.org/formations-test.html...

Raphael a dit le

@bmael :
Bonjour. Vous avez copié le code LESS tel quel dans votre fichier .css, il ne peut pas être interprêté ainsi. Il faut que le code LESS soit compilé pour générer du CSS.
Si vous êtes complètement étranger à LESS, voici comment faire vos premiers pas : http://lesscss.org/

Bonne lecture, bon apprentissage et bonne journée :)

bmael a dit le

Oui j'ai entendu parlé du LESS mais ne connais pas. Je vais donc étudier ça.
Je vous remercie.

quice a dit le

Ce qui me fait un peu rigoler sur les outils "pour gagner du temps" comme par exemple grunt pur ne citer que lui, c'est que on "gagne" du temps pour aller le gaspiller à la cafet', au déjeuner ou en salle de jeu pour ceux qui en ont...
De toute façon, pour "voler" les contrats à la concurrence, les boites réduisent le temps de dev au max, voire à l'abus, ce qui fait que la plupart du temps, on livre toujours en retard... C'est du vécu... malheureusement, alors il faut rester de plus en plus tard le soir...
Perso je maitrise bootstrap/less, j'aime et ça me suffit, on va économiser quoi avec le reste, quelques ko? on peut largement les gagner en optimisant les images. Dans mon ancienne boite,on m'avait confié l'optimisation d'un site pour mobile. quand j'ai vu le poids des images quand j'ai repris le projet, j'ai failli avoir une attaque, surtout quand le DT insistait sur l'optimisation du code... je ne dirais qu'une chose : LOL

Gili a dit le

@quice changes de boulot si tu te suffit de bootstrap/less qui sont des technos à un instant T. Hors le web ça évolue tout le temps. C'est comme si je disais j'ai appris avec les tableaux de mises en pages et perso ça me suffit ! De plus, l'optimisation des ressources n'a rien avoir avec l'optimisation de ton système de mise en page. Donc non ça te fera même pas gagner quelque KO.

Après le temps économiser grâce à ce genre d'outil t'en fait ce que tu veux. Soit tu augmentes la buisness value de ton project soit tu vas, effectivement, boire un pot à la machine à café.

quice a dit le

Qaund je dis j'aime et ça me suffit, il est bien entendu que c'est en attendant quelque chose de plus conséquent en terme de performance et pas une petite réécriture de ce qui éxiste déjà. pas la peine de réinventer la roue, elle éxiste déjà -____-'
Après, tu bosses comme tu veux, pour ce qui est du temps gagné, si le but est de rester plus longtemps en cafet, ben LOL. Si déjà t'arrives pas à comprendre ça, ben... ... ... ..., je ne répondrai pas à la suite... je ne vais répéter la messe pour les sourds.

Raphael a dit le

@quice : "si le but est de rester plus longtemps en cafet, ben LOL".

On peut aussi voir les choses autrement.
En ce qui me concerne, j'estime que tout le temps que je dois passer sur des trucs facilement automatisables (minification, concaténation, compilation, etc.) est du temps que je ne peux pas consacrer à faire du code propre, documenté, maintenable, accessible.
Bref, c'est plutôt une question de "balance des tâches" que de simplement vouloir "travailler moins".

oneskill a dit le

Bonsoir . Excusez moi de vous déranger mais j'aurais une petite question qui peux sembler bête mais après maintes recherches et essais , je ne me figure pas si ceci est possible . Voici donc ma question . Est il possible avec flexbox que mon header avec ma navbar et mon logo reste static ( comme avec position:fixed) et que mon site déroule derrière en scroll down ? Je désespère de trouver là. Merci de m'avoir lu et pour vos réponses.

oneskill a dit le

Bonsoir . Excusez moi de vous déranger mais j'aurais une petite question qui peux sembler bête mais après maintes recherches et essais , je ne me figure pas si ceci est possible . Voici donc ma question . Est il possible avec flexbox que mon header avec ma navbar et mon logo reste static ( comme avec position:fixed) et que mon site déroule derrière en scroll down ? Je désespère de trouver là. Merci de m'avoir lu et pour vos réponses.

cyrano25550 a dit le

Merci beaucoup mais dommage que cela ne fonctionne pas correctement avec IE11 (les box ne remplissent pas complètement la ligne). Erreurs signalées dans Can I use et pour lesquelles Microsoft n'annonce pas de délai de correction (le problème remonté a l'air différent mais mon niveau de retraité autodidacte ne permet pas de l'affirmer !).

casp a dit le

Je crève d'envie d'utiliser cette méthode...
D’ailleurs les flexbox d'une façon général c'est vraiment magique.

Mais pour le moment à cause de IE à la noix je m'en passe.. snif je vais pas m'amuser à gagner du temps en utilisant flexbox pour le perdre à devoir prévoir les cas LT IE11....

Raphael a dit le

@cyrano25550 : ça devrait être résolu à présent avec les dernières modifs.

MathieuVD a dit le

Bonjour Raphael,

Il semble que flex-wrap: wrap ne fonctionne pas sur android 4.1.1
En tout cas pas sur le Samsung SIII avec lequel j'effectue mes tests et ce malgré la présence de -webkit-flex-wrap: wrap;
Une idée de comment on peut régler cela ?

Merci

cux a dit le

Bonjour Raphael,

je signale une petite coquille dans "Gérer la gouttière", 3ème li :

appliquer un margin-left de 30px sur chaque enfant (plutôt que margin-left qui ne permettrait pas de conserver un total de 50% + 50%,,

devrait être, si je ne m'abuse :

appliquer un padding-left de 30px sur chaque enfant (plutôt que margin-left qui ne permettrait pas de conserver un total de 50% + 50%),

Merci pour le tuto !

samIntegrateur a dit le

@MathieuVD : Tu dois avoir android browser je pense, il n'est malheureusement pas très à jour et me pose régulièrement des soucis. Plus généralement, sur certains navigateurs pas si vieux, le support de "flex" était partiel, et la propriété "wrap" n'était pas pris en compte.
Si tu utilises Modernizr, tu peux utiliser la classe "no-flexbox" qui tu permettras de gérer un fallback en inline-block par exemple.

Gyzm0 a dit le

Bonjour Raphael,

Tout d'abord merci pour ce tuto très intéressant !
J'ai remarqué une petite coquille dans le code css de la partie "gérer la gouttiere" :

margin-left: 30px solid transparent;

Est ce une erreur ?

romainlange a dit le

Bonjour Raphaël,

merci pour le tuto... que j'ai bien du mal à reproduire.
J'ai repris le HTML+CSS (sans LESS) et je ne parviens pas à 4 colonnes avec une largeur de 25% sur les enfants.
Par ailleurs les valeurs "solid transparent" sur la margin affichent une erreur dans l'inspecteur Chrome.
Qu'est-ce qui cloche?
http://codepen.io/romainlange/pen/RPPeMg

Merci!

romainlange a dit le

Ne réussissant pas à recréer la démo, j'ai trouvé une alternative en HTML & CSS: pour quatre colonnes, chaque bloc a width=24% + une margin-left=1%.
http://codepen.io/romainlange/pen/pJJGqB

Sinon, n'y a-t-il pas une erreur dans le texte?
> appliquer un margin-left de 30px sur chaque enfant (plutôt que margin-left qui ne permettrait pas de conserver un total de 50% + 50%)
> appliquer un margin-left ... plutôt que margin-left ... ?

Bon dimanche!

romainlange a dit le

Bonjour Raphaël,

je reviens à la charge, mais il me semble qu'il y a un soucis dans cet article:
le code indique une largeur de 25% pour les enfants, le texte parle de 50% et l'image d'illustration montre 33,33%. Non?

romainlange a dit le

Trouvé:
le teste parle de padding-left
le code de margin-left
chez moi ça marche avec border-left
http://codepen.io/romainlange/pen/RPPeMg

Merci !

Raphael a dit le

Bonjour Romain (et désolé pour le délai de réponse).

Il y avait effectivement une coquille dans les explications et le code.
Il s'agit bien au final de n'utiliser que margin (pas padding ni border). Les codes sont corrigés ainsi que les exemples en ligne :
(sans LESS) : http://codepen.io/raphaelgoetter/pen/mzKrh?editors=110
(avecLESS) : http://codepen.io/raphaelgoetter/pen/BybOag?editors=110

ObjM a dit le

Bonjour Raphael,
tout d'abord merci pour ce tuto !
je l'ai appliqué à mon site et je suis très content, sauf que .....
Comme Zemoko le soulignait, ça ne marche pas avec Safari ! :( .
Meme avec la petite ligne "fex-wrap: wrap;" dont il parle ! .
Les boites se mettent juste les unes en dessous des autres ! (les garces ! ).
Que faire ?
Alex

ObjM a dit le

Bonjour Raphael,
petit up.
Je n'arrive tjs pas a voir pourquoi ça ne marche pas correctement avec Safari :(
Une idée? Une solution?

tdmsihem a dit le

Bonjour Raphael,
Merci pour le tuto .
J'ai commencé à utiliser ce framework et je trouve vraiment super cool .
Alors j'ai voulu utilisé la grille pour créer une page style mosaïque (image + text)
J'ai besoin d'utiliser ' flex-item-double ' mai dans le sens vertical non horizontale
Est -il possible de faire ceci ?
s'il y a d'autre alternative veillez m'indiquer svp :)

MissAnna a dit le

Bonjour Raphael, j'ai testé sous :
- chrome 45
- IE 11
- Firefox 38.
L'affichage de la grille fonctionne parfaitement sous les trois navigateurs. Affichage de la grille en 3 colonnes.

Mais j'ai de gros problème pour l'impression de ces pages !
Sous firefox la grille s'imprime sous 2 colonnes
Sous Chrome, la grille s'imprime sous une seule colonne (pourquoi pas 2 colonnes comme sous firefox ??, y'a la place)
sous IE, magie : page blanche...., les pages s'affichent (27),mais sans contenu..