Bento Card Stack
Grille bento avec piles de cartes décalées en profondeur — au survol, chaque pile s'ouvre en éventail via une transition CSS spring sans JavaScript.
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
La structure repose sur un CSS Grid 4×3 (repeat(4,1fr) colonnes, repeat(3,1fr) rangées). Deux cellules larges occupent span 2/span 2 pour les piles principales, deux cellules inférieures couvrent span 2 pour les mini-cartes — le tout dans .bento-container avec max-width: 600px et aspect-ratio: 4/3.
L'empilement est simulé en CSS pur : les trois .stacked-card sont en position: absolute, centrées par translate(-50%, -50%). La deuxième carte est décalée translateY(8px) scale(0.95) et la troisième translateY(16px) scale(0.90) — aucune perspective 3D, l'illusion de profondeur vient uniquement du décalage Y et de la réduction d'échelle.
Au :hover sur la cellule bento, trois transitions 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) (spring avec légère surtension) jouent simultanément : la carte avant part en translateX(-30%) rotate(-10deg), la carte centrale remonte de 5 px, la carte arrière part en translateX(30%) rotate(10deg) — formant un éventail symétrique. Les .mini-card des cellules inférieures suivent la même logique avec translateY et rotation légère.
Le JS expose replayAnimation() pour rejouer la variante .bento-animated (entrée par @keyframes bentoFadeIn) : il force un reflow via offsetHeight puis remet l'animation à zéro. Le mécanisme card-stack lui-même est 100 % CSS — aucune initialisation JS n'est nécessaire.
Accessibilité
- prefers-reduced-motion absent : le code ne contient pas de media query
prefers-reduced-motion. Les transitions CSS jouent même si l'utilisateur a demandé à réduire le mouvement. Correctif minimal :@media (prefers-reduced-motion: reduce) { .stacked-card, .mini-card { transition: none; } }. - L'animation est déclenchée au :hover CSS uniquement — aucun équivalent au clavier (
:focus-withinabsent). Les utilisateurs naviguant au clavier ne voient pas l'éventail. - Aucune sémantique ARIA sur les cellules : les
.bento-cellsont des<div>non interactifs sansroleniaria-label. Ajouterrole="group"etaria-labelsur chaque pile si son contenu est signifiant. - Les gradients couleur des cartes (teintes indigo-violet proches) offrent un contraste limité pour du texte blanc superposé — à vérifier avec votre palette via un outil de contraste WCAG.
- Les
.mini-cardsont des<div>purement décoratifs sans texte : aucune alternative textuelle n'est requise tant qu'ils restent visuels.
Compatibilité navigateur
Nécessite CSS Grid et CSS transitions — supportés depuis 2017 dans tous les navigateurs modernes. Zéro dépendance externe.
Sur écran tactile, :hover CSS ne se déclenche pas : les cartes restent empilées dans leur état de repos, lisibles. Aucune erreur JS.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="bento-container">
<div class="bento-grid bento-cardstack">
<!-- Cellule large 1 : pile de 3 cartes -->
<div class="bento-cell cell-1">
<div class="card-stack">
<div class="stacked-card"><span>Carte 3</span></div>
<div class="stacked-card"><span>Carte 2</span></div>
<div class="stacked-card"><span>Carte 1</span></div>
</div>
</div>
<!-- Cellule large 2 : pile de 3 cartes -->
<div class="bento-cell cell-2">
<div class="card-stack">
<div class="stacked-card"><span>C</span></div>
<div class="stacked-card"><span>B</span></div>
<div class="stacked-card"><span>A</span></div>
</div>
</div>
<!-- Cellule inférieure gauche : mini-cartes -->
<div class="bento-cell cell-3">
<div class="mini-cards">
<div class="mini-card"></div>
<div class="mini-card"></div>
<div class="mini-card"></div>
</div>
</div>
<!-- Cellule inférieure droite : mini-cartes -->
<div class="bento-cell cell-4">
<div class="mini-cards">
<div class="mini-card"></div>
<div class="mini-card"></div>
<div class="mini-card"></div>
</div>
</div>
</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 |
|---|---|---|
| --bg-elevated | #1a1a24 | Couleur de fond des cellules bento contenant les piles — modifiez pour un contraste plus marqué ou une teinte brand. |
| --bg-card | #12121a | Couleur de fond de base des cellules bento sans variante elevated. |
| --border | rgba(255,255,255,0.08) | Bordure des cartes empilées. Montez l'opacité à 0.18 pour des contours plus visibles. |
| --radius-md | 0.5rem | Rayon des coins des .stacked-card. Passez à 0 pour des cartes carrées ou 1rem pour plus de rondeur. |
| .stacked-card:nth-child(n) background | gradients indigo→violet | Remplacez le linear-gradient de chaque :nth-child(1/2/3) pour personnaliser la palette carte par carte selon votre identité. |
| translateX(±30%) dans :hover | ±30% | Amplitude du déploiement latéral en hover. ±45% pour un éventail large, ±15% pour un déploiement subtil. |
| rotate(±10deg) dans :hover | ±10deg | Angle de rotation des cartes latérales en hover. 0deg = déploiement sans rotation, 18deg = éventail très prononcé. |
| transition sur .stacked-card | 0.4s cubic-bezier(.175,.885,.32,1.275) | Durée et easing de l'animation. 0.25s ease-out pour un rendu sobre, 0.6s pour un rebond plus ample. |
FAQ
<div class="stacked-card"> en premier enfant (il sera le plus en dessous visuellement). Puis ajoutez la règle repos : .bento-cardstack .stacked-card:nth-child(4) { transform: translate(-50%,-50%) translateY(24px) scale(0.85); z-index: 0; } et la règle hover correspondante avec un translateX plus amplifié.:hover CSS ne se déclenche pas nativement au toucher. Pour l'activer au tap, ajoutez un listener touchstart qui bascule une classe .is-open sur la cellule, puis ciblez .bento-cell.is-open .stacked-card dans vos règles CSS de déploiement à la place du :hover.translate(-50%,-50%) translateY(-35%) rotate(-8deg) ; carte centrale → translate(-50%,-65%) ; carte arrière → translate(-50%,-50%) translateY(35%) rotate(8deg). Le cubic-bezier et la durée restent inchangés.