Description:
Hero Card Stack is a React animation component that creates a scrollable deck of cards with spring-physics transitions, blur effects, and built-in keyboard navigation. Inspired by Apple’s Time Machine.
Features
- Scroll-driven card navigation that maps vertical scroll distance to an active card index with a configurable 50px snap interval.
- Spring-physics animations via Framer Motion applied individually to each card’s scale, vertical position, blur, and opacity.
- Arrow key support (left and right) for keyboard-first navigation through the card stack.
- Automatic keyboard shortcut suppression when the user focuses on an input, textarea, or select element.
- Cards fan out with an 8% scale reduction and a -30px vertical offset per stack position.
- Images load from
src/assets/as static imports. The component duplicates the sequence internally for an extended scroll range. - Tailwind CSS v4 styling with Vite-powered hot module replacement for fast iteration.
Preview

Use Cases
- A product landing page that presents multiple features or pricing tiers as stacked cards, where users scroll through each one in sequence.
- A developer portfolio where project cards reveal one at a time as the visitor scrolls down, each displaying its own image and context.
- A multi-step onboarding flow where each instruction or action appears as the next card in the deck.
- A media gallery or photo viewer that presents images as a tactile card deck with spring-based transitions between items.
How To Use It
1. Clone and install.
git clone https://github.com/mirayatech/hero-card-stack.git
cd hero-card-stack
npm install2. Start the development server. Vite starts the dev server at http://localhost:5173 by default. Open it, scroll the page, or press the arrow keys to see the card stack in action.
npm run dev3: Add your images. Place .jpg images named 1.jpg through 7.jpg in src/assets/. The component imports these files statically and duplicates the sequence to extend the scroll depth.
src/
assets/
1.jpg
2.jpg
3.jpg
4.jpg
5.jpg
6.jpg
7.jpg4. The TimeMachine component manages the scroll listener and card index internally. Drop it into any layout:
// src/App.tsx
// Import the main card stack component
import { TimeMachine } from './components/time-machine';
function App() {
return (
// The scroll container must span at least 100vh + 200px
// to support the full snap sequence
<main className="relative min-h-screen">
{/* TimeMachine attaches its own scroll listener on mount */}
<TimeMachine />
</main>
);
}
export default App;5. All animation parameters live in src/lib/constants.ts. Edit these values to tune the stack behavior:
// src/lib/constants.ts
export const CARD_COUNT = 5;
export const LENGTH = CARD_COUNT - 1;
export const SNAP_DISTANCE = 50;
export const FRAME_OFFSET = -30;
export const FRAMES_VISIBLE_LENGTH = 3;6. The useShortcuts hook in src/hooks/use-shortcuts.ts wires ArrowRight and ArrowLeft to navigation callbacks. It suppresses automatically when a form field holds focus:
// Register arrow key listeners with navigation callbacks
import { useShortcuts } from '../hooks/use-shortcuts';
useShortcuts({
// Arrow Right advances to the next card in the stack
onNext: () => setActiveIndex((i) => i + 1),
// Arrow Left steps back to the previous card
onPrev: () => setActiveIndex((i) => i - 1),
});