Early bird: 30% off Dashral ProSee pricing

Dashral

Card DeckPro

A deck of cards to flick through: throw the top one to the back, step back with the arrows, and tap a card to lift it and show its detail.

Installation

When to use

  • A few things of the same kind people flick through, one at a time: payment cards, membership passes, tickets, profiles.
  • When each one has a detail worth opening in place, without leaving the page: a card's balance and latest payments.

When not to use

  • Many items to compare or scan: a list, a table or a grid shows them all at once.
  • Slides that should keep their order and size, like a gallery: the Carousel fits better.

Usage

Card Deck with your own data: a whole component you can paste as a page.

example.tsx
"use client"

import { CardDeck } from "@/components/dashral/pro/card-deck"

type Pass = { id: string; name: string; tier: string; color: string }

const passes: Pass[] = [
  { id: "p1", name: "Olivia Martin", tier: "Gold", color: "bg-amber-600" },
  { id: "p2", name: "Liam Johnson", tier: "Silver", color: "bg-slate-500" },
  { id: "p3", name: "Sofia Ramirez", tier: "Platinum", color: "bg-zinc-800" },
]

export default function Passes() {
  return (
    <CardDeck
      label="Membership passes"
      items={passes}
      getKey={(pass) => pass.id}
      getLabel={(pass) => `${pass.tier} pass for ${pass.name}`}
      // Each card's face fills the card: the deck shapes it (radius, shadow) and moves it.
      renderCard={(pass) => (
        <div className={`flex size-full flex-col justify-between p-5 text-white ${pass.color}`}>
          <span className="text-sm font-medium">{pass.tier}</span>
          <span className="text-lg font-semibold">{pass.name}</span>
        </div>
      )}
      // Tap the top card: this comes out from under it.
      renderDetail={(pass) => <p className="text-sm text-muted-foreground">{pass.name}'s {pass.tier} pass gives priority entry and two guest passes a month.</p>}
    />
  )
}

API reference

CardDeck

A deck of cards to flick through: the top card in front and the next ones fanned out behind it. Drag or throw the top card to send it to the back (a short drag glides home), use the arrows or the arrow keys, and tap the top card to lift it and show its detail. Generic: renderCard draws each face, renderDetail what a lifted card shows.

PropTypeDefault
itemsT[]

Required. The cards, front to back.

—
renderCard(item: T, state: { top: boolean; index: number }) => React.ReactNode

Required. A card's face: it fills the card, which the deck shapes (radius, shadow) and moves.

—
renderDetail(item: T, state: { index: number; close: () => void }) => React.ReactNode

What a lifted card shows beneath it. Left out, tapping does nothing and the hint only mentions swiping.

—
getLabel(item: T) => string

Required. What a card is called, for screen readers ("Travel card ending in 4242").

—
getKey(item: T, index: number) => string

A card's key; its index by default.

(_item, i) => String(i)
indexnumber

The card on top.

—
defaultIndexnumber0
onIndexChange(index: number) => void—
openboolean

Whether the top card is lifted.

—
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void—
aspectnumber

A card's width over its height; a payment card's (1.586) by default.

1.586
arrowsboolean

Previous and next buttons beside the count. Off by default: the deck is swiped (and the arrow keys work).

false
behindnumber

How many cards show behind the top one.

2
labelstring

The deck's name, for screen readers.

"Cards"
hintReact.ReactNode

Under the controls; left out, how to use the deck. Pass null for none.

—
classNamestring—
classNamesCardDeckClassNames—

type CardDeckClassNames

PropTypeDefault
rootstring—
deckstring—
cardstring—
controlsstring—
detailstring—

Keyboard

KeysAction
→Throw the top card to the back
←Bring the last card back to the top
EnterorSpaceLift the top card to show its detail
EscPut the lifted card back

Accessibility

  • A group with the role description "card deck", named by label; it is one tab stop.
  • A live region says the card in front and its place ("Visa, 2 of 4"), and when it's opened.
  • Only the top card is exposed; the cards behind it are hidden from screen readers.
  • The arrow buttons (arrows) are named Previous card and Next card.

Motion

  • A drag follows the pointer one to one and tilts the card; past the threshold it's thrown to the back, short of it the card glides home.
  • Lifted, the card rises and its detail opens as a drawer from under it. Phones get a light buzz when a throw commits.
  • Smooth tweens on [0.4, 0, 0.2, 1], never springs, except the small press as you grab a card. With reduced motion there's no tilt and the cards fade instead of moving.