Back to gallery

Design engineering: a swipeable cards carousel

A cards carousel with swipe gestures, loopable or not. This is an interaction pioneered by Tinder and that is not so common in desktop apps. It’s so much fun to use, go ahead and drag / swipe cards around and see what happens.

6 cards

Install

Open the repo in Github (and drop a star if you like it!)

npm install @daformat/react-swipeable-cards

Code sample

Below is a minimal example to reproduce the examples above, view the full tsx and scss on github

// full source: https://github.com/daformat/hello-mat/blob/master/pages/design-engineering/component/swipeable-cards.tsx

<SwipeableCards.Root
  cards={[...cards /* omitted for brevity */ ]}
  className={styles.cards_root}
  data-style={"stacked-offset" /* "stacked-rotation" | "minimal" */}
  swipeStyle={"sendToBack"}
  sendToBackMargin={16}
  loop
>
  <SwipeableCards.Cards
    visibleStackLength={4}
    style={{ aspectRatio: "650 / 400" }}
  />
</SwipeableCards.Root>

Swipe gestures

Performing swipe gestures on the web is not a native feature, so you’ll have to do it yourself. The gist of it is to translate the card as you drag it, and compute the velocity of the swipe for a realistic momentum to be applied as you discard a card. This is done simply by dividing the distance traveled by the card by the amount of time elapsed since the last pointer move event.

Handling low velocity

If you drag a card just a little a bit and release the pointer, the card will return to the stack. Unless you moved it by some minimal distance. In this case, we need to ”fake“ the velocity so the card properly animates out of the viewport.

Faking gestures when using buttons

When using buttons, the user didn’t actually perform a swipe gesture, so we need to simulate it. We do this by mocking the dragging state, so that the card properly animates out.

Sending to back

When sending the cards to the back of the stack, we need to ensure the swiped card has travelled enough so that when swapping its z-index, the card doesn’t overlap the stack. Many implementations disregard this issue, as this is not trivial to implement. This package properly ensures that, no more partial clipping when sending a card to the bottom of the stack!

The component allows you to customize the margin by using the sendToBackMargin prop, which is used to specify the pixels-based distance to enforce when sending a card to the back.

That’s a wrap

I was curious to see how I would implement this, so I settled on finding out. I’m overall pretty pleased with the result, and I think I’ll re-use the component in the future. There is something deeply satisfying in using this interaction, and I think Tinder made it their landmark for a reason.

Up next

A Number Flow Input component
-->
<--

Right before

Rolling stacking cards