Grid to List Toggle
Bascule fluide grille ↔ liste avec technique FLIP — les items se repositionnent par animation physique, révélant les libellés au passage.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Cartes comprend 41 effets, dont 6 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Le conteneur utilise CSS Grid : en mode grille, grid-template-columns: repeat(3, 1fr) dispose 6 items sur 2 rangées. Un clic ajoute la classe .list-mode qui bascule sur grid-template-columns: 1fr — une colonne pleine largeur. Problème : grid-template-columns n'est pas une propriété interpolable dans les navigateurs actuels, donc CSS ne peut pas animer ce changement. C'est le JS qui prend le relais avec la technique FLIP.
La technique FLIP (First-Last-Invert-Play) opère en quatre phases sur chaque .grid-list-item : First — lire les coordonnées courantes avec getBoundingClientRect() ; Last — appliquer la classe .list-mode (le layout Grid recalcule immédiatement) ; Invert — calculer le delta δx/δy entre ancienne et nouvelle position, puis forcer transform: translate(δx, δy) sans transition pour ramener l'item à son emplacement visuel d'origine ; Play — dans un requestAnimationFrame, réactiver la transition CSS et retirer le transform, laissant l'item glisser vers sa vraie destination.
En parallèle, deux micro-transitions CSS s'opèrent sur chaque item : le libellé (.item-text) passe de opacity: 0; max-width: 0 en grille à opacity: 1; max-width: 200px en liste, se déroulant depuis l'icône ; l'icône (.item-icon) réduit de 32 × 32 px à 24 × 24 px. Ces deux transitions partagent le même cubic-bezier(.4, 0, .2, 1) que le FLIP, synchronisant visuellement l'entrée du texte avec le déplacement de l'item.
La fonction setGridList(bouton, mode) est déclarée en global (hors IIFE) pour rester appelable depuis les attributs onclick HTML. Elle retrouve le groupe de toggles via t.parentElement.querySelectorAll('.morph-toggle') pour mettre à jour l'état .active — ce qui permet de câbler plusieurs instances sur la même page en leur fournissant chacune un conteneur cible distinct.
Accessibilité
- prefers-reduced-motion absent : ni le CSS ni le JS ne vérifient cette préférence OS — l'animation FLIP et les transitions
max-width/opacitys'exécutent même si l'utilisateur a activé « Réduire le mouvement ». À ajouter avant un usage en production accessible. - Les boutons toggle sont de vrais
<button>HTML — activables au clavier (Tab, Entrée, Espace) sans JS supplémentaire. - Aucun
aria-pressedsur les boutons toggle : l'état actif (classe.active) n'est pas exposé aux lecteurs d'écran. - Le conteneur
.grid-list-containerne porte niroleniaria-label— la sémantique de la région n'est pas déclarée. - La couleur
#a5b4fcdes libellés visibles en mode liste sur fond sombre (~rgba(99,102,241,.15)sur#0a0a0f) atteint un ratio de contraste ≥ 5:1 — WCAG AA satisfait pour le texte normal.
Compatibilité navigateur
Nécessite CSS Grid Level 1 et requestAnimationFrame — supportés dans tous les navigateurs modernes. La propriété grid-template-columns n'est pas interpolable nativement : la fluidité repose entièrement sur la technique FLIP côté JS.
Si JavaScript est désactivé, les boutons toggle ne fonctionnent pas et la vue reste figée en mode grille par défaut — le contenu reste lisible et la mise en page ne casse pas.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="demo-preview">
<div class="morph-toggle-group">
<button class="morph-toggle active" onclick="setGridList(this, 'grid')">Grid</button>
<button class="morph-toggle" onclick="setGridList(this, 'list')">List</button>
</div>
<div class="grid-list-container" id="gridListDemo">
<div class="grid-list-item">
<div class="item-icon"></div>
<span class="item-text">Label</span>
</div>
<!-- répétez .grid-list-item autant de fois que nécessaire -->
</div>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
| --primary | #6366f1 | Variable CSS contrôlant la couleur des boutons toggle, les bordures et le dégradé de fond des items. Remplacez par votre couleur brand. |
| grid-template-columns | repeat(3, 1fr) | Colonnes en mode grille. Changez 3 en 2 (items larges), 4 (items compacts) ou 6 (icônes seules sans texte). |
| transition: .5s | 0.5s cubic-bezier(.4, 0, .2, 1) | Durée et easing du FLIP et des transitions max-width/opacity. 0.3s pour une réponse vive, 0.8s pour un effet cinématique. |
| .item-icon width/height | 32px / 32px (grille), 24px / 24px (liste) | Taille de l'icône dans chaque mode. Passez à 40px en grille pour des icônes plus visibles, ou 20px en liste pour une vue ultra-compacte. |
| .item-text max-width | 200px (mode liste) | Largeur maximale du libellé révélé en liste. De 120px pour des labels courts à 320px pour des titres longs. |
| gap | 8px | Espacement entre les items (les deux modes). 12–16px pour une mise en page aérée, 4px pour une liste ultra-dense. |
| .grid-list-item padding | 12px | Rembourrage interne de chaque item. 16–20px pour des items respirants avec grandes icônes, 8px pour une liste compacte. |
| .grid-list-item border-radius | 8px | 4px pour un style rectangulaire strict, 12px pour des cartes douces, 50% (avec items carrés et icône seule) pour des pastilles circulaires. |
FAQ
grid-template-columns n'est pas une propriété interpolable — les navigateurs ne savent pas calculer les états intermédiaires entre repeat(3, 1fr) et 1fr. La technique FLIP contourne cela : elle mesure les positions avant et après le changement de layout, déplace instantanément chaque item avec transform: translate(δx, δy) pour le ramener visuellement à son ancienne position, puis retire le transform dans un requestAnimationFrame — la transition CSS prend le relais et donne l'illusion d'un glissement physique.setGridList cible en dur document.getElementById('gridListDemo'). Pour plusieurs instances, dupliquez la structure HTML avec un id différent par conteneur, puis créez une version de la fonction spécifique à chaque id — ou adaptez-la pour traverser le DOM depuis le bouton : t.closest('.votre-wrapper').querySelector('.grid-list-container') avant d'appeler le reste de la logique FLIP.<button> HTML natifs répondent aux événements touch sans configuration supplémentaire. Il n'y a pas de geste de swipe intégré ; si vous souhaitez déclencher la bascule au glisser, ajoutez un touchstart ou pointerdown sur le conteneur et appelez setGridList manuellement.