Skip to content
animorio
IT
All components

The swipe deck

Cards, Physics, TransitionsGSAPAdvancedCompressed weight: Demo 9.7 KBGSAP 28 KBFreeDownload the zip

A deck of ideas for the weekend: the top card follows the hand and turns around the point it is held by, a stamp says what letting go would do; thrown, it flies off with the speed it had, spinning and dropping a little, while the cards underneath come up, and Undo brings it back from where it went.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The swipe deck</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the swipe deck: the top card follows the hand and turns around the point it is held by; thrown, it flies on
         with the speed it had, and the cards under it come up -->
    <section class="plans" aria-labelledby="plans-title" data-plans data-autoclic>
      <header class="plans__head">
        <h2 class="plans__title" id="plans-title">This weekend</h2>
        <p class="plans__count" data-count></p>
      </header>
      <div class="deck" data-deck>
        <div class="done" data-done hidden>
          <p class="done__over">You kept</p>
          <p class="done__count" data-kept>0</p>
          <ul class="done__list" data-list aria-label="Plans kept"></ul>
          <button class="done__again" type="button" data-again>Start over</button>
        </div>
      </div>
      <div class="actions">
        <button class="act act--skip" type="button" aria-label="Skip" data-skip>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/></svg>
        </button>
        <button class="act act--undo" type="button" aria-label="Undo" data-undo disabled>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 7.5H14.5a5 5 0 0 1 0 10H8"/><path d="M11.5 4.5L8.5 7.5l3 3"/></svg>
        </button>
        <button class="act act--keep" type="button" aria-label="Keep" data-keep>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 12.5l4.2 4.2 8.8-9.4"/></svg>
        </button>
      </div>
      <p class="plans__hint" id="plans-hint">Drag right to keep, left to skip.</p>
      <p class="sr" aria-live="polite" data-live></p>
    </section>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

42 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The swipe deck” from Animorio and fit it into my site

What it does

On a white background This weekend, 1 of 6 and a deck of six coloured cards, each with a drawing, a label, a big name and when (Sunrise hike, Pottery class, Night market, Jazz in the park, Lake swim, Old town walk); under it, a red button to skip, a grey one to go back and a green one to keep. When the page opens the cards rise from below one after another, each landing on the deck and pushing the ones under it down. The top card’s drawing moves gently: the sun rises, a gleam crosses the vase, steam rises from the bowl, the record spins, the waves roll, the arches draw themselves. Dragged, the card follows the hand, tilting (held by its upper half it leans the way it goes, by its lower half the other way), the stamp Keep or Skip appears and the green or red button fills with its colour, while the card underneath grows and rises. Let go past the mark, or with a quick flick, it flies off with the speed it had, spinning and dropping a little; let go before, it goes back to the middle on a spring. The buttons throw it with a little swing; Undo brings it back from where it went. When the cards are over You kept appears with the number and the names kept in their colours, and Start over deals the deck again.

How it works

The card in the hand

On pointerdown the top card takes the pointer (setPointerCapture) and follows the hand in x and y; the rotation is x × TILT, with a sign that depends on where it is held: above the middle it turns the way it moves, below it the other way, like a real card. The speed is measured over the last 70 milliseconds of movement.

Thrown or put back

Let go past THROW pixels, or faster than FLICK pixels a second the same way, the card flies: x and y go on with the speed it had (at least 1100 pixels a second sideways), a gravity makes it drop a little and it keeps turning, until it leaves the screen. Otherwise it goes back to the middle on a spring (SPRING), worked out on GSAP’s ticker in steps of 4 milliseconds.

The deck underneath

Each card sits one step lower for every card on top of it: smaller by DEPTH[0] and lower by DEPTH[1] pixels at every step, brought there by its own spring; from the fourth down it fades away. As the top card moves off, the ones underneath come up by how far it is from the middle, up to a whole step.

Stamps, buttons and Undo

The stamps Keep and Skip show and shrink from 1.35 to 1 with the sideways move, and the buttons fill with their colour with color-mix. The buttons throw the card with a little swing their way; Undo puts it back on top, coming back from where it went, on a softer spring, and the deck moves to make room for it.

Keys and the preview

The top card is reached with Tab: the right arrow keeps it, the left one skips it, and the focus goes to the next card; the others have aria-hidden. An aria-live region says Kept, Skipped, is back. A click from a script (the library’s preview) is a pretend hand: it drags the card along a curve and throws it, now and then brings one back, and at the end deals the deck again.

Values to change

NameFileDefaultWhat it changes
THROWscript.js110How far, in pixels, a card must be dragged for letting go to throw it.
FLICKscript.js650How fast, in pixels a second, a shorter drag must go to throw the card all the same.
TILTscript.js0.07How many degrees a card turns for every pixel it is dragged sideways.
SPRINGscript.js[260, 0.62]The spring that brings a card back: how stiff it is and how much it is held back.
DEPTHscript.js[0.05, 16]How much smaller, and how many pixels lower, each card down the deck sits.
PLANSscript.js[['Sunrise hike', 'Outdoors', 'Sat 6:10 am · 2 h', 'sun'], …]The plans: name, kind, when and the drawing (sun, vase, bowl, record, waves, arches).

Try them in the Playground

When to use it

For choosing among many proposals one at a time: ideas for the weekend, dishes on a menu, houses to visit, candidates, photos to keep, the questions of a game.

On phones

The cards are dragged with a finger without scrolling the page (touch-action none on the cards) and without the long-press menu; the deck is at most 62 per cent of the screen high, so the buttons stay within the thumb’s reach.

With reduced motion

The card still follows the hand, but let go it goes straight to the middle or disappears without flying; the deck changes without springs, the cards do not rise from below at the start and the drawings stay still.

Pitfalls

  • A constant called top at the top level of a classic script clashes with window.top and the script does not run (SyntaxError): it needs another name.
  • gsap.quickSetter with scale writes nothing: scaling needs two quickSetters, scaleX and scaleY.
  • An invisible card (opacity 0) still takes clicks: the cards that are not on top, and those flying away, have pointer-events none, or they cover the buttons.
  • A flick’s speed must be measured over its last moments: an average over too long underestimates it, because the hand speeds up at the end.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 9.7 KB; the libraries: GSAP 28 KB.

Testing

Tested on 3 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All components