Files
cycle-atlas/.agents/skills/gsap-web/examples/standalone-template.html
T

60 lines
2.7 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>loading</title>
<!-- GSAP core from CDN — no build step. Add plugins the same way if needed:
https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/ScrollTrigger.min.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>
<style>
:root { color-scheme: dark; }
* { margin: 0; box-sizing: border-box; }
body {
min-height: 100svh; display: grid; place-items: center;
background: radial-gradient(120% 120% at 50% 0%, #1b1f3b 0%, #0a0a12 60%);
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
color: #f4f5ff; overflow: hidden;
}
.stage { text-align: center; padding: 6vmin; }
.title { font-size: clamp(2.5rem, 9vw, 6rem); font-weight: 800; letter-spacing: -0.03em; line-height: 1; }
.sub { font-size: clamp(1rem, 3.2vw, 1.6rem); margin-top: 1.2rem; color: #aab; }
.cta { display: inline-block; margin-top: 2.4rem; padding: 0.9rem 1.8rem;
border-radius: 999px; background: #5b6cff; color: #fff; font-weight: 600; }
</style>
</head>
<body>
<main class="stage">
<h1 class="title">Ship it.</h1>
<p class="sub">A standalone, screenshot-verifiable GSAP scene.</p>
<a class="cta" href="#">Get started</a>
</main>
<script>
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
// ── One master timeline: a single playhead the harness can seek ──────────
const tl = gsap.timeline({ defaults: { ease: "power3.out", duration: 0.7 } });
if (reduce) {
// Reduced motion: no spatial movement — just make the final state visible.
gsap.set([".title", ".sub", ".cta"], { opacity: 1, y: 0 });
} else {
tl.from(".title", { yPercent: 110, opacity: 0, ease: "power4.out", duration: 0.9 })
.from(".sub", { y: 24, opacity: 0 }, "-=0.45")
.from(".cta", { scale: 0.9, opacity: 0, ease: "back.out(1.7)" }, "-=0.3");
}
// ── Seek harness: ?t=N freezes the timeline at N seconds for a clean shot ─
const t = new URLSearchParams(location.search).get("t");
if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); } // deterministic frozen frame
// no ?t → plays normally on load
// ── Ready signals for headless verification ──────────────────────────────
window.__ready = true;
document.title = "ready";
console.log("duration", tl.duration()); // end time for the ?t=<dur> shot
</script>
</body>
</html>