| name | motion |
| description | Activate when a user explicitly requests scroll-driven storytelling, GSAP animations, complex motion choreography, or highly kinetic web experiences that go beyond standard CSS transitions. |
| version | 1 |
| category | frontend-code |
| outputs | code |
Motion
Purpose
This skill generates web interfaces where animation and scroll-driven behavior are central to the user experience — not decorative additions. It covers GSAP ScrollTrigger, pinned sections, horizontal scroll hijacking, text-reveal animations, card-stacking sequences, and spring-physics interaction states. The output must implement real working animation code, not CSS-only transitions labeled as "motion."
When to Activate
- User explicitly requests GSAP, ScrollTrigger, or scroll-driven animations
- User requests an "interactive" or "animated" landing page with cinematic scroll behavior
- User asks for pinned sections, horizontal scroll, or text-reveal scroll effects
- Motion is described as the primary design feature (not a secondary enhancement)
When Not to Activate
- Motion is one enhancement among many (use
frontend with MOTION 7–8)
- The user wants static components with CSS hover effects only
- The project is a data dashboard where animation would harm usability or screen reader access
- The user has not mentioned motion or animation at all
Core Principles
-
Separate animation libraries from each other. GSAP and Framer Motion must not be imported in the same component. GSAP handles isolated scroll sequences and canvas backgrounds. Framer Motion handles UI-level component interactions. Mixing them in the same component tree causes conflicting render cycles.
-
Scroll animations use ScrollTrigger or IntersectionObserver, not scroll event listeners. window.addEventListener('scroll') causes continuous reflows and hurts mobile performance. All scroll-driven behavior uses GSAP ScrollTrigger or IntersectionObserver.
-
Pinned sections require explicit container height. When pinning a section during scroll, the parent container must have a height proportional to the scroll distance required. Pinned sections without explicit height cause layout collapse on mobile.
-
Stagger requires correct Parent/Child structure. When using Framer Motion staggerChildren, the parent motion.div with variants and all child motion.div elements must live in the same Client Component tree. Data fetched asynchronously passes as props into a centralized Parent Motion wrapper.
-
All animations are GPU-safe. Animate only transform and opacity. No top, left, width, height transitions. Apply will-change: transform only on actively animating elements — never globally.
-
Respect prefers-reduced-motion. Wrap all animation initialization in a check: window.matchMedia('(prefers-reduced-motion: reduce)').matches. If true, skip animation initialization or provide instant transitions as fallback. This is a non-negotiable accessibility requirement.
-
GSAP and Three.js require strict useEffect cleanup. Any useEffect that initializes GSAP timelines, ScrollTrigger instances, or Three.js renderers must include a cleanup function that kills timelines and disposes renderers. Missing cleanup causes memory leaks and ghost scroll behaviors.
-
Mobile performance requires reduced animation. Below 768px, reduce or disable complex scroll sequences. backdrop-filter: blur must never be applied to scrolling containers. Grain/noise overlays must use pseudo-elements.
Configuration
| Parameter | Options | Default |
|---|
| MOTION_LIB | gsap / framer-motion / css-only | gsap |
| SCROLL_MODE | scroll-trigger / intersection-observer | scroll-trigger |
| MOBILE_FALLBACK | disable-animations / reduce-animations / full | reduce-animations |
| PARALLAX | enabled / disabled | enabled |
| PAGE_STRUCTURE | aida / editorial / scrolltelling / dashboard | aida |
Workflow
-
Verify dependencies. Check package.json for gsap, @gsap/react, framer-motion, or three. Output install commands for any missing libraries before writing animation code.
-
Plan the animation sequence. Before coding, list: which sections are pinned, which elements have scroll-entry animations, which text blocks have scrub/reveal effects, and any horizontal scroll zones. Define the scroll progression as a narrative with a clear start and end state.
-
Separate animation components. Extract all animation-heavy components into isolated Client Components ("use client"). Static layout remains in Server Components. GSAP and Framer Motion must not coexist in the same component.
-
Implement scroll behaviors.
- Scroll entry:
translateY(20px) opacity: 0 → resolved, IntersectionObserver
- Pinned sections: set explicit container height proportional to scroll distance
- Text scrub: wrap text in span-per-word structure, animate
opacity: 0.1 → 1.0 on scroll progress
- Card stacking: cards with
sticky: top-0 and increasing z-index, each pinned during scroll
- Horizontal scroll: convert vertical scroll to horizontal translation on a container
-
Implement prefers-reduced-motion guard. Before any animation initialization: if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
-
Add mobile handling. At 768px or below: disable or simplify complex scroll sequences. Ensure pinned sections do not cause content overlap. Remove backdrop-filter: blur from scrolling containers.
-
Write cleanup functions. Every useEffect with GSAP or Three.js: return () => { timeline.kill(); ScrollTrigger.getAll().forEach(t => t.kill()); }
-
Run Quality Gates.
Page Structure Reference (AIDA default)
When the user requests a full animated page without specifying structure, use AIDA:
- Attention (Hero): Cinematic, clean opening — one strong focal point, short headline, primary CTA, strong background treatment
- Interest (Features/Bento): High-density grid or interactive typographic section — where scroll-entry stagger and pinned reveals live
- Desire (GSAP scroll sequence): Pinned storytelling section — horizontal scroll, scrubbing text reveal, or card stacking
- Action (CTA/Footer): High-contrast closing section — single primary action, supporting trust cue
Each section has a scroll behavior. The page must feel like a sequence of chapters, not a stack of boxes.
Forbidden Patterns
window.addEventListener('scroll') for any animation trigger
- GSAP and Framer Motion imported in the same component
useEffect without cleanup for GSAP, ScrollTrigger, or Three.js initialization
backdrop-filter: blur on scrolling containers
- Animation of
top, left, width, or height properties
- Global
will-change: transform applied without active animation
- Missing
prefers-reduced-motion support
- Text scrub animations where text starts at
opacity: 0 (use 0.1 minimum)
- Scroll hijacking that makes the page non-navigable without JavaScript
- GSAP
scrollTrigger instances left alive after component unmount
Output Requirements
- All animation components isolated in Client Components with
"use client"
prefers-reduced-motion guard present in every animation initialization
useEffect cleanup functions present for all GSAP and Three.js instances
- Scroll sequence documented in a brief comment block at top of animation files
- Mobile fallback behavior explicitly implemented
- Apply
complete behavior — no truncation
Quality Gates
Failure Modes
"Ghost scroll": After navigating away, scroll effects from the previous page still fire. Cause: ScrollTrigger instances not cleaned up. Fix: add ScrollTrigger.getAll().forEach(t => t.kill()) in useEffect cleanup.
"Mobile layout collapse": Pinned sections cause content stacking on narrow screens. Cause: pinned container height not set, or mobile fallback missing. Fix: set explicit container height; disable pin on mobile via ScrollTrigger matchMedia.
"Invisible text": Text scrub starts at opacity: 0 — content is inaccessible if JS fails. Fix: use opacity: 0.1 as the initial state so content is always minimally readable.
"Library conflict": Framer Motion layout animations and GSAP fight on the same element — elements jitter or teleport. Fix: separate library domains strictly. GSAP for scroll sequences on a parent container; Framer Motion for UI micro-interactions on leaf components.
Response Format
Lead with a brief animation map (4–6 bullet points naming each scroll behavior and its trigger). Then deliver complete, runnable code. Apply complete behavior.