How to Implement GSAP Scroll Animations with the Sticky-Stack Pattern in Taste Skill
To implement GSAP scroll animations with the sticky-stack pattern in Taste Skill, create a React client component that registers the GSAP ScrollTrigger plugin, wraps all tween and pin instances in a gsap.context() bound to a local ref, checks useReducedMotion() to respect accessibility preferences, and pins each card at the viewport top while the subsequent card drives a scale-and-opacity tween via scrub: true.
Taste Skill treats motion as a first-class concern through the MOTION_INTENSITY dial documented in skills/taste-skill/SKILL.md. When MOTION_INTENSITY is set greater than 3, the skill expects real scroll-driven animations, and the sticky-stack pattern becomes the canonical approach for viewport-pinned card sequences. All implementations must satisfy the Dependency Verification rule in section 3.F and the mandatory reduced-motion guardrail defined in the skill.
Motion Intensity and Dependency Requirements
Before emitting any animation code, Taste Skill validates that the brief justifies scroll-driven motion. According to the skill document lines 60–66 in skills/taste-skill/SKILL.md, the MOTION_INTENSITY dial must be set high enough to trigger the sticky-stack pattern. If the brief suggests a static design, the dial stays at 3 or below and the component renders without animations.
The Dependency Verification rule in section 3.F requires that both gsap and gsap/ScrollTrigger exist in the project’s package.json. If either dependency is missing, the skill emits the install command before generating code:
npm install gsap
StickyStack Component Implementation
Because GSAP manipulates the DOM directly, the component must start with "use client" and be isolated from server rendering. The canonical implementation wraps all ScrollTrigger instances in gsap.context() so they are automatically cleaned up on unmount, preventing memory leaks.
// src/components/StickyStack.tsx
"use client";
import { useRef, useEffect } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useReducedMotion } from "motion/react";
gsap.registerPlugin(ScrollTrigger);
export function StickyStack({ cards }: { cards: React.ReactNode[] }) {
const ref = useRef<HTMLDivElement>(null);
const reduce = useReducedMotion();
useEffect(() => {
if (reduce || !ref.current) return;
const ctx = gsap.context(() => {
const cardEls = gsap.utils.toArray<HTMLElement>(".stack-card");
cardEls.forEach((card, i) => {
if (i === cardEls.length - 1) return;
ScrollTrigger.create({
trigger: card,
start: "top top",
endTrigger: cardEls[cardEls.length - 1],
end: "top top",
pin: true,
pinSpacing: false,
});
gsap.to(card, {
scale: 0.92,
opacity: 0.55,
ease: "none",
scrollTrigger: {
trigger: cardEls[i + 1],
start: "top bottom",
end: "top top",
scrub: true,
},
});
});
}, ref);
return () => ctx.revert();
}, [reduce]);
return (
<div ref={ref} className="relative">
{cards.map((card, i) => (
<div
key={i}
className="stack-card sticky top-0 min-h-[100dvh] flex items-center justify-center"
>
{card}
</div>
))}
</div>
);
}
Pinning and ScrollTrigger Architecture
The sticky-stack effect relies on two coordinated GSAP behaviors. First, each card except the last is pinned to the viewport top using ScrollTrigger.create() with start: "top top" and pin: true. Second, as implemented in Leonxlnx/taste-skill, the skill expects pinSpacing: false so the next card can slide over the pinned element without extra spacer elements.
The visual shrink effect is driven by the next card’s scroll position. A gsap.to() tween targets scale: 0.92 and opacity: 0.55 with ease: "none". Its nested scrollTrigger uses the subsequent card as the trigger with scrub: true, linking the transformation directly to scroll progress. This creates the smooth stack-shrink behavior described in the skill’s canonical skeleton.
Reduced Motion Safety
Taste Skill enforces a mandatory reduced-motion guardrail. The component imports useReducedMotion from motion/react and checks the value before initializing any GSAP logic. If the user prefers reduced motion, the useEffect returns early and the component renders the static card markup without pins or tweens. This satisfies the accessibility requirements defined in the skill document.
Using the Component in a Next.js Page
Once the component exists, import it into a page and pass an array of card elements:
// src/app/page.tsx
import { StickyStack } from "@/components/StickyStack";
export default function HomePage() {
const cards = [
<section className="bg-white dark:bg-zinc-900 text-center">
<h1 className="text-4xl md:text-6xl font-bold">Welcome</h1>
</section>,
<section className="bg-gray-50 dark:bg-zinc-800 text-center">
<h2 className="text-3xl md:text-5xl">Our Vision</h2>
</section>,
<section className="bg-white dark:bg-zinc-900 text-center">
<p className="max-w-[65ch] mx-auto">
A short description that fits the design-read.
</p>
</section>,
];
return <StickyStack cards={cards} />;
}
The skill’s canonical skeleton expects each card wrapper to carry the stack-card class and Tailwind utilities such as sticky top-0 min-h-[100dvh] so the DOM structure aligns with the GSAP selectors and viewport pinning logic.
Summary
- Taste Skill requires
MOTION_INTENSITY > 3to justify scroll-driven animations; otherwise the component falls back to static markup. - The Dependency Verification rule in section 3.F mandates that
gsapandgsap/ScrollTriggerexist inpackage.jsonbefore code is generated. - The component must be a client component with
"use client"because GSAP manipulates the DOM directly. - All ScrollTrigger instances and tweens must live inside a
gsap.context()bound to a local ref, with cleanup handled byctx.revert()on unmount. - Each card is pinned at
start: "top top"withpinSpacing: false, while the next card drives a scale and opacity tween viascrub: true. useReducedMotion()frommotion/reactguards the effect so users who prefer reduced motion receive accessible, static content.
Frequently Asked Questions
What dependencies are required for the GSAP sticky-stack pattern?
According to the Leonxlnx/taste-skill source code, the Dependency Verification rule in section 3.F requires gsap and gsap/ScrollTrigger to be listed in package.json. If they are missing, the skill emits npm install gsap before generating any animation code.
Why must the StickyStack component use "use client"?
GSAP and ScrollTrigger manipulate the DOM directly, which cannot occur during server-side rendering. The skill explicitly mandates a client-only component isolated from server rendering to prevent hydration mismatches and ensure the gsap.context() lifecycle runs correctly in the browser.
How does the implementation respect users who prefer reduced motion?
The component checks useReducedMotion() from motion/react at the top of its useEffect. When this hook returns true, the effect exits early and renders the static card markup without initializing any ScrollTrigger pins or tweens. This satisfies Taste Skill’s mandatory reduced-motion guardrail.
Where is the canonical sticky-stack pattern documented in Taste Skill?
The canonical GSAP sticky-stack skeleton is documented in skills/taste-skill/SKILL.md between lines 66 and 84. This section defines the official ScrollTrigger pattern, including the pinning logic at top top and the scale-and-opacity tween that agents are expected to emit.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →