Web Animations Medium
Scroll Trigger Story
Section-by-section storytelling using viewport-triggered reveals.
Open in Lab
MCP
gsap scrolltrigger
Targets: JS HTML
Code
body {
margin: 0;
background: #060b13;
color: #f2f6fe;
font-family: "Avenir Next", sans-serif;
}
.back {
position: fixed;
top: 14px;
left: 14px;
color: #86e8ff;
text-decoration: none;
z-index: 10;
}
.panel {
min-height: 100vh;
width: min(900px, 92%);
margin: 0 auto;
display: grid;
align-content: center;
gap: .7rem;
border-bottom: 1px solid #24314a;
}
h1,
h2,
p {
margin: 0;
}
p {
color: #c1cde1;
max-width: 60ch;
}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;
}
if (!window.MotionPreference.prefersReducedMotion() && window.gsap && window.ScrollTrigger) {
window.gsap.registerPlugin(window.ScrollTrigger);
window.gsap.utils.toArray(".scroll-reveal-section").forEach((panel) => {
window.gsap.fromTo(
panel,
{ y: 80, opacity: 0.15 },
{
y: 0,
opacity: 1,
ease: "power2.out",
scrollTrigger: { trigger: panel, start: "top 82%", end: "top 35%", scrub: 1 },
}
);
});
}<!doctype html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo 03 - Scroll Story</title><link rel="stylesheet" href="style.css" /></head>
<body>
<a class="back" href="../">Back</a>
<main>
<section class="panel scroll-reveal-section"><h1>Scroll Story</h1><p>Reveal each panel as it enters viewport.</p></section>
<section class="panel scroll-reveal-section"><h2>Act I</h2><p>Set context with atmosphere and typography.</p></section>
<section class="panel scroll-reveal-section"><h2>Act II</h2><p>Increase depth and motion rhythm.</p></section>
<section class="panel scroll-reveal-section"><h2>Act III</h2><p>Resolve to CTA with cleaner composition.</p></section>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<script src="script.js"></script>
</body></html>Scroll Trigger Story
Section-by-section storytelling using viewport-triggered reveals.
Source
- Repository:
libs-gen - Original demo id:
03-scroll-trigger-story
Notes
Section-by-section storytelling using viewport-triggered reveals.