Animated Stack
Cards cycle to the front on an auto-loop using AnimatePresence. The front card animates in from above; old front exits downward.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
Animated StackCards cycle to the front on an auto-loop using AnimatePresence. The front card animates in from above; old front exits downward.
"use client";
import { useEffect, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
type AnimatedStackProps = {
items?: { label: string; color: string }[];
interval?: number;
};
const DEFAULT_ITEMS = [
{ label: "Alpha", color: "#6366f1" },
{ label: "Beta", color: "#8b5cf6" },
{ label: "Gamma", color: "#a855f7" },
];
export function AnimatedStack({
items = DEFAULT_ITEMS,
interval = 2000,
}: AnimatedStackProps) {
const [frontIndex, setFrontIndex] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setFrontIndex((i) => (i + 1) % items.length);
}, interval);
return () => clearInterval(id);
}, [items.length, interval]);
const ordered = items.map((item, i) => {
const dist = (i - frontIndex + items.length) % items.length;
return { ...item, dist, originalIndex: i };
});
return (
<div className="mp-animated-stack">
{ordered
.slice()
.sort((a, b) => b.dist - a.dist)
.map(({ label, color, dist, originalIndex }) => (
<AnimatePresence key={originalIndex} initial={false}>
<motion.div
key={`${originalIndex}-${frontIndex}`}
className="mp-animated-stack-card"
style={{
background: color,
zIndex: items.length - dist,
position: dist === 0 ? "relative" : "absolute",
top: dist === 0 ? 0 : dist * 6,
left: dist === 0 ? 0 : dist * 4,
}}
initial={dist === 0 ? { scale: 0.85, opacity: 0, y: -16 } : false}
animate={{
scale: 1 - dist * 0.04,
opacity: 1 - dist * 0.28,
y: 0,
}}
exit={{ scale: 0.8, opacity: 0, y: 16 }}
transition={{ duration: 0.32, ease: [0.22, 1, 0.36, 1] }}
>
<span className="mp-animated-stack-label">{label}</span>
</motion.div>
</AnimatePresence>
))}
</div>
);
}Install
npm install framer-motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| items | { label: string; color: string }[] | 3 built-in cards | Stack items |
| interval | number | 2000 | Cycle interval in ms |