# How to Implement GSAP Scroll Animations with the Sticky-Stack Pattern in Taste Skill

> Learn to implement GSAP scroll animations with the sticky-stack pattern in Taste Skill. Discover how to create engaging scroll effects for your web projects.

- Repository: [Leon Lin/taste-skill](https://github.com/Leonxlnx/taste-skill)
- Tags: how-to-guide
- Published: 2026-06-06

---

**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`](https://github.com/Leonxlnx/taste-skill/blob/main/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`](https://github.com/Leonxlnx/taste-skill/blob/main/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`](https://github.com/Leonxlnx/taste-skill/blob/main/package.json). If either dependency is missing, the skill emits the install command before generating code:

```bash
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.

```tsx
// 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:

```tsx
// 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 > 3` to justify scroll-driven animations; otherwise the component falls back to static markup.
- The **Dependency Verification** rule in section 3.F mandates that `gsap` and `gsap/ScrollTrigger` exist in [`package.json`](https://github.com/Leonxlnx/taste-skill/blob/main/package.json) before 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 by `ctx.revert()` on unmount.
- Each card is pinned at `start: "top top"` with `pinSpacing: false`, while the next card drives a scale and opacity tween via `scrub: true`.
- `useReducedMotion()` from `motion/react` guards 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`](https://github.com/Leonxlnx/taste-skill/blob/main/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`](https://github.com/Leonxlnx/taste-skill/blob/main/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.