023

Draggable Card Stack

£10 or the whole Vault, £99

Swipeable card deck powered by Draggable and InertiaPlugin. Throw the top card to send it to the back; momentum decides dismiss or spring-back, and the rest of the page follows whichever card is on top.

DraggableInertiaPlugin intermediate
4 more details
draggablemomentum3d-transformsstagger

This demo reads better at your own screen size than in the frame below:

Updated

About this effect

A swipeable card deck built on GSAP's Draggable and InertiaPlugin. Drag the top card and release: a hard flick or a drag past the threshold throws it off-screen and recycles it to the back of the pile, while a gentle release springs it back with an elastic snap. Cards behind scale and slide forward in real time as the top card leaves. The deck also publishes which card is on top: headings, notes and index rows elsewhere on the page bind to it by attribute, or you can listen for a cardstack:change event, so a deck can drive a whole layout rather than sitting in a box on its own. Prev/next buttons run the same animations for keyboard users.

Draggable Card Stack - GSAP animation effect preview

What's included

10 items
  • Momentum-aware dismissal: InertiaPlugin velocity tracking decides throw vs spring-back
  • Infinite recycling: dismissed cards return to the bottom of the pile
  • Live drag feedback: top card tilts with drag distance while the next card scales up in preview
  • Elastic snap-back for releases that do not pass the distance or velocity threshold
  • Bindings that follow the top card: data-stack-title, data-stack-note and data-stack-item
  • A cardstack:change event carrying index, total, card and title for anything else on the page
  • Configurable stack depth, offset, scale step, and thresholds via data attributes
  • Prev/next button controls that mirror the swipe animations for keyboard access
  • ARIA live region announces the current card and position to screen readers
  • Reduced-motion mode swaps cards instantly with no animation and no dragging

Perfect for

5 use cases
  • Travel, tour or itinerary pickers where the page copy changes with the card
  • Testimonial decks that invite interaction instead of auto-rotating
  • Product or plan highlights on landing pages that reward exploration
  • Team member or speaker cards for event and agency sites
  • Onboarding tip sequences users can flick through at their own pace

How it works

1 section

Cards are absolutely positioned and laid out by depth, using y offset, scale and z-index to build the pile. Draggable tracks pointer velocity; on release, the throw speed and the drag distance decide between flying the card off-screen and springing it back. Dismissed cards go to the back of the order and the pile re-lays out with a stagger. Every layout ends in one announce step that writes the counter and live region, updates any bound elements, and dispatches cardstack:change, so the deck stays the single source for what is on top. Buttons call the same dismiss and restore routines, so keyboard and pointer users see identical motion.

Plugins Draggable, InertiaPlugin
Difficulty Intermediate
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Draggable Card Stack opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£10 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £99, one payment.

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before closing </body> tag:

Code snippet omitted: it ships with the download.

3. Add the effect HTML to your <body>:

Code snippet omitted: it ships with the download.

The controls and status elements are optional but recommended: they provide the keyboard and screen reader path. They must be siblings of .card-stack inside the same wrapper.

Using It With Your Own Design

What the effect requires of your markup: a .card-stack with a fixed height and .stack-card children that are position: absolute; inset: 0. That is the whole contract. The script never reads or writes card content — it only sets transforms, z-index and the .is-top class — so a card can contain anything. .stack-controls, .stack-count and .stack-status are looked up as siblings of .card-stack, inside the same wrapper; drop them and the deck still drags, it just loses its keyboard and screen reader path.

What is only the demo's CSS. In style.css, everything under "SHOWCASE SHELL" — .showcase-shell, .showcase-toolbar, .showcase-prompt, .travel-scene, .scene-copy, .scene-title, .scene-note, .scene-index — is the demo page's dressing. Delete all of it. So is the card's own look: the destination photos, .stack-card-body, .label, .stack-card-title, .stack-card-text and .card-date. What the effect needs is the .card-stack, .stack-card, .is-top and .stack-controls blocks, plus .sr-only.

The non-obvious CSS the effect depends on:

  • .card-stack needs an explicit height. Its children are all absolutely positioned, so it has no height of its own and the pile collapses to nothing.
  • Leave room below the pile for the controls. Preview cards sit data-max-visible × data-offset pixels below the container's box, outside its height. The demo reserves that with a margin-bottom on .card-stack; without it the buttons sit on top of the pile.
  • transform-origin: 50% 92% is what makes a thrown card pivot from its lower edge rather than its centre, which is what reads as weight.
  • touch-action: none on .is-top is required or a vertical swipe scrolls the page instead of dragging the card. It is scoped to the top card so the rest of the page still scrolls normally.
  • Nothing is hidden at rest. The :nth-child rules build a complete static pile before JavaScript runs, and cards past the fourth are opacity: 0 by design — they are duplicates of what is already on screen, not lost content. If you change data-max-visible, update those rules to match.
  • Reduced motion keeps the same layout and disables only dragging and the tweens; the prev/next buttons swap cards instantly.

Following the Top Card

The deck publishes which card is on top after every change, so the rest of the page can react without reaching into the stack.

By attribute. Point data-target at the deck's id (needed only when a page has more than one deck):

Code snippet omitted: it ships with the download.

Attribute Gets
data-stack-title The top card's data-title as its text
data-stack-note The top card's data-note as its text
data-stack-item .is-active when its position matches the top card

By event. For anything the attributes do not cover:

Code snippet omitted: it ships with the download.

It fires once on init and once per change, bubbles, and is the same signal the counter and the ARIA live region use.

Options

Set these as data attributes on the .card-stack element.

Attribute Values Default Description
data-enabled true, false true Enable/disable the effect
data-max-visible 2-6 4 Cards visible in the fanned pile
data-offset Any number 18 Vertical gap (px) between stacked cards
data-scale-step 0.02-0.12 0.06 Scale reduction per card of depth
data-threshold 0.2-0.8 0.4 Drag distance to dismiss, as a fraction of card width
data-velocity Any number 700 Release velocity (px/s) that dismisses regardless of distance

Each .stack-card can set data-title for the screen reader announcement ("Kyoto in Autumn, card 1 of 6"), and data-note for a longer line picked up by [data-stack-note]. Give the .card-stack an id if anything on the page targets it.

Accessibility

This effect is fully accessible:

  • Reduced motion: With prefers-reduced-motion: reduce, dragging is disabled and the prev/next buttons swap cards instantly with no animation
  • Keyboard navigation: The prev/next buttons drive the exact same dismiss and restore animations as swiping
  • Screen readers: An aria-live status region announces the current card title and position after every change
  • Semantic HTML: Controls are real <button> elements with aria-labels; decorative images use empty alt

Touch Behavior

Draggable handles touch natively. The top card sets touch-action: none so swipes are captured by the deck; the rest of the page scrolls normally.

Dependencies

Required:

  • GSAP 3.12+
  • Draggable plugin
  • InertiaPlugin (free on the public CDN since GSAP 3.13)

Optional:

  • None. The effect does not use scroll, so no ScrollTrigger or Lenis needed.

Photo credits

Destination photos are bundled as local WebP files in assets/img/. Replace them with your own images; retain draggable="false" and the image's pointer-events: none so dragging the image moves the card.

Photos from Unsplash, under the Unsplash License.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.