Loupe

CSS Grid Layout : Créer une mise en page responsive

Historique et présentation

La mise en page avec CSS est ce qui a toujours fait râler les personnes ayant touché à ce langage de près ou de loin. La création d'un gabarit peut parfois ressembler à la construction d'un château de cartes, où la moindre propriété CSS mal placée ou mal utilisée peut faire perdre des heures de travail (et quelques cheveux aussi) 💀.

Depuis plusieurs années maintenant, nos UI Designers utilisent des grilles pour faire leurs maquettes graphiques. Malheureusement cette façon de procéder n'avait jamais trouvé d'équivalent avec CSS. Historiquement, nous nous sommes toujours débrouillés avec des <table> (çaylemäl), des floats, des inline-block et plus récemment avec des flexbox. Cependant, aucune de ces techniques n'était réellement faite pour construire des gabarits à proprement dit. Il s'agissait même parfois de rustines / de hacks, toujours en attendant mieux. 

C'est pourquoi, la spécification CSS Grid Layout (ou simplement Grid) était attendue depuis plusieurs années avec impatience. Elle jouit aujourd'hui d'un support navigateur plutôt bon, du coup c'est certainement le bon moment pour se lancer et créer vos premières mises en page avec 🤔. 

Compatibilité navigateurs pour CSS Grid

Le but de cet article est de comprendre les étapes de construction d'un gabarit simple avec CSS Grid Layout. Je conseille vivement la lecture du guide complet sur la spécification de l'excellent Chris Coyier au préalable ✔.

Une page Web est normalement composée de régions / zones à placer sur des lignes, des colonnes ou bien les 2 à la fois. Là ou Flexbox permet de faire une mise en page uni-dimensionnelle (row OU column), Grid permet d'agir de manière bi-dimensionnelle (row ET column). C'est donc LA Technique idéale pour répondre à notre besoin.

Création de la grille

Nous souhaitons réaliser la mise en page suivante, basée sur 6 colonnes :

Mise en page souhaitée

Commençons par créer notre structure HTML :

<body>
  <header class="logo box">
    Logo
  </header>

  <nav class="menu box">
    Menu
  </nav>

  <div class="breadcrumb box">
    Fil d'Ariane
  </div>

  <main class="main">
    <article class="box">
      Article 1
    </article>
    <article class="box">
      Article 2
    </article>
    <article class="box">
      Article 3
    </article>
    <article class="box">
      Article 4
    </article>
  </main>

  <aside class="sidebar box">
    Sidebar
  </aside>

  <footer class="footer box">
    Footer
  </footer>
</body>

Et quelques lignes de CSS pour démarrer :

body
{
  padding: 20px;
}

.box
{
  background: #333;
  color: #fff;
  padding: 20px;
}

La première chose à faire est de définir notre Grid Container. Nous devons donc déclarer 6 colonnes de largeur égale et 4 lignes de hauteur libre. De plus nous avons des espacements de 20px entre chaque colonne et chaque ligne : 

body
{
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(4, auto);
  grid-gap: 20px;
}

Les propriétés grid-template-columns et grid-template-rows permettent respectivement de déclarer les colonnes et les lignes, grid-gap permet de définir les espacements. Etant donné que nous souhaitons la même largeur pour toutes les cellules, nous utilisons la fonction repeat pour boucler sur le nombre qui nous intéresse. L'unité fr représente une fraction de l'espace à occuper par la cellule (à la manière des flexbox avec flex: 1). C'est une unité pratique car sa valeur est automatiquement calculée par le navigateur en fonction de la largeur ou de la hauteur disponible dans le Container

Par défaut, les Grid Items (enfants directs du Grid Container) viennent chacun leur tour remplir les cases de la grille (l'ordre d'apparition dans le code HTML est alors respecté). Dans notre cas, nous avons 6 Grid Items (logo, menu, fil d'Ariane, contenu principal, sidebar et footer) et la totalité de la grille devrait être remplie. Voici ce que nous obtenons :

Définition du layout

Pour l'exemple, si nous avions voulu déclarer 3 colonnes respectivement de 100px, 50% et auto (largeur libre), nous aurions écrit :

body
{
  grid-template-columns: 100px 50% auto;
}

Il est possible de mélanger les unités et laisser le navigateur se débrouiller pour faire en sorte que la mise en page tienne. Et ça, c'est plutôt cool ! ✌

Positionnement des Grid Items

Nous souhaitons maintenant positionner nos Grid Items dans les bonnes cases avec les bonnes dimensions. Voici les nouvelles règles CSS à créer :

.menu
{
  grid-column: span 5;
}

.breadcrumb
{
  grid-column: 1 / -1;
}

.main
{
  grid-column: span 4;
}

.sidebar
{
  grid-column: span 2;
}

.footer
{
  grid-column: 1 / -1;
}

Pour le moment, nous n'agissons que sur le positionnement par rapport aux colonnes de notre layout grâce à la propriété grid-column (qui est un raccourci pour grid-column-start et grid-column-end) placée sur chacun de nos Grid Items. Cette propriété permet tout simplement de définir une colonne de départ / une colonne d'arrivée ou bien une plage à occuper par le Grid Item (NB : Les index des colonnes commencent à 1 et non à 0).

  • Le logo étant déjà correctement positionné (1ère colonne, 1ère ligne), nous ne le modifions pas.
  • Le menu, doit occuper le reste de la 1ère ligne. Nous lui affectons donc 5 colonnes grâce au mot-clé span. N'ayant plus de place pour se loger sur la 1ère ligne, les Grid Items suivants sont automatiquement renvoyés à la suivante.
  • Le fil d'Ariane doit occuper une ligne complète, c'est pourquoi nous le faisons démarrer à la 1ère colonne et le faisons terminer à la dernière en utilisant un chiffre négatif qui permet de compter à partir de la fin. Nous aurions pu écrire span 6 mais c'était pour utiliser une autre syntaxe.
  • Pour le contenu principal et la sidebar, nous partons sur une répartition 2/3 - 1/3 avec respectivement span 4 et span 2.
  • Le footer reprend les mêmes propriétés que le fil d'Ariane.

Nous obtenons le résultat suivant :

Mise en page réparée

Et si on pimentait un peu les choses ?

Le cas que nous avons vu est idéal. Les Grids Items apparaissent dans le code HTML dans l'ordre qui nous convient le mieux et nous n'avons qu'à agir sur les colonnes. Rêvons plus grand et supposons maintenant que le code HTML est le suivant :

<body>
  <footer class="footer box">
    Footer
  </footer>
  
  <aside class="sidebar box">
    Sidebar
  </aside>

  <nav class="menu box">
    Menu
  </nav>
  
  <header class="logo box">
    Logo
  </header>

  <main class="main">
    <article class="box">
      Article 1
    </article>
    <article class="box">
      Article 2
    </article>
    <article class="box">
      Article 3
    </article>
    <article class="box">
      Article 4
    </article>
  </main>
  
  <div class="breadcrumb box">
    Fil d'Ariane
  </div>
</body>

A première vue, notre mise en page est tellement déformée qu'il faudrait tout recommencer 😭 :

C'est tout cassé

D'où venons nous ? Où allons-nous ? J'ignore de le savoir. Mais ce que je n'ignore pas de le savoir, c'est que le bonheur réside dans la spécification de Grid. Eh oui ! Nous allons uniquement utiliser la propriété grid-row pour rétablir l'ordre naturel des choses. Voici donc le code CSS mis à jour :

.logo
{
  grid-column: 1;
  grid-row: 1;
}

.menu
{
  grid-column: 2 / span 5;
  grid-row: 1;
}

.breadcrumb
{
  grid-column: 1 / -1;
  grid-row: 2;
}

.main
{
  grid-column: 1 / span 4;
  grid-row: 3;
}

.sidebar
{
  grid-column: 5 / span 2;
  grid-row: 3;
}

.footer
{
  grid-column: 1 / -1;
  grid-row: 4;
}

A l'instar de grid-column, la propriété grid-row (qui est un raccourci pour grid-row-start et grid-row-end) permet de positionner les Grids Items par rapport à des lignes avec le même principe (un début / une fin ou une plage à occuper). Dans notre cas, seule la ligne de début est nécessaire pour remettre les éléments sur la bonne rangée.

Le menu apparaissant avant le logo et la sidebar avant le contenu principal dans le nouveau code HTML, vous noterez les légères retouches apportées aux propriétés grid-column de ces éléments pour notamment spécifier les colonnes de début 😛.

Imbrication de Grids

Vous l'aurez noté, nous n'avons pas encore correctement positionné les articles à l'intérieur du contenu principal. C'est parce que nous avons besoin de repartir sur une nouvelle Grid de 2 colonnes de largeur égale ainsi qu'un nombre de ligne indéterminé (puisqu'il peut potentiellement y avoir une infinités d'articles). Nous allons donc étendre les possibilités du Grid Item concerné en le transformant en Grid Container :

.main
{
  grid-column: 1 / span 4;
  grid-row: 3;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: minmax(100px, auto);
  grid-gap: 20px;
}

En plus des autres propriétés que nous avons déjà vues la propriété grid-auto-rows fait son apparition. Elle permet de définir une hauteur pour toutes les lignes "auto-générées". Nous avons également utilisé la fonction minmax qui nous permet de définir une dimension minimum et maximum pour nos lignes (les articles feront donc au minimum 100px de haut). 

Résultat final

Nous avons donc atteint notre but : Faire une mise en page avec Grid indépendante de la structure HTML. 😍

Besoin d'une syntaxe alternative ?

Depuis le début, nous travaillons avec une certaine syntaxe / méthode : nous déclarons un Grid Container et son template (colonnes / lignes) et positionnons les Grid Items "à la main" en spécifiant des pseudo-coordonnées (grid-column et grid-row). Il existe une autre manière de procéder qui peut s'avérer être plus élégante à écrire dans certains cas en utilisant les Grid Areas. Nous allons modifier le code CSS de la manière suivante :

body
{
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(4, auto);
  grid-gap: 20px;
  grid-template-areas:  "logo menu menu menu menu menu"
                        "breadcrumb breadcrumb breadcrumb breadcrumb breadcrumb breadcrumb"
                        "main main main main sidebar sidebar"
                        "footer footer footer footer footer footer";
}

.logo
{
  grid-area: logo;
}

.menu
{
  grid-area: menu;
}

.breadcrumb
{
  grid-area: breadcrumb;
}

.main
{
  grid-area: main;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: minmax(100px, auto);
  grid-gap: 20px;
}

.sidebar
{
  grid-area: sidebar;
}

.footer
{
  grid-area: footer;
}

Il y a 2 différences notables : Tout d'abord nous donnons des noms de zone aux Grid Items avec la propriété grid-area. Ensuite nous utilisons ces noms dans la propriété grid-template-areas au niveau du Grid Container. Cette dernière propriété est une représentation visuelle de l'occupation des zones au sein de la grille. En guise de valeur, nous devons fournir un ensemble de lignes contenant la zone à afficher par colonne. 

C'est une syntaxe assez pratique lorsque nous travaillons sur un nombre de colonnes peu élevé. Si vous êtes sur une grille à 12 colonnes comme Bootstrap, privilégiez la précédente méthode pour éviter de répéter les noms des zones. 😉   

Et le responsive design dans tout ça ?

Le gros avantage de la mise en page avec Grid est le fait que le code HTML se retrouve très allégé. Fini les imbrications de <div> avec des classes .row et .col-xxx qui ajoutaient de la complexité à la structure HTML et qui étaient surtout utilisées pour un besoin purement graphique. Maintenant, tout se positionne presque par magie (ou grâce au talent de l'intégrateur). En plus de cela, l'autre avantage est que les Grid Items peuvent s'affranchir de leur ordre d'apparition dans la page HTML.

Généralement, plus le code HTML est léger, plus facile est le Responsive Design. Changer de mise en page ne représente aucune difficulté car cela se fait en modifiant les propriétés grid-template-columnsgrid-template-rows et grid-template-areas. Supposons que nous souhaitons partir sur 1 colonne et 6 lignes pour le format mobile, nous modifions le Grid Container comme suit :

body
{
  display: grid;
  grid-gap: 20px;
  grid-template-columns: 1fr;
  grid-template-rows: repeat(6, auto);
  grid-template-areas:  "logo"
                        "menu"
                        "breadcrumb"
                        "main"
                        "sidebar"
                        "footer";
}

Nous obtenons maintenant la mise en page suivante :

Mise en page mobile

Et c'est tout ? Oui, c'est tout 💪

Conclusion

Voici le fichier CSS final :

body
{
  display: grid;
  grid-gap: 20px;
  grid-template-columns: 1fr;
  grid-template-rows: repeat(6, minmax(50px, auto));
  grid-template-areas:  "logo"
                        "menu"
                        "breadcrumb"
                        "main"
                        "sidebar"
                        "footer";
}

.box
{
  background: #333;
  color: #fff;
  padding: 10px;
}

.logo
{
  grid-area: logo;
}

.menu
{
  grid-area: menu;
}

.breadcrumb
{
  grid-area: breadcrumb;
}

.main
{
  grid-area: main;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: minmax(100px, auto);
  grid-gap: 20px;
}

.sidebar
{
  grid-area: sidebar;
}

.footer
{
  grid-area: footer;
}

@media screen and (min-width: 768px)
{
  body
  {
    grid-template-columns: repeat(6, 1fr);
    grid-template-rows: repeat(4, minmax(100px, auto));
    grid-template-areas:  "logo menu menu menu menu menu"
                          "breadcrumb breadcrumb breadcrumb breadcrumb breadcrumb breadcrumb"
                          "main main main main sidebar sidebar"
                          "footer footer footer footer footer footer";
  }
}

Nous avons pu voir comment construire pas à pas une mise en page avec CSS Grid Layout. Les avantages de cette méthode sont réels (HTML allégé, passage au Responsive Design facilité, syntaxe simple et très visuelle, ...) et répondent à un vrai besoin graphique et technique 🏆

Evidemment, les possibilités de Grid ne s'arrêtent pas à notre exemple, loin de là. Tout comme Flexbox, Grid permet très facilement de gérer les alignements verticaux et horizontaux au niveau du Container et des Items avec justify-* et align-* par exemple.

Si votre projet et vos navigateurs cibles le permettent, alors n'hésitez pas, faites des Grid !

Quelques liens utiles / complémentaires sur le sujet :

Have fun !

❤ Made with CSS & love.

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus