Web Animations Medium
GSAP Hero Intro
Headline and CTA entrance timeline with ambient motion layers.
Open in Lab
MCP
gsap css vanilla-js
Targets: JS HTML
Code
body {
margin: 0;
font-family: "Avenir Next", sans-serif;
background: #070b14;
color: #f3f7ff;
}
.back {
position: fixed;
top: 14px;
left: 14px;
color: #86e8ff;
text-decoration: none;
z-index: var(--z-ui);
}
.hero {
min-height: 100vh;
display: grid;
place-content: center;
text-align: center;
gap: 0.8rem;
position: relative;
overflow: hidden;
}
.ambient {
position: absolute;
inset: 0;
z-index: var(--z-bg);
}
.orb {
position: absolute;
border-radius: 50%;
filter: blur(10px);
opacity: 0.6;
}
.a {
width: 30vw;
height: 30vw;
left: 8%;
top: 20%;
background: #6cc9ff55;
}
.b {
width: 24vw;
height: 24vw;
right: 12%;
top: 12%;
background: #d077ff55;
}
.c {
width: 18vw;
height: 18vw;
right: 22%;
bottom: 14%;
background: #ffe7a355;
}
.kicker {
margin: 0;
color: #86e8ff;
letter-spacing: .12em;
text-transform: uppercase;
}
h1 {
margin: 0;
font-size: clamp(2.2rem, 8vw, 5rem);
}
.subtitle {
margin: 0;
color: #c0cee2;
}
.actions {
display: flex;
gap: .7rem;
justify-content: center;
flex-wrap: wrap;
}
button {
border: 0;
border-radius: 999px;
padding: .75rem 1rem;
font-weight: 700;
cursor: pointer;
}
button:first-child {
background: #1f2f49;
color: #fff;
}
button:last-child {
background: #86e8ff;
color: #062030;
}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 toggle = document.getElementById("toggle");
let reduced = window.MotionPreference.prefersReducedMotion();
const active = [];
function track(animation) {
active.push(animation);
return animation;
}
function teardown() {
active.forEach((animation) => animation.kill());
active.length = 0;
document.querySelectorAll(".orb").forEach((n) => {
n.style.transform = "none";
});
document.querySelectorAll(".kicker, h1, .subtitle, .actions").forEach((n) => {
n.style.opacity = "1";
n.style.transform = "none";
});
}
function play() {
if (reduced || !window.gsap) return;
const tl = track(window.gsap.timeline());
tl.from(".kicker", { y: 24, opacity: 0, duration: 0.4 })
.from("h1", { y: 40, opacity: 0, duration: 0.6, ease: "power3.out" }, "<0.08")
.from(".subtitle", { y: 18, opacity: 0, duration: 0.45 }, "<0.08")
.from(".actions", { y: 20, opacity: 0, duration: 0.4 }, "<0.12");
track(
window.gsap.to(".a", { x: 24, y: -16, duration: 4, repeat: -1, yoyo: true, ease: "sine.inOut" })
);
track(
window.gsap.to(".b", { x: -22, y: 16, duration: 5, repeat: -1, yoyo: true, ease: "sine.inOut" })
);
track(
window.gsap.to(".c", {
x: 16,
y: -12,
duration: 3.2,
repeat: -1,
yoyo: true,
ease: "sine.inOut",
})
);
}
function apply() {
teardown();
toggle.textContent = reduced ? "Enable motion" : "Disable motion";
play();
}
toggle.addEventListener("click", () => {
reduced = !reduced;
apply();
});
apply();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo 02 - GSAP Hero Intro</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="back" href="../">Back</a>
<main class="hero scene-intro-hero">
<div class="ambient" aria-hidden="true">
<span class="orb a"></span>
<span class="orb b"></span>
<span class="orb c"></span>
</div>
<p class="kicker">Demo 02</p>
<h1>GSAP Hero Intro</h1>
<p class="subtitle">Headline, CTA, and ambient layers in a controlled timeline.</p>
<div class="actions">
<button id="toggle">Disable motion</button>
<button>Start Project</button>
</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>GSAP Hero Intro
Headline and CTA entrance timeline with ambient motion layers.
Source
- Repository:
libs-gen - Original demo id:
02-gsap-hero-intro
Notes
Headline and CTA entrance timeline with ambient motion layers.