Installation
npm install react-driftkitimport { SnapDock, InspectorBubble } from 'react-driftkit';Interactive Demo
Peek edge
Peek size
28px
Depth fade
0.10/depth
Hover peek
12px
Animation
320ms
Swipe to dismiss
overview
Overview
Stack 2–N cards where each back card peeks out by a configurable amount. Click the peek to flick that card to the front.
details
Details
Peek from any edge — top, bottom, left, or right. Peek size and animation duration/easing are all configurable.
stats
Stats
Cards are React children keyed by id. Controlled or uncontrolled front card, plus optional swipe-to-dismiss for tip-style flows.
credits
Credits
Built on a single CSS grid cell with transforms — no runtime deps, no layout thrash. Style the cards however you'd like.
Hover a peeking card — it nudges out a little further and lifts to full opacity as a click affordance. Click it to flick it forward. Flip the peek edge and size above to reshape the deck: top/bottom cards recede into depth, left/right cards fan out at an angle, and further-back cards fade. Toggle swipe-to-dismiss and drag the front card in the direction opposite the peek to remove it past the threshold.
API Reference
| Prop | Type | Default |
|---|---|---|
frontId Controlled id of the front card — matches a child's React key. Omit for uncontrolled. | string | — |
defaultFrontId Uncontrolled initial front card id. Falls back to the first child's key if unset. | string | — |
peek Which edge the back cards peek from. | 'top' | 'bottom' | 'left' | 'right' | 'bottom' |
peekSize Pixels of each back card that remain visible behind the card in front of it. | number | 24 |
depthScale How much each back card shrinks per depth level, for top/bottom peek — makes the stack feel recessed. Set to 0 for a flat stack. | number | 0.05 |
fanAngle Degrees each back card rotates per depth level, for left/right peek — makes the stack fan out at an angle. Set to 0 for a flat stack. | number | 4 |
depthFade Opacity subtracted per depth level — further-back cards look more distant. Clamped so no card drops below 0.25. Set to 0 to disable. | number | 0.08 |
hoverPeek Extra pixels a back card translates out along the peek axis when it is hovered or keyboard-focused. Opacity also snaps back to 1 during the hover to signal clickability. Set to 0 to disable. | number | 8 |
swipeToDismiss When true, the front card can be pointer-dragged off in the direction opposite the peek to fire on.dismiss. | boolean | false |
dismissThreshold Fraction of the card's axis size the drag must cross to count as a dismiss. Values are clamped to [0, 1]. | number | 0.3 |
animation Override the transition used for the flick and swipe animations — duration (ms) and easing (CSS easing). | FlickDeckAnimation | — |
animation.duration Transition duration in milliseconds. | number | 320 |
animation.easing CSS easing function applied to transform and opacity transitions. | string | 'cubic-bezier(0.22, 1, 0.36, 1)' |
on Event handlers: frontChange, dismiss. Both optional. | FlickDeckEvents | — |
on.frontChange Fires whenever the front card changes — click, keyboard activation, or setter. | (id: string) => void | — |
on.dismiss Fires when the front card is swiped past dismissThreshold. Consumer is expected to remove that child from children. | (id: string) => void | — |
className CSS class added to the deck container. | string | '' |
style Inline styles merged onto the deck container. | CSSProperties | — |
cardClassName CSS class added to every card wrapper. | string | '' |
cardStyle Inline styles merged onto every card wrapper. | CSSProperties | — |
children Each child must have a unique key — that key is the card's id. | ReactNode | — |
The deck lays its cards out in a single CSS grid cell and offsets back cards via transform, so the container auto-sizes to the largest card plus padding for the peek. Each card wrapper exposes data-flick-deck-card, data-flick-deck-front, data-flick-deck-active (hovered/focused back card), and data-flick-deck-depth so you can drive styles from CSS without re-rendering.
Code Examples
tsx
import { useState } from 'react';
import { FlickDeck } from 'react-driftkit';
function App() {
const [frontId, setFrontId] = useState('overview');
return (
<FlickDeck
frontId={frontId}
peek="bottom"
peekSize={28}
on={{ frontChange: setFrontId }}
>
<Card key="overview" title="Overview">...</Card>
<Card key="details" title="Details">...</Card>
<Card key="stats" title="Stats">...</Card>
</FlickDeck>
);
}Types
typescript
type FlickDeckPeek = 'top' | 'bottom' | 'left' | 'right';
interface FlickDeckProps {
// Controlled / uncontrolled front card. The id is the child's React `key`.
frontId?: string;
defaultFrontId?: string;
// Which edge the back cards peek from, and how much of each is visible.
peek?: FlickDeckPeek; // default 'bottom'
peekSize?: number; // default 24 (px)
// Back cards shrink along the peek axis (top/bottom), fan out at an angle
// on the peek axis (left/right). Set either to 0 for a flat stack.
depthScale?: number; // default 0.05 (5% smaller per depth level)
fanAngle?: number; // default 4 (degrees per depth level)
// Further-back cards fade. Hovered/focused back card peeks out a little
// more and snaps to full opacity as a click affordance. Set to 0 to disable.
depthFade?: number; // default 0.08 (opacity per depth level)
hoverPeek?: number; // default 8 (extra px on hover/focus)
// When true, the front card can be dragged off in the direction opposite
// of `peek` to fire `on.dismiss`. Off by default.
swipeToDismiss?: boolean;
dismissThreshold?: number; // fraction of card axis — default 0.3
animation?: {
duration?: number; // default 320 (ms)
easing?: string; // default 'cubic-bezier(0.22, 1, 0.36, 1)'
};
on?: {
frontChange?: (id: string) => void;
dismiss?: (id: string) => void;
};
className?: string;
style?: CSSProperties;
cardClassName?: string;
cardStyle?: CSSProperties;
// Each child must have a unique `key` — that key is the card's id.
children?: ReactNode;
}Enjoying react-driftkit?
Star the repo on GitHub to help more devs discover it.