CSS Grid

Ce billet date de plusieurs années, ses informations peuvent être devenues obsolètes.

Après plus de 15 ans de hacks et de polémiques sur la mise en page CSS, ne voilà-t-il pas qu'en 2018 le CSS Grid Layout Module est quasiment disponible dans tous les navigateurs majeurs.

Si vous aimez l'histoire et les détails, allez lire CSS Grid Layout is Here to Stay et The Story of CSS Grid, from Its Creators.

Si vous cherchez un bon tutoriel, celui de MDN est bien. Il commence aussi à y avoir pas mal d'articles.

Ci-dessous vous trouverez mes notes sur le sujet. Et autant dire que pour les vétérans de la mise en page CSS, les cartes sont rebattues. Il faut se débarrasser des réflexes acquis au fil des années. Bienvenue dans un nouveau monde.

Je me répète mais cette spécification est toujours en Candidate Recommendation à l'heure de la rédaction de ce billet. Et sous ce statut, les choses peuvent encore changer !

Terminologie

Un grid container définit un contexte de formatage grid pour son contenu. Ses enfants deviennent des grid items.

Voici les principaux termes utilisés dans la spécification :

  • Grid line ou ligne de grille
  • Grid track ou piste de grille : une séquence continue entre 2 lignes adjacentes, autrement dit l'espace entre deux lignes d'une grille
  • Grid cell ou cellule de grille : un espace délimité par 4 lignes sans qu'aucune ligne ne passe à travers, la plus petite unité de la grille
  • Grid area ou région de grille : n'importe quel espace délimité par 4 lignes et composé d'une ou plusieurs cellules de grille

Création d'une grille basique

On commence par réfléchir en termes de pistes. Il s'agit de spécifier la taille des pistes de chacun des deux axes d'une grille : les rangées et les colonnes.

Pour cela on utilise sur l'élément grid container les propriétés grid-template-rows et grid-template-columns :

#grid {
    display: grid;
    grid-template-columns: 200px 50% 100px;
    grid-template-rows: 3em 80% 2em;
}

De cette définition de la taille des pistes découlent des lignes. Ces lignes sont numérotées automatiquement selon le sens de lecture du document en partant de 1 (pas de zéro). Il est possible de donner un ou plusieurs noms aux lignes de façon explicite :

#grid {
    display: grid;
    grid-template-columns: [start col-a] 200px [col-b] 50% [col-c] 100px [stop end last];
    grid-template-rows: [start masthead] 3em [content] 80% [footer] 2em [stop end];
}

Création d'une grille avec des régions

On peut définir une grille de façon plus visuelle avec une syntaxe qui s'approche un peu de l'auto-documentation.

Pour cela on utilise sur l'élément grid container la propriété grid-template-areas afin de créer des régions nommées. Sa syntaxe permet de visualiser la structure de la grille :

#grid {
    display: grid;
    grid-template-areas:
        "header   header    header    header"
        "left     content   content   right"
        "left     footer    footer    footer";
}

Des régions ainsi créées découlent des pistes. grid-template-areas fonctionne donc de concert avec grid-template-columns et grid-template-rows pour dimensionner ces pistes. Il existe un raccourci grid-template qui regroupe ces 3 propriétés.

Un point important à noter est que grid-template-areas crée implicitement des noms pour certaines lignes. il s'agit des lignes de départ et de fin de chaque région qui sont nommées avec le nom de la région auquel on ajoute les suffixes -start et -end. Et ce sur chacun des 2 axes de la région. Sur la base de l'exemple ci-dessus, certaines lignes de la région header qui s'étend sur 4 pistes obtiennent les noms implicites suivants :

  • la ligne 1 verticale se nomme header-start
  • la ligne 5 verticale se nomme header-end
  • la ligne 1 horizontale se nomme header-start
  • la ligne 2 horizontale se nomme header-end

Comme il est possible de donner plusieurs noms aux lignes, on peut toujours en ajouter des supplémentaires de façon explicite à l'aide des autres propriétés.

Positionner des éléments sur une grille

Pour positionner des éléments sur la grille, il faut réfléchir en termes de lignes. Pour cibler une ligne on se réfère à son numéro ou à son nom (implicite ou explicite).

On agit cette fois sur les éléments à placer eux-mêmes (les grid-items) en utilisant des propriétés qui servent à les attacher aux lignes de la grille :

  • grid-row-start et grid-row-end pour placer un élément sur les lignes allant de haut en bas
  • grid-column-start et grid-column-end pour placer un élément sur les lignes allant de gauche à droite
  • grid-row, raccourci pour grid-row-start / grid-row-end
  • grid-column, raccourci pour grid-column-start / grid-column-end
  • grid-area, raccourci pour grid-row-start / grid-column-start / grid-row-end / grid-column-end

Les valeurs des propriétés raccourcies sont séparées par le caractère / :

.one {
    grid-column: 1 / 5;
    grid-row: 1;
}

Quelques points intéressants :

  • les valeurs non spécifiées des propriétés raccourcies sont définies à auto
  • il est possible de compter les lignes dans l'ordre inverse en utilisant des valeurs négatives ; -1 désigne alors par exemple la première ligne disponible à droite pour grid-column et la première ligne en bas pour grid-row
  • il est possible d'utiliser le mot clé span pour spécifier combien de pistes de grille (grid tracks) doit occuper un élément de grille (grid-item)
  • l'ordre de grid-area est haut / gauche / bas / droite, ce qui est conflictuel avec l'ordre traditionnel comme celui de padding qui est haut / droite / bas / gauche ; c'est très bizarre…

Le raccourci grid-area prend tout son sens quand le conteneur grille est déclaré avec des régions. Son fonctionnement rend les déclarations suivantes équivalentes :

grid-area: footer-start / footer-start / footer-end / footer-end;
grid-area: footer / footer / footer / footer;
grid-area: footer;

Du coup grid-area permet de se référer à une région avec une syntaxe limpide :

#grid {
    display: grid;
    grid-template-areas:
        "header   header   header   header"
        "nav      main     main     main"
        "nav      footer   footer   footer";
}
#header { grid-area: header; }
#nav    { grid-area: nav; }
#main   { grid-area: main; }
#footer { grid-area: footer; }

On a l'impression de manipuler des régions alors qu'on touche toujours aux lignes.

Définir la taille des pistes avec l'unité fr

L'unité fr représente une fraction de l'espace vacant dans un grid container et permet de créer des pistes de grilles flexibles. On l'utilise avec les propriétés grid-template-columns et grid-template-rows :

grid-template-columns: 1fr 1fr 1fr 1fr;

Pour déterminer la valeur d'un fr, l'espace disponible du grid container est divisé par le nombre de fr utilisés :

width: 100em;
grid-template-columns: 10em 4fr 3fr 20em;

=>  100em - 10em - 20em = 70em
    70em / (4fr + 3fr) = 10
    10em 40em (4fr * 10) 30em (3fr * 10) 20em

C'est très utile quand on a certaines colonnes qui doivent avoir une longueur fixe et d'autres qui doivent être flexibles.

C'est plus pratique d'utilisation que les % car parfois des largeurs à 100% avec des marges supplémentaires génèrent un overflow.

L'expression repeat()

L'expression repeat() permet de répéter un fragment des pistes d'une grille. On l'utilise avec les propriétés grid-template-columns et grid-template-rows :

/* Répéter 10 fois une colonne tous les 6 ems. */
#grid {
    display: grid;
    grid-template-columns: repeat(10, 6em);
}

/* Répéter 4 fois un même motif. */
#grid {
    display: grid;
    grid-template-columns: repeat(4, 1em 1fr 1fr);
}

/* Il est possible d'ajouter des valeurs avant ou après l'expression `repeat()`. */
#grid {
    display: grid;
    grid-template-columns: repeat(4, 1em 1fr 1fr) 2em;
}

Ajuster la taille des pistes en fonction du contenu

Il est possible de donner une taille à une piste en fonction de la taille intrinsèque de son contenu avec les valeurs min-content ou max-content pour les propriétés grid-template-columns et grid-template-rows. C'est utile quand par exemple on ne connaît pas d'avance la taille du contenu des grid items.

Ces valeurs sont décrites dans une autre spécification CSS (encore à l'état de Working Draft).

Elles permettent de définir une taille "idéale" en fonction du contenu.

min-content obtiendra la même taille que l'élément dont la taille intrinsèque est la plus petite au delà de laquelle il n'est plus possible de réduire la taille sans couper ou masquer cet élément.

max-content obtiendra la même taille que l'élément dont la taille intrinsèque est la plus grande.

C'est plus parlant avec un exemple.

La fonction minmax(min, max)

Cette fonction permet de définir une taille de piste en incluant une valeur minimale et une valeur maximale. On l'utilise avec les propriétés grid-template-columns et grid-template-rows.

Je vous conseille d'aller lire How the minmax() Function Works pour comprendre comment elle fonctionne.

Il faut noter que l'unité fr n'est pas autorisée en tant que valeur min de minmax(min, max).

Pour aller plus loin

Je m'arrête là pour le moment mais il me reste pas mal de points à creuser :

Mais ça sera pour une autre fois :)

Avant CSS et CORS en local Après Utiliser cssnext vite fait bien fait

Tag Kemar Joint