Grille CSS et formes personnalisées, 1
Combinez les techniques de grille CSS et ajoutez-y un peu de magie clip-path et de masque CSS pour créer des grilles d'images fantaisistes pour pratiquement toutes les formes que vous pouvez imaginer !
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".
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%) ;
}
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.
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.
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.
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 :
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.
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 :
Grille CSS de formes triangulaires
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.
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 :
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. 🎉
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.
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.
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 :
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 !
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 :
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.
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.