Card Stack
Cinq cartes de 80 px en position: sticky, épinglées de 10 px en 10 px dans un bloc de 280 px : chacune entre par une animation pilotée par view() — opacité 0 → 1, translateY 40 → 0, scale .92 → 1 sur 64 px de défilement — puis reste sous la suivante, son bord de 10 px visible. CSS seul, zéro JavaScript, course de 720 px.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Scroll comprend 70 effets, dont 12 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Le HTML vendu tient en quatre niveaux : le cadre .demo-preview (flex centré, min-height: 300px, position: relative, fond #0a0a0f, texte blanc, box-sizing: border-box pour tout ce qu'il contient), le bloc défilant .sdc-scroll-container (100% × 280px, overflow-y: auto, scroll-behavior: smooth, barre de 4 px stylée par ::-webkit-scrollbar), le plateau .ss-cardstack-content (1000 px de haut, padding: 20px 12px) et cinq .ss-cardstack-card : 90 % de large, 80 px de haut, margin: 0 auto 16px, border-radius: 12px, flex centré, texte blanc de 13,6 px en graisse 700, ombre 0 4px 20px rgba(0,0,0,.3), un dégradé à 135° par carte (#4f46e5 → #6366f1 pour la première, #ec4899 → #f472b6 pour la cinquième). Le pas vertical vaut 96 px : les cartes commencent à 20, 116, 212, 308 et 404 px du haut du plateau, et la course mesure 1000 − 280 = 720 px. Sous le bloc, le span.sdc-scroll-hint « Scrollez ↓ » (absolu, 8 px du bas du cadre, 10,4 px, blanc à 30 %) pulse entre .3 et .7 toutes les 2 s. Le champ JavaScript est vide ; aucune variable CSS du site, aucune erreur console.
L'entrée est une animation CSS dont le temps est le défilement. La règle de base pose l'état de départ — opacity: 0; transform: translateY(40px) scale(.92) — et la @keyframes ssCardStack ne contient qu'une image to (opacity: 1; transform: translateY(0) scale(1)). animation-timeline: view() crée une timeline de vue anonyme : le scroller est l'ancêtre défilant le plus proche, .sdc-scroll-container, et la progression suit la position de la carte dans ses 280 px. animation-range: entry entry 80% (forme courte de entry 0% entry 80%) borne l'animation à l'entrée : 0 % quand le haut de la carte touche le bord bas du bloc, 100 % quand elle y est entrée tout entière ; 80 % d'une carte de 80 px, c'est 64 px de défilement pour tout le fondu. animation-duration: auto, linear et animation-fill-mode: both font le reste : avant la plage, l'état de départ ; après, l'état final — dans les deux sens (mesuré : revenir à 0 rend aux cartes 4 et 5 leur opacité 0). Mesuré dans Chromium 151, page vierge : la carte 4 va de 0 à 1 entre 28 et 92 px de défilement (.031 à 30, .5 à 60, .969 à 90), la carte 5 entre 124 et 188 (.563 à 160). Les trois premières sont déjà à 1 au repos : leur plage d'entrée est passée avant le début de la course (première question de la FAQ).
L'empilement est du position: sticky pur, avec un top qui croît de 10 px par carte (:nth-child(1) à 10 px … :nth-child(5) à 50 px). Une carte s'épingle quand sa position normale passe sous son top : à 10, 96, 182, 268 et 354 px de défilement. Les suivantes, plus tardives dans le DOM, se peignent par-dessus : chaque carte laisse voir 10 px de la précédente, et la pile occupe de 10 à 130 px du bloc. L'entrée et l'épinglage ne se chevauchent jamais — la carte 4 finit son fondu à 92 px et s'épingle à 268. Le bloc conteneur du sticky est le plateau : ses 1000 px gardent les cinq cartes collées jusqu'au bout, mais la pile est complète à 354 px et les 366 px restants ne changent plus rien.
Accessibilité
- prefers-reduced-motion absent du code : mesuré sous émulation, rien ne change — les cartes 4 et 5 restent à
opacity: 0tant qu'on n'a pas défilé jusqu'à elles, comme sans la préférence, et l'indice pulse toujours. Le fondu est piloté par le lecteur (aucun mouvement sans son geste), ce qui le range parmi les animations déclenchées par l'interaction (WCAG 2.3.3). Pour un repli à plat, ajoutez@media (prefers-reduced-motion: reduce) { .ss-cardstack-card { animation: none; opacity: 1; transform: none } .sdc-scroll-hint { animation: none } }: les cinq cartes sont visibles d'emblée et s'empilent sans fondu — ce que voit déjà Firefox. - Clavier : mesuré, Tab donne le focus au bloc sans
tabindexdans Chromium (flèche bas 40 px, PageDown 285, Fin 720) et dans Firefox (51, 303, 720) ; WebKit ne focalise pas un bloc défilant sanstabindex="0"— ajoutez-le sur.sdc-scroll-container, avec unaria-labelet un style:focus-visible. Sur écran tactile, le geste doit commencer dans le bloc ;overscroll-behavior: containévite d'entraîner la page en bout de course. - Contraste (texte blanc de 13,6 px en graisse 700, seuil 4,5:1) : 6,3 → 4,5:1 sur la première carte, puis 4,5 → 3,0 (deuxième), 4,2 → 2,7 (troisième), 3,5 → 2,5 (quatrième) et 3,5 → 2,7 (cinquième) : quatre cartes sur cinq passent sous le seuil sur leur moitié claire. Assombrissez la seconde couleur de chaque dégradé (
#4338ca,#6d28d9,#a21caf,#be185dtiennent 6:1 et plus avec du blanc) ou posez le texte sur un fond uni. L'indice à 30 % vaut 2,6:1 : décoratif,aria-hidden="true". - Lecteurs d'écran et contenu recouvert : les cinq cartes sont des
divlus dans l'ordre du DOM ;opacity: 0ne retire rien de l'arbre d'accessibilité, un lecteur d'écran lit donc les cinq d'un coup. Si les cartes portent une information, prenezul/liouarticleavec un titre. Visuellement, une carte épinglée n'est plus lisible que sur ses 10 px de bord : seul le retour en arrière la découvre (le sticky se défait dans l'autre sens, mesuré) — prévoyez-le si vos cartes sont des données et non des étapes. L'indice « Scrollez ↓ » est écrit en dur en français.
Compatibilité navigateur
CSS seul, zéro dépendance, zéro JavaScript : animation-timeline: view(), animation-range et animation-duration: auto (animations pilotées par le défilement), position: sticky, overflow-y, dégradés, @keyframes. Les animations pilotées par le défilement existent dans Chrome et Edge depuis la version 115, dans Safari et iOS depuis la version 26 (mesuré dans WebKit 26.5 : mêmes valeurs qu'en Chromium) ; Firefox ne les gère dans aucune version. Mesuré dans Firefox 153 : animation-timeline et animation-duration: auto sont ignorés, la durée calculée tombe à 0 s et l'animation, en fill-mode: both, se pose sur son image to dès le chargement — les cinq cartes sont visibles à pleine opacité, sans fondu ni glissement, et s'empilent exactement comme ailleurs (10, 20, 30, 40, 50 px en fin de course). Rien n'est perdu, seule l'entrée manque. Le code ne contient pas de bloc @supports : l'état final sert de repli.
Là où animation-timeline manque (Firefox, Chrome avant 115, Safari avant 26) : cartes à plat, opacité 1, sans translation ni échelle, empilement sticky identique. Un navigateur sans position: sticky ferait défiler les cartes comme une liste ordinaire. Sans le cadre .demo-preview, l'indice « Scrollez ↓ » perd son ancre et tombe au bas de la page : retirez le span ou donnez position: relative à votre propre cadre.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="ss-cardstack-content">
<div class="ss-cardstack-card">…</div>
<div class="ss-cardstack-card">…</div>
<div class="ss-cardstack-card">…</div>
<div class="ss-cardstack-card">…</div>
<div class="ss-cardstack-card">…</div>
</div>
</div>
<span class="sdc-scroll-hint">…</span>
</div>
Le code complet HTML + CSS + JS de Card Stack, prêt à coller — et les 811 effets. Ou 299 € une fois, accès à vie.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Nombre de cartes et décalage (HTML + CSS — :nth-child(n) { top }) |
5 cartes, top 10 → 50 px par pas de 10 | Le pas est le bord visible de chaque carte sous la suivante. Une sixième carte n'a ni fond ni top propre (elle prend top: 10px et un fond transparent) : ajoutez :nth-child(6) { top: 60px; background: … }. Un pas de 6 px serre la pile, 16 px laisse lire un début de titre sur chaque bord. |
Plage d'entrée (CSS — animation-range: entry entry 80%) |
entry 0 % → entry 80 % (64 px de défilement pour 80 px de carte) | entry entry 100% étire le fondu sur toute l'entrée (80 px) ; entry 20% entry 100% retarde le départ ; cover 0% cover 40% continue après l'entrée complète. La plage se lit en pourcentage de la hauteur de la carte : plus la carte est haute, plus le fondu dure. La courbe linear colle au geste ; ease-out fait apparaître vite puis poser lentement. |
État de départ (CSS — règle de base opacity: 0; transform: translateY(40px) scale(.92)) |
invisible, 40 px plus bas, 92 % | La @keyframes ne contient que l'image to : le départ est celui de la règle de base, c'est là qu'on le règle. translateY(16px) scale(.98) donne une entrée discrète ; opacity: .4 laisse deviner la carte avant son entrée. |
Hauteur du plateau et du bloc (CSS — .ss-cardstack-content { height: 1000px }, .sdc-scroll-container { height: 280px }) |
1000 px / 280 px (course 720, pile complète à 354) | La course vaut plateau − bloc ; la pile est complète à 354 px (dernière carte épinglée), au-delà rien ne change. Un plateau de 640 px (course 360) finit juste après ; si le nombre de cartes varie, passez en height: auto avec un padding-bottom égal à la hauteur du bloc. Le bloc de 280 px décide combien de cartes sont déjà visibles au repos (trois ici). |
Taille et écart des cartes (CSS — height: 80px, width: 90%, margin: 0 auto 16px) |
80 px, 90 %, 16 px (pas de 96 px) | Le pas règle le rythme des épinglages : une carte tous les 96 px de défilement. Les cartes sont des boîtes flex centrées avec un seul texte ; pour une ligne date / libellé / montant, passez justify-content: space-between et un padding horizontal — le sticky et l'animation ne dépendent pas du contenu. |
Couleurs (CSS — :nth-child(n) { background: linear-gradient(135deg, …) }) |
5 dégradés de l'indigo au rose, texte blanc | Chaque carte porte son fond ; la pile se lit par ces bandes de 10 px, gardez des couleurs distinctes d'une carte à l'autre. Le texte blanc de 13,6 px ne tient 4,5:1 que sur la première carte : assombrissez la seconde couleur ou passez à des cartes unies (blanches bordées sur fond clair, comme la première scène). |
Défilement doux et indice (CSS — scroll-behavior: smooth, .sdc-scroll-hint) |
smooth / « Scrollez ↓ » pulsant 2 s | smooth n'agit que sur les positions posées par script (mesuré : scrollTop = 720 se relit 0 et arrive en 420 ms environ dans Chromium, 540 dans Firefox, 200 dans WebKit) — auto si un bouton pilote le bloc. L'indice est un reste du catalogue : français en dur, ancré au cadre .demo-preview ; pour vous en passer, retirez le span et la @keyframes sdcHintPulse. |
FAQ
entry court de l'instant où le haut de la carte touche le bord bas du bloc à celui où elle y est entrée entière ; au repos, les cartes 1 et 2 (20 → 100 et 116 → 196 px) sont entières dans les 280 px du bloc, et la troisième (212 → 292) n'a que 12 px dehors : sa plage entry 0% → 80% va de −68 à −4 px de défilement, déjà passée, et fill-mode: both lui applique l'image finale. Mesuré : au repos, opacité 1 sur les trois, 0 sur les cartes 4 et 5, qui entrent entre 28 et 92 px puis 124 et 188. Pour que toutes entrent en fondu, décalez-les sous le bord bas — padding-top: 280px sur le plateau, la course commence alors par un écran vide — ou réduisez le bloc sous le pas : 90 px ne montrent que la première carte.position: sticky, même view() avec entry entry 80%, même état de départ (40 px plus bas, 92 %), même pas de 10 px, zéro JavaScript des deux côtés. Les écarts se lisent dans les deux codes : fx-0568 pose des cartes de 90 px à 85 % (80 px à 90 % ici), les épingle de 30 à 70 px (10 à 50 ici), remplace le plateau fixe de 1000 px par deux espaceurs — 40 px avant la première carte, 120 px après la dernière — et donne à son bloc height: 100% et un padding de 20 px, au lieu d'un bloc de 280 px dont le padding est porté par le plateau. Mesuré dans Chromium, les deux dans un bloc de 280 px : fx-0568 défile sur 450 px, montre deux cartes au repos (la troisième à 11 %), épingle à 10, 106, 202, 298 et 394 px et sa pile occupe 50 → 180 px, le padding du bloc s'ajoutant au top de chaque carte ; Card Stack défile sur 720 px, montre trois cartes, épingle à 10, 96, 182, 268 et 354 px, pile de 10 à 130 px. Règle d'usage : partez de Card Stack, et ne reprenez de fx-0568 que ses espaceurs si le nombre de cartes varie — le plateau suit alors le contenu, à condition que l'espaceur final remplisse ce qui reste du bloc sous la dernière carte épinglée : 100 px dans un bloc de 280 (les 120 px de fx-0568 suffisent), 220 dans un bloc de 400 — mesuré, avec 120 px la dernière carte s'arrête à 154 px et ne s'épingle jamais. Hauteur, largeur et top de départ sont des valeurs à changer, pas une raison de choisir l'un ou l'autre.view() prend l'ancêtre défilant le plus proche, donc le document si vous retirez overflow-y: auto du bloc (ou le bloc lui-même), et position: sticky s'épingle alors par rapport à la fenêtre. Remplacez le plateau de 1000 px par vos sections, avec assez de contenu après la dernière carte pour qu'elle ait le temps de s'épingler (sa position normale moins son top). Le pas de 10 px devient petit sur un grand écran : 24 à 32 px laissent lire le titre de chaque carte recouverte. Un en-tête fixe s'ajoute au top de chaque carte, sinon il la couvre ; l'indice, ancré au cadre, passerait en position: fixed. Firefox y voit la même pile, sans fondu.