La Cascade

Rechercher

Grille CSS et formes personnalisées, 1

par Temani Afif, 28 août 2022, cssgrid, css, article original paru le 15 août 2022 dans CSS Tricks


Dans un article précédent, j'ai examiné la capacité de CSS Grid à créer des mises en page complexes en utilisant ses pouvoirs d'auto-placement. Je suis allé plus loin dans un autre article qui ajoutait un effet de zoom au survol des images dans une disposition en grille. À présent, je veux me plonger dans un autre type de grille, celle qui fonctionne avec des formes.

Par exemple, que faire si les images ne sont pas parfaitement carrées mais ont plutôt la forme d'hexagones ou de losanges ? spoiler : nous pouvons le faire. En fait, nous allons combiner les techniques de grille CSS que nous avons déjà examinées et ajouter un peu de magie CSS de type clip-path et mask pour créer des grilles d'images fantaisistes pour pratiquement toutes les formes que vous pouvez imaginer !

Commençons par un balisage

La plupart des mises en page que nous allons examiner peuvent sembler faciles à réaliser à première vue, mais le défi consiste à les réaliser avec le même balisage HTML. Nous pouvons utiliser beaucoup de wrappers, de divs et autres, mais l'objectif de cet article est d'utiliser la même quantité de code HTML, et la plus petite possible, pour obyenir toutes les différentes grilles. Après tout, qu'est-ce que CSS sinon un moyen de séparer le style et le balisage ? Notre style ne doit pas dépendre du balisage, et vice versa.

Ceci étant dit, commençons :

<div class="gallery">
  <img src="..." alt="..." />
  <img src="..." alt="..." />
  <img src="..." alt="..." />
  <img src="..." alt="..." />
  <!-- autant de fois que l'on veut -->
</div>

Tout ce dont nous avons besoin ici, c'est d'un conteneur avec des images. Rien de plus !

Grille CSS d'hexagones

Cette grille est aussi parfois appelée "nid d'abeille".

voir Honeycomb image gallery de Temani Afif dans CodePen

Il y a déjà beaucoup d'autres articles de blog qui montrent comment faire cela. J'en ai même écrit un ici sur CSS-Tricks ! Cet article est toujours bon et explique en détail comment créer une mise en page réactive. Mais pour ce cas précis, nous allons nous appuyer sur une approche CSS beaucoup plus simple.

Tout d'abord, utilisons le clip-path sur les images pour créer la forme hexagonale et plaçons-les toutes dans la même zone de grille pour qu'elles se chevauchent.

.gallery {
  --s : 150px ; /* contrôle la taille */
  display : grid ;
}

.gallery > img {
  grid-area : 1/1 ;
  width : var(--s) ;
  aspect-ratio : 1.15 ;
  object-fit : couverture ;
  clip-path : polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%) ;
}
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%)

Rien de bien original pour l'instant. Toutes les images sont des hexagones et sont superposées. Il semble que nous n'ayons qu'un seul élément d'image en forme d'hexagone, mais en réalité il y en a sept.

voir Untitled de Temani Afif dans CodePen

L'étape suivante consiste à appliquer une translation aux images pour les placer correctement sur la grille.

Remarquez que nous voulons toujours qu'une des images reste au centre. Les autres sont placées autour d'elle en utilisant CSS translate et la bonne vieille géométrie. Voici les formules que j'ai créées pour chaque image de la grille :

translate((height + gap)*sin(0deg), (height + gap)*cos(0))
translate((height + gap)*sin(60deg), (height + gap)*cos(60deg))
translate((height + gap)*sin(120deg), (height + gap)*cos(120deg))
translate((height + gap)*sin(180deg), (height + gap)*cos(180deg))
translate((height + gap)*sin(240deg), (height + gap)*cos(240deg))
translate((height + gap)*sin(300deg), (height + gap)*cos(300deg))

Quelques calculs et optimisations plus tard (sautons cette partie ennuyeuse, d'accord ?), nous obtenons le CSS suivant :

.gallery {
  --s: 150px; /* contrôle de la taille */
  --g: 10px; /* contrôle de l'espacement */
  display: grid;
}
.gallery > img {
  grid-area: 1/1;
  width: var(--s);
  aspect-ratio: 1.15;
  object-fit: cover;
  clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%);
  transform: translate(var(--_x, 0), var(--_y, 0));
}
.gallery > img:nth-child(1) {
  --_y: calc(-100% - var(--g));
}
.gallery > img:nth-child(7) {
  --_y: calc(100% + var(--g));
}
.gallery > img:nth-child(3),
.gallery > img:nth-child(5) {
  --_x: calc(-75% - 0.87 * var(--g));
}
.gallery > img:nth-child(4),
.gallery > img:nth-child(6) {
  --_x: calc(75% + 0.87 * var(--g));
}
.gallery > img:nth-child(3),
.gallery > img:nth-child(4) {
  --_y: calc(-50% - 0.5 * var(--g));
}
.gallery > img:nth-child(5),
.gallery > img:nth-child(6) {
  --_y: calc(50% + 0.5 * var(--g));
}

👉🏾 Ce sera peut-être plus facile lorsque nous aurons de vraies fonctions de trigonométrie en CSS !

Chaque image est traduite par les variables --_x et --_y qui sont basées sur ces formules. Seule la deuxième image (nth-child(2)) est indéfinie dans tout sélecteur car c'est celle qui se trouve au centre. Il peut s'agir de n'importe quelle image si vous décidez d'utiliser un ordre différent. Voici l'ordre que j'utilise :

Avec seulement quelques lignes de code, nous obtenons une super grille d'images. À cela, j'ai ajouté un petit effet de survol aux images pour rendre les choses plus fantaisistes.

Et devinez quoi ? Nous pouvons obtenir une autre grille hexagonale en mettant simplement à jour quelques valeurs.

voir Another hexagon grid de Temani Afif dans CodePen

Si vous vérifiez le code et le comparez avec le précédent, vous remarquerez que j'ai simplement interverti les valeurs à l'intérieur de clip-path et que j'ai permuté entre --x et --y. C'est tout !

Grille CSS de losanges

Ce losange est juste un carré qui a subi une rotation de 45 degrés.

voir Rhombus image gallery de Temani Afif dans CodePen

Même HTML, vous vous souvenez ? Nous commençons par définir une grille d'images 2×2 en CSS :

.gallery {
  --s: 150px; /* contrôle la taille */

  display: grid;
  gap: 10px;
  grid: auto-flow var(--s) / repeat(2, var(--s));
  place-items: center;
}
.gallery > img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
}

La première chose qui pourrait attirer votre attention est la propriété grid. Elle n'est pas souvent utilisée, mais elle est très utile car il s'agit d'un raccourci qui nous permet de définir une grille complète en une seule déclaration. Ce n'est pas la propriété la plus intuitive — sans parler de la lisibilité — mais nous sommes ici pour apprendre et découvrir de nouvelles choses, alors utilisons-la plutôt que d'écrire toutes les propriétés individuelles de la grille.

grid: auto-flow var(--s) / repeat(2, var(--s));
/* est équivalent à ceci : */
grid-template-columns: repeat(2, var(--s));
grid-auto-rows: var(--s);

Cela définit deux colonnes de largeur égale à la variable --s et fixe la hauteur de toutes les lignes à --s également. Comme nous avons quatre images, nous obtiendrons automatiquement une grille de 2×2.

Voici une autre façon dont nous aurions pu l'écrire :

grid-template-columns: repeat(2, var(--s));
grid-template-rows: repeat(2, var(--s));

...ce qui peut être réduit avec le raccourci grid :

grid: repeat(2, var(--s)) / repeat(2, var(--s));

Après avoir défini la grille, nous la faisons pivoter ainsi que les images avec des transformations CSS et nous obtenons ceci :

voir Untitled de Temani Afif dans CodePen

Notez comment je les fais pivoter tous les deux de 45deg, mais dans la direction opposée.

.gallery {
  /* etc. */
  transform: rotate(45deg);
}
.gallery > img {
  /* etc. */
  transform: rotate(-45deg);
}

La rotation des images dans le sens négatif les empêche de subir une rotation avec la grille, elles restent donc droites. Maintenant, nous appliquons un clip-path pour découper une forme de losange à partir des images.

clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%)
voir Untitled de Temani Afif dans CodePen

Nous avons presque terminé ! Nous devons rectifier la taille des images pour qu'elles s'emboîtent. Sinon, elles sont très espacées au point que cela ne ressemble pas à une grille d'images.

L'image se trouve à l'intérieur de la limite du cercle vert, qui est le cercle inscrit de la zone de la grille où l'image est placée. Ce que nous voulons, c'est agrandir l'image pour qu'elle rentre dans le cercle rouge, qui est le cercle circonscrit de la zone de la grille.

Ne vous inquiétez pas, je ne présenterai pas de géométrie plus ennuyeuse. Tout ce que vous devez savoir, c'est que le rapport entre le rayon de chaque cercle est la racine carrée de 2 (sqrt(2)). C'est la valeur dont nous avons besoin pour augmenter la taille de nos images afin de remplir la zone. Nous utiliserons 100%*sqrt(2) = 141% et le tour est joué !

.gallery {
  --s: 150px; /* contrôle de la taille */

  display: grid;
  grid: auto-flow var(--s) / repeat(2, var(--s));
  gap: 10px;
  place-items: center;
  transform: rotate(45deg);
}
.gallery > img {
  width: 141%; /* 100%*sqrt(2) = 141% */
  aspect-ratio: 1;
  object-fit: cover;
  transform: rotate(-45deg);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}

Comme pour la grille hexagonale, nous pouvons rendre les choses plus fantaisistes avec ce bel effet de survol de zoom :

voir Rhombus image gallery de Temani Afif dans CodePen

Grille CSS de formes triangulaires

voir Triangular grid of images de Temani Afif dans CodePen

Vous savez probablement maintenant que la grande astuce consiste à déterminer le clip-path pour obtenir les formes que nous voulons. Pour cette grille, chaque élément a sa propre valeur de clip-path alors que les deux dernières grilles utilisaient des formes cohérentes. Donc, cette fois-ci, c'est comme si nous travaillions avec plusieurs formes triangulaires différentes qui s'assemblaient pour former une grille rectangulaire d'images.

Les trois images en haut.
Les trois images en bas.

Nous les plaçons dans une grille 3×2 avec le CSS suivant :

.gallery {
  display : grid ;
  gap : 10px ;
  grid-template-columns : auto auto auto ; /* 3 colonnes */
  place-items : center ;
}
.gallery > img {
  width : 200px ; /* contrôle la taille */
  aspect-ratio : 1 ;
  object-fit : cover ;
}
/* les valeurs de clip-path */
.gallery > img:nth-child(1) { clip-path : polygon(0 0, 50% 0, 100% 100% ,0 100%) ; }
.gallery > img:nth-child(2) { clip-path : polygon(0 0, 100% 0, 50% 100%) ; }
.gallery > img:nth-child(3) { clip-path : polygon(50% 0, 100% 0, 100% 100%, 0 100%) ; }
.gallery > img:nth-child(4) { clip-path : polygon(0 0, 100% 0, 50% 100%, 0 100%) ; }
.gallery > img:nth-child(5) { clip-path : polygon(50% 0, 100% 100%, 0% 100%) ; }
.gallery > img:nth-child(6) { clip-path : polygon(0 0, 100% 0 ,100% 100%, 50% 100%) ; } } }

Voici ce que nous obtenons :

voir Untitled de Temani Afif dans CodePen

La touche finale consiste à rendre la largeur de la colonne centrale égale à 0 pour se débarrasser des espaces entre les images. C'est le même genre de problème d'espacement que nous avions avec la grille de losanges, mais avec une approche différente pour les formes que nous utilisons maintenant :

grid-template-columns: auto 0 auto;

J'ai dû jouer avec les valeurs de clip-path pour m'assurer qu'elles s'emboîtent toutes comme un puzzle. Les images originales se chevauchent lorsque la colonne du milieu a une largeur nulle, mais après avoir découpé les images, l'illusion est parfaite :

Grille CSS en pizza

Devinez quoi ? Nous pouvons obtenir une autre grille cool en ajoutant simplement border-radius et overflow à notre grille ou à nos formes triangulaires. 🎉

voir Pizza-like grid de Temani Afif dans CodePen

Grille CSS de pièces de puzzle

Cette fois, nous allons jouer avec la propriété CSS mask pour que les images ressemblent aux pièces d'un puzzle.

voir Puzzle-like grid of images de Temani Afif dans CodePen

Note : si vous n'avez pas encore utilisé mask avec les dégradés CSS, je vous recommande vivement cet autre article que j'ai écrit sur le sujet car il vous aidera pour la suite. Pourquoi des dégradés ? Parce que c'est ce que nous utilisons pour obtenir les encoches rondes dans les formes des pièces du puzzle.

La configuration de la grille devrait être un jeu d'enfant maintenant, alors concentrons-nous plutôt sur la partie masque.

voir Untitled de Temani Afif dans CodePen

Comme illustré dans la démo ci-dessus, nous avons besoin de deux dégradés pour créer la forme finale. Un dégradé crée un cercle (la partie verte) et l'autre crée la bonne courbe tout en remplissant la partie supérieure.

--g: 6px; /* contrôle le gap */
--r: 42px; /* contrôle les formes circulaires */

background: radial-gradient(
    var(--r) at left 50% bottom var(--r),
    green 95%,
    #0000
  ),
  radial-gradient(
      calc(var(--r) + var(--g)) at calc(100% + var(--g)) 50%,
      #0000 95%,
      red
    ) top/100% calc(100% - var(--r)) no-repeat;

Deux variables contrôlent la forme. La variable --g n'est rien d'autre que la gouttière de la grille. Nous devons tenir compte de celle-ci pour placer correctement nos cercles afin qu'ils se chevauchent parfaitement lorsque le puzzle entier est assemblé. La variable --r contrôle la taille des parties circulaires de la forme du puzzle.

Maintenant, nous prenons le même CSS et mettons à jour quelques valeurs dans celui-ci pour créer les trois autres formes :

voir Untitled de Temani Afif dans CodePen

Nous avons les formes, mais pas les bords superposés dont nous avons besoin pour les faire s'emboîter. Chaque image est limitée à la cellule de la grille dans laquelle elle se trouve, il est donc logique que les formes soient en quelque sorte mélangées pour le moment :

Nous devons créer un débordement en augmentant la hauteur/largeur des images. D'après la figure ci-dessus, nous devons augmenter la hauteur de la première et de la quatrième image tandis que nous augmentons la largeur de la deuxième et de la troisième. Vous avez probablement déjà deviné que nous devons les augmenter en utilisant la variable --r.

.gallery > img:is(:nth-child(1), :nth-child(4)) {
  width: 100%;
  height: calc(100% + var(--r));
}
.gallery > img:is(:nth-child(2), :nth-child(3)) {
  height: 100%;
  largeur: calc(100% + var(--r));
}

On s'approche !

voir Untitled de Temani Afif dans CodePen

Nous avons créé le chevauchement mais, par défaut, nos images se chevauchent soit à droite (si nous augmentons la largeur), soit en bas (si nous augmentons la hauteur). Mais ce n'est pas ce que nous voulons pour les deuxième et quatrième images. La solution est d'utiliser place-self: end sur ces deux images et notre code complet devient :

voir Untitled de Temani Afif dans CodePen

Voici un autre exemple où j'utilise un dégradé conique au lieu d'un dégradé radial. Cela nous donne des pièces de puzzle triangulaires tout en gardant les mêmes HTML et CSS sous-jacents.

voir Puzzle Grid of images de Temani Afif dans CodePen

Un petit dernier pour la route ! Cette fois-ci, j'utilise le clip-path, et comme il s'agit d'une propriété que nous pouvons animer, nous obtenons un survol cool en mettant simplement à jour la propriété personnalisée qui contrôle la forme.

Voir la liste des articles de Temani Afif traduits dans La Cascade.
Article original paru le 15 août 2022 dans CSS Tricks
Traduit avec l'aimable autorisation de CSS Tricks et de Temani Afif.
Copyright CSS Tricks © 2022