Web Animations Medium
Card Stack Choreography
Looping card stack transforms for staged reveal sequences.
Open in Lab
MCP
gsap card-motion
Targets: JS HTML
Code
body {
margin: 0;
min-height: 100vh;
display: grid;
place-content: center;
background: #060a12;
color: #f2f7ff;
font-family: "Avenir Next", sans-serif;
}
.back {
position: fixed;
top: 14px;
left: 14px;
color: #86e8ff;
text-decoration: none;
}
h1 {
text-align: center;
margin: 0 0 1rem;
}
.stack {
position: relative;
width: min(88vw, 520px);
height: 340px;
}
.card {
position: absolute;
inset: 0;
border-radius: 18px;
display: grid;
place-content: center;
font-size: 2rem;
font-weight: 800;
border: 1px solid #344865;
background: linear-gradient(150deg, #223652, #6f3eb2);
}
.card:nth-child(2) {
background: linear-gradient(150deg, #2a4454, #2e6cb3);
}
.card:nth-child(3) {
background: linear-gradient(150deg, #4d2f69, #6c3eb2);
}
.card:nth-child(4) {
background: linear-gradient(150deg, #3f4b7d, #486bb0);
}if (!window.MotionPreference) {
const __mql = window.matchMedia("(prefers-reduced-motion: reduce)");
const __listeners = new Set();
const MotionPreference = {
prefersReducedMotion() {
return __mql.matches;
},
setOverride(value) {
const reduced = Boolean(value);
document.documentElement.classList.toggle("reduced-motion", reduced);
window.dispatchEvent(new CustomEvent("motion-preference", { detail: { reduced } }));
for (const listener of __listeners) {
try {
listener({ reduced, override: reduced, systemReduced: __mql.matches });
} catch {}
}
},
onChange(listener) {
__listeners.add(listener);
try {
listener({
reduced: __mql.matches,
override: null,
systemReduced: __mql.matches,
});
} catch {}
return () => __listeners.delete(listener);
},
getState() {
return { reduced: __mql.matches, override: null, systemReduced: __mql.matches };
},
};
window.MotionPreference = MotionPreference;
}
const reduced = window.MotionPreference.prefersReducedMotion();
const cards = Array.from(document.querySelectorAll(".card"));
cards.forEach((c, i) => {
c.style.transform = `translateY(${i * 10}px) scale(${1 - i * 0.03})`;
c.style.zIndex = String(cards.length - i);
});
if (!reduced && window.gsap) {
const tl = window.gsap.timeline({ repeat: -1, repeatDelay: 0.6 });
cards.forEach((c, i) => {
tl.to(
c,
{ y: -220, rotation: i % 2 ? 9 : -9, opacity: 0, duration: 0.45, ease: "power2.in" },
i * 0.22
).set(c, { y: 0, rotation: 0, opacity: 1 }, i * 0.22 + 0.46);
});
}<!doctype html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo 07 - Card Stack Choreo</title><link rel="stylesheet" href="style.css" /></head>
<body>
<a class="back" href="../">Back</a>
<main>
<h1>Card Stack Choreography</h1>
<div class="stack" id="stack">
<article class="card">Card 01</article>
<article class="card">Card 02</article>
<article class="card">Card 03</article>
<article class="card">Card 04</article>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body></html>Card Stack Choreography
Looping card stack transforms for staged reveal sequences.
Source
- Repository:
libs-gen - Original demo id:
07-card-stack-choreo
Notes
Looping card stack transforms for staged reveal sequences.