Les grilles sont des outils incontournables pour structurer le layout d’une page web. float
, grid
, flex
… il existe de nombreuses approches pour les implémenter en CSS. Focus sur une approche que j’affectionne particulièrement pour l’expérience développeur (DX) qu’elle propose !
Pourquoi créer des grilles en CSS ?
Les grilles CSS permettent de diviser l’espace d’une page en colonnes et rangées, facilitant la création de mises en page cohérentes, adaptables et esthétiques.
Aussi, elles permettent de gérer efficacement le responsive design en adaptant l’affichage à différents types d’écrans :
- Mobile
- Tablette
- Desktop
- Etc.
Elles sont indispensables pour structurer le layout principal d’une page web, tels que :
- Header et footer : Les grilles divisent un header en sections comme un logo, des liens de navigation et des icônes de réseaux sociaux, tandis que le footer peut afficher des liens et des informations de contact en colonnes.
- Sections de contenu : Sur une page de blog ou un site e-commerce, les grilles organisent des articles, produits ou services en colonnes.
- Sidebars et contenu principal : Les grilles permettent d’ajuster la taille d’une sidebar et du contenu principal.
- Galeries d’images : Les grilles facilitent l’alignement des images en colonnes ou en mosaïque.
Il existe aujourd’hui 3 grandes façons d’implémenter un système de grille responsive en CSS :
- Avec la propriété
float
(pour les sites anciens) - Avec la propriété
flex
(découvrir flexbox) - Avec la propriété
grid
(découvrir grid)
Dans ce tutoriel, nous allons voir comment créer une grille responsive et personnalisable en CSS avec le module grid
, en utilisant des classes spécifiques pour gérer les colonnes selon les tailles d’écrans.
Tutoriel : créer une grille reponsive en CSS
La grille est une composante centrale de notre framework car elle sera utilisée dans de nombreuses situations.
Le plupart des frameworks CSS se basent sur une grille de 12 colonnes, un nombre d’or en webdesign en raison de sa grande divisibilité entière.
Nous allons dans notre cas proposer un système légèrement plus modulable en permettant de définir des grilles de 1 à 12 colonnes.
Conteneur de grille
Définir des colonnes fixes
Afin d’optimiser l’expérience développeur, nous déclarerons un conteneur de grille (on parle généralement de « grid-container ») ainsi que son nombre de colonnes au sein d’une classe CSS nommée ainsi : grid-{nb-colonnes}
.
Nous appliquerons en ce sens notre display: grid
sur un sélecteur indiquant « tous les éléments contenant grid-
».
[class*="grid-"] {
display: grid;
}
Pour diviser notre grille en colonnes, nous devons faire appel à la propriété CSS grid-template-columns
. On obtient donc les 12 classes CSS suivantes :
.grid-1 { grid-template-columns: repeat(1, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-5 { grid-template-columns: repeat(5, 1fr); }
.grid-6 { grid-template-columns: repeat(6, 1fr); }
.grid-7 { grid-template-columns: repeat(7, 1fr); }
.grid-8 { grid-template-columns: repeat(8, 1fr); }
.grid-9 { grid-template-columns: repeat(9, 1fr); }
.grid-10 { grid-template-columns: repeat(10, 1fr); }
.grid-11 { grid-template-columns: repeat(11, 1fr); }
.grid-12 { grid-template-columns: repeat(12, 1fr); }
On divise le conteneur en colonnes identiques, sur lesquelles on pourra placer nos éléments (aussi appelé « grid-items ») de manière modulaire.
Par défaut, chaque grid-item occupera une colonne.
<div class="grid-2">
<div>Ligne 1 / Colonne 1</div>
<div>Ligne 1 / Colonne 2</div>
<div>Ligne 2 / Colonne 1</div>
<div>Ligne 2 / Colonne 2</div>
</div>
Définir des colonnes variables (responsive)
Si 1, 2 ou 3 colonnes conviennent généralement sur mobile, il peut arriver de souhaiter diviser son interface en davantage de colonnes à mesure que l’écran de l’appareil devient large (tablettes, PC…).
Il est temps de mettre au point un système permettant de redéfinir dynamiquement le nombre de colonnes d’une grille avec grid-template-columns
, lorsque l’affichage dépasse certains points de ruptures.
Ces ajustements seront définis au sein de classes CSS similaires, mais cette fois-ci préfixées par un mot-clé associé au breakpoint :
Breakpoints | Classes | Valeur (px) |
---|---|---|
xs | aucun préfixe (mobile-first) | < 576px |
sm | sm:grid-{valeur} | ≥ 576px |
md | md:grid-{valeur} | ≥ 768px |
lg | lg:grid-{valeur} | ≥ 992px |
xl | xl:grid-{valeur} | ≥ 1200px |
@media (min-width: 576px) {
.sm\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.sm\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.sm\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.sm\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.sm\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.sm\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.sm\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.sm\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.sm\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.sm\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.sm\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.sm\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
@media (min-width: 768px) {
.md\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.md\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.md\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.md\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.md\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.md\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.md\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.md\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.md\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.md\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.md\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.md\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
@media (min-width: 992px) {
.lg\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.lg\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.lg\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.lg\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.lg\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.lg\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.lg\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.lg\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.lg\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.lg\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.lg\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.lg\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
@media (min-width: 1200px) {
.xl\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.xl\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.xl\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.xl\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.xl\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.xl\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.xl\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.xl\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.xl\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.xl\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.xl\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.xl\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
Pourquoi ne pas avoir défini les valeurs des breakpoints dans des variables CSS ? 🤔
Il aurait été tentant de créer des variables pour stocker les valeurs de nos breakpoints de media queries :
:root {
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
}
Mais cela n’est pas possible car les media queries sont évaluées avant le rendu de la page, au moment où le navigateur détermine le style à appliquer en fonction des caractéristiques de l’appareil (largeur, résolution, etc.).
Les variables CSS, en revanche, sont évaluées durant le rendu et peuvent être dynamiques. Les media queries ont besoin de valeurs statiques pour pouvoir être comparées immédiatement, tandis que les variables CSS sont trop flexibles et peuvent changer au cours de la navigation.
Facteur d’étirement
Nos grid-items occupent par défaut une seule colonne et ne sont pas extensibles, ce qui leur impose naturellement de tous occuper la même place dans la grille.
En revanche, il est souvent nécessaire de créer un layout dans lequel nos élements possèdent une taille variable. On pourrait par exemple souhaiter avoir les ratios d’affichage suivants :
- 3 colonnes :
1/3
-2/3
- 4 colonnes :
1/4
-2/4
-1/4
- 4 colonnes :
3/4
-1/4
- Etc.
En bref, nous devons être en mesure de spécifier à nos grid-items d’occuper plusieurs colonnes. Cette expansion se fera en créant des classes appliquant la propriété grid-column
.
Expansion fixe
Pour dire à nos éléments de s’étendre sur plusieurs colonnes, nous allons créer une classe pour chaque facteur d’expansion :
Classe | Proportion occupée |
---|---|
.col-1 | 1/12 (8.33%) |
.col-2 | 2/12 (16.67%) |
.col-3 | 3/12 (25%) |
.col-4 | 4/12 (33.33%) |
.col-5 | 5/12 (41.67%) |
.col-6 | 6/12 (50%) |
.col-7 | 7/12 (58.33%) |
.col-8 | 8/12 (66.67%) |
.col-9 | 9/12 (75%) |
.col-10 | 10/12 (83.33%) |
.col-11 | 11/12 (91.67%) |
.col-12 | 12/12 (100%) |
.col-1 { grid-column: span 1; }
.col-2 { grid-column: span 2; }
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-9 { grid-column: span 9; }
.col-10 { grid-column: span 10; }
.col-11 { grid-column: span 11; }
.col-12 { grid-column: span 12; }
<div class="grid:3">
<div class="col-1">1/3</div>
<div class="col-2">2/3</div>
</div>
Voilà un beau ratio 1/3
- 2/3
!
Expansion variable (responsive)
Si nous avons désormais la possibilité de spécifier pour un élément combien de colonnes il doit occuper, ce nombre de colonnes sera le même sur mobile, tablette, PC…
Il devient alors essentiel de créer un facteur d’expansion variable.
L’idée consiste à redéfinir la propriété grid-column
, selon les mêmes breakpoints utilisés par le grid-container.
On obtient ainsi le code suivant :
.col-1 { grid-column: span 1; }
.col-2 { grid-column: span 2; }
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-9 { grid-column: span 9; }
.col-10 { grid-column: span 10; }
.col-11 { grid-column: span 11; }
.col-12 { grid-column: span 12; }
@media (min-width: 576px) {
.sm\:col-1 { grid-column: span 1; }
.sm\:col-2 { grid-column: span 2; }
.sm\:col-3 { grid-column: span 3; }
.sm\:col-4 { grid-column: span 4; }
.sm\:col-5 { grid-column: span 5; }
.sm\:col-6 { grid-column: span 6; }
.sm\:col-7 { grid-column: span 7; }
.sm\:col-8 { grid-column: span 8; }
.sm\:col-9 { grid-column: span 9; }
.sm\:col-10 { grid-column: span 10; }
.sm\:col-11 { grid-column: span 11; }
.sm\:col-12 { grid-column: span 12; }
}
@media (min-width: 768px) {
.md\:col-1 { grid-column: span 1; }
.md\:col-2 { grid-column: span 2; }
.md\:col-3 { grid-column: span 3; }
.md\:col-4 { grid-column: span 4; }
.md\:col-5 { grid-column: span 5; }
.md\:col-6 { grid-column: span 6; }
.md\:col-7 { grid-column: span 7; }
.md\:col-8 { grid-column: span 8; }
.md\:col-9 { grid-column: span 9; }
.md\:col-10 { grid-column: span 10; }
.md\:col-11 { grid-column: span 11; }
.md\:col-12 { grid-column: span 12; }
}
@media (min-width: 992px) {
.lg\:col-1 { grid-column: span 1; }
.lg\:col-2 { grid-column: span 2; }
.lg\:col-3 { grid-column: span 3; }
.lg\:col-4 { grid-column: span 4; }
.lg\:col-5 { grid-column: span 5; }
.lg\:col-6 { grid-column: span 6; }
.lg\:col-7 { grid-column: span 7; }
.lg\:col-8 { grid-column: span 8; }
.lg\:col-9 { grid-column: span 9; }
.lg\:col-10 { grid-column: span 10; }
.lg\:col-11 { grid-column: span 11; }
.lg\:col-12 { grid-column: span 12; }
}
@media (min-width: 1200px) {
.xl\:col-1 { grid-column: span 1; }
.xl\:col-2 { grid-column: span 2; }
.xl\:col-3 { grid-column: span 3; }
.xl\:col-4 { grid-column: span 4; }
.xl\:col-5 { grid-column: span 5; }
.xl\:col-6 { grid-column: span 6; }
.xl\:col-7 { grid-column: span 7; }
.xl\:col-8 { grid-column: span 8; }
.xl\:col-9 { grid-column: span 9; }
.xl\:col-10 { grid-column: span 10; }
.xl\:col-11 { grid-column: span 11; }
.xl\:col-12 { grid-column: span 12; }
}
On peut désormais écrire le code HTML suivant :
<div class="grid-1 md:grid-3 xl:grid-4">
<div class="md:col-1 xl:col-1">1/1 (xs), 1/3 (sm) et 1/4 (xl)</div>
<div class="md:col-2 xl:col-3">1/1 (xs), 2/3 (sm) et 3/4 (xl)</div>
</div>
Code complet
Voici le code complet de votre grille responsive :
[class*="grid-"] {
display: grid;
}
.grid-1 { grid-template-columns: repeat(1, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-5 { grid-template-columns: repeat(5, 1fr); }
.grid-6 { grid-template-columns: repeat(6, 1fr); }
.grid-7 { grid-template-columns: repeat(7, 1fr); }
.grid-8 { grid-template-columns: repeat(8, 1fr); }
.grid-9 { grid-template-columns: repeat(9, 1fr); }
.grid-10 { grid-template-columns: repeat(10, 1fr); }
.grid-11 { grid-template-columns: repeat(11, 1fr); }
.grid-12 { grid-template-columns: repeat(12, 1fr); }
@media (min-width: 576px) {
.sm\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.sm\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.sm\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.sm\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.sm\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.sm\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.sm\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.sm\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.sm\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.sm\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.sm\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.sm\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
@media (min-width: 768px) {
.md\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.md\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.md\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.md\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.md\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.md\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.md\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.md\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.md\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.md\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.md\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.md\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
@media (min-width: 992px) {
.lg\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.lg\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.lg\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.lg\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.lg\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.lg\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.lg\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.lg\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.lg\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.lg\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.lg\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.lg\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
@media (min-width: 1200px) {
.xl\:grid-1 { grid-template-columns: repeat(1, 1fr); }
.xl\:grid-2 { grid-template-columns: repeat(2, 1fr); }
.xl\:grid-3 { grid-template-columns: repeat(3, 1fr); }
.xl\:grid-4 { grid-template-columns: repeat(4, 1fr); }
.xl\:grid-5 { grid-template-columns: repeat(5, 1fr); }
.xl\:grid-6 { grid-template-columns: repeat(6, 1fr); }
.xl\:grid-7 { grid-template-columns: repeat(7, 1fr); }
.xl\:grid-8 { grid-template-columns: repeat(8, 1fr); }
.xl\:grid-9 { grid-template-columns: repeat(9, 1fr); }
.xl\:grid-10 { grid-template-columns: repeat(10, 1fr); }
.xl\:grid-11 { grid-template-columns: repeat(11, 1fr); }
.xl\:grid-12 { grid-template-columns: repeat(12, 1fr); }
}
.col-1 { grid-column: span 1; }
.col-2 { grid-column: span 2; }
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-9 { grid-column: span 9; }
.col-10 { grid-column: span 10; }
.col-11 { grid-column: span 11; }
.col-12 { grid-column: span 12; }
@media (min-width: 576px) {
.sm\:col-1 { grid-column: span 1; }
.sm\:col-2 { grid-column: span 2; }
.sm\:col-3 { grid-column: span 3; }
.sm\:col-4 { grid-column: span 4; }
.sm\:col-5 { grid-column: span 5; }
.sm\:col-6 { grid-column: span 6; }
.sm\:col-7 { grid-column: span 7; }
.sm\:col-8 { grid-column: span 8; }
.sm\:col-9 { grid-column: span 9; }
.sm\:col-10 { grid-column: span 10; }
.sm\:col-11 { grid-column: span 11; }
.sm\:col-12 { grid-column: span 12; }
}
@media (min-width: 768px) {
.md\:col-1 { grid-column: span 1; }
.md\:col-2 { grid-column: span 2; }
.md\:col-3 { grid-column: span 3; }
.md\:col-4 { grid-column: span 4; }
.md\:col-5 { grid-column: span 5; }
.md\:col-6 { grid-column: span 6; }
.md\:col-7 { grid-column: span 7; }
.md\:col-8 { grid-column: span 8; }
.md\:col-9 { grid-column: span 9; }
.md\:col-10 { grid-column: span 10; }
.md\:col-11 { grid-column: span 11; }
.md\:col-12 { grid-column: span 12; }
}
@media (min-width: 992px) {
.lg\:col-1 { grid-column: span 1; }
.lg\:col-2 { grid-column: span 2; }
.lg\:col-3 { grid-column: span 3; }
.lg\:col-4 { grid-column: span 4; }
.lg\:col-5 { grid-column: span 5; }
.lg\:col-6 { grid-column: span 6; }
.lg\:col-7 { grid-column: span 7; }
.lg\:col-8 { grid-column: span 8; }
.lg\:col-9 { grid-column: span 9; }
.lg\:col-10 { grid-column: span 10; }
.lg\:col-11 { grid-column: span 11; }
.lg\:col-12 { grid-column: span 12; }
}
@media (min-width: 1200px) {
.xl\:col-1 { grid-column: span 1; }
.xl\:col-2 { grid-column: span 2; }
.xl\:col-3 { grid-column: span 3; }
.xl\:col-4 { grid-column: span 4; }
.xl\:col-5 { grid-column: span 5; }
.xl\:col-6 { grid-column: span 6; }
.xl\:col-7 { grid-column: span 7; }
.xl\:col-8 { grid-column: span 8; }
.xl\:col-9 { grid-column: span 9; }
.xl\:col-10 { grid-column: span 10; }
.xl\:col-11 { grid-column: span 11; }
.xl\:col-12 { grid-column: span 12; }
}
J’éspère que ce tutoriel sur la création de grilles CSS responsives vous permettra de construire de la plus belle des manières, le layout de vos rêves (rien que ça)… 🌈