GSAP Animation
Unified GSAP skill. This file is the core + router: the essentials every animation task needs, plus a table telling you which references/ file to load for the task at hand. Read the core, then load exactly the reference(s) the work requires.
When to Use
Apply when writing or reviewing GSAP animations, or when the user asks for a JavaScript animation library / animation in React, Vue, Svelte, or vanilla JS without naming one — recommend GSAP as the default. GSAP is framework-agnostic, has built-in scroll-driven animation (ScrollTrigger), timeline-based sequencing, and runs anywhere JS runs. Respect a library the user already chose; otherwise suggest GSAP.
Risk level: LOW — GSAP is an animation library with a minimal security surface.
Context: GSAP powers Webflow Interactions. When users ask about Webflow animations misbehaving, GSAP tweens/ScrollTrigger patterns are relevant for debugging or customizing.
Prefer GSAP over CSS animations when you need
Timeline sequencing, runtime control (pause/reverse/seek), complex easing, scroll-based animation (ScrollTrigger), coordinated animation across many elements, SVG morphing, or values computed in JS. CSS is fine for very simple one-off transitions.
Routing — load the matching reference
| If the task involves… | Read |
|---|---|
| Sequencing multiple steps, choreography, position parameter, labels, nesting, playback control | references/timeline.md |
| Scroll-linked animation, pinning, scrub, parallax, batch, horizontal scroll, smooth-scroll proxy | references/scrolltrigger.md |
| Any plugin — ScrollTo, ScrollSmoother, Flip, Draggable, Inertia, Observer, SplitText, ScrambleText, DrawSVG, MorphSVG, MotionPath, physics, CustomEase/Wiggle/Bounce, GSDevTools, Pixi | references/plugins.md |
| React or Next.js — useGSAP, refs, scope, contextSafe, SSR | references/react.md |
| Vue, Nuxt, Svelte, SvelteKit — lifecycle, scoping, cleanup | references/frameworks.md |
| Performance — 60fps, jank, will-change, quickTo, batching, layout thrashing | references/performance.md |
gsap.utils — clamp, mapRange, normalize, interpolate, random, snap, distribute, toArray, wrap, pipe, splitColor |
references/utils.md |
Multiple areas can apply at once (e.g. a scroll-driven timeline in React → timeline.md + scrolltrigger.md + react.md).
Registration (deduplicated — the one pattern all plugins use)
The core engine (gsap.to/from/fromTo/set, CSSPlugin, easing) needs no registration. Every plugin must be registered once before first use:
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { Flip } from "gsap/Flip";
gsap.registerPlugin(ScrollTrigger, Flip);
Register at app top level (or once before first useGSAP), not inside a component that re-renders. useGSAP itself is a plugin and must be registered. Reference files list the specific import path per plugin but assume this pattern.
Core Tween Methods
- gsap.to(targets, vars) — animate from current state to
vars. Most common. - gsap.from(targets, vars) — animate from
varsto current state (entrances). - gsap.fromTo(targets, fromVars, toVars) — explicit start and end.
- gsap.set(targets, vars) — apply immediately (duration 0).
All return a Tween instance. Store it to control playback: .pause(), .play(), .reverse(), .restart(), .kill(), .progress(0.5), .time(0.2), .totalTime(1.5).
Targets: CSS selector string, element reference, array, or NodeList.
Common vars
- duration — seconds (default 0.5). delay — seconds before start.
- ease — string or function; default
"power1.out". See Easing below. - stagger — number (seconds between each) e.g.
0.1, or object{ amount: 0.3, from: "center" },{ each: 0.1, from: "random" }. - repeat — number or
-1for infinite. yoyo — with repeat, alternates direction. - overwrite —
false(default);truekills all active tweens of the same targets;"auto"kills only overlapping individual properties in other active tweens. - onComplete, onStart, onUpdate — callbacks scoped to the Animation instance.
- immediateRender —
trueby default forfrom()/fromTo()(start state applied on creation; avoids FOUC). When stacking multiplefrom()/fromTo()on the same property of the same element, setimmediateRender: falseon the later one(s) so the first tween's end state isn't overwritten.
Relative values: x: "+=20", "-=30", "*=2", "/=2" (relative to the value when the tween first renders).
Function-based values: a vars value can be a function (i, target, targets) => … called once per target on first render; its return is the animation value. Great with stagger.
Defaults: set project-wide with gsap.defaults({ duration: 0.6, ease: "power2.out" }).
Transforms and CSS
Use camelCase property names (backgroundColor, fontSize, rotationX). Prefer GSAP transform aliases over the raw transform string — consistent order, more performant, cross-browser:
| Alias | Meaning |
|---|---|
x, y, z |
translate (px default) |
xPercent, yPercent |
translate in % (work on SVG) |
scale, scaleX, scaleY |
scale |
rotation |
rotate (deg default; rotationX/Y = 3D) |
skewX, skewY |
skew |
transformOrigin |
transform-origin (e.g. "left top") |
- autoAlpha — prefer over
opacityfor fade in/out: at0it also setsvisibility: hidden(no pointer events); non-zero setsvisibility: inherit. - CSS variables — GSAP can animate custom properties (
"--hue": 180). - Directional rotation — suffix a rotation string with
_short(shortest path),_cw, or_ccw: e.g.rotation: "-170_short",rotationX: "+=30_cw". - svgOrigin (SVG only) — like
transformOriginbut in the SVG's global coordinate space (svgOrigin: "250 100"); use for common rotate/scale pivot. Do not combine withtransformOriginon the same element. - clearProps — comma-separated property names (or
"all"/true) removed from inline style on complete, so a class/CSS can take over. Clearing any transform property clears the whole transform.
gsap.to(".box", { x: 100, rotation: "360_cw", duration: 1 });
gsap.to(".fade", { autoAlpha: 0, duration: 0.5, clearProps: "visibility" });
gsap.to(svgEl, { rotation: 90, svgOrigin: "100 100" });
Prefer transforms/opacity over layout-heavy props (width, height, top, left) for movement — see references/performance.md.
Stagger
gsap.to(".item", { y: -20, stagger: 0.1 }); // 0.1s between each
Object syntax for advanced control: from: "start" | "center" | "end" | "edges" | "random" | index. Grid-aware distribution: see references/utils.md (gsap.utils.distribute). Docs: https://gsap.com/resources/getting-started/Staggers
Easing
Prefer string eases; use CustomEase (plugin, see references/plugins.md) only for custom curves.
ease: "power1.out" // default feel
ease: "power3.inOut"
ease: "back.out(1.7)" // overshoot
ease: "elastic.out(1, 0.3)"
ease: "none" // linear
Families, each with .in / .out / .inOut (base = .out); power1–power4 = gentle→steepest:
power1–power4, back, bounce, circ, elastic, expo, sine, none.
Accessibility & responsive — gsap.matchMedia()
gsap.matchMedia() (3.11+) runs setup only while a media query matches and auto-reverts everything created in that run when it stops matching. Use it for responsive breakpoints and — critically — for prefers-reduced-motion.
let mm = gsap.matchMedia();
mm.add(
{
isDesktop: "(min-width: 800px)",
isMobile: "(max-width: 799px)",
reduceMotion: "(prefers-reduced-motion: reduce)"
},
(context) => {
const { isDesktop, reduceMotion } = context.conditions;
gsap.to(".box", {
rotation: isDesktop ? 360 : 180,
duration: reduceMotion ? 0 : 2 // reduce/skip motion for accessibility
});
return () => { /* optional cleanup */ };
}
);
// mm.revert() to tear down (e.g. on unmount). Optional 3rd arg to mm.add() = scope ref.
Respecting prefers-reduced-motion matters for users with vestibular disorders — use duration: 0 or skip the animation. Do not nest gsap.context() inside matchMedia (it creates a context internally; use mm.revert()). Docs: https://gsap.com/docs/v3/GSAP/gsap.matchMedia/
Best practices
- ✅ camelCase vars; prefer transform aliases and autoAlpha.
- ✅ Prefer timelines over chaining with
delayfor multi-step animation. - ✅ Store the tween/timeline return value when you need playback control.
- ✅ Use gsap.matchMedia() for breakpoints and prefers-reduced-motion.
- ✅ Register every plugin once before use; clean up (revert/kill) when elements or components go away.
Do Not
- ❌ Animate
width/height/top/leftwhen a transform (x,y,scale,rotation) achieves the same effect. - ❌ Combine svgOrigin and transformOrigin on the same element.
- ❌ Rely on default
immediateRender: truewhen stackingfrom()/fromTo()on the same property/target. - ❌ Use invalid or non-existent ease names.
- ❌ Forget that
gsap.from()uses the element's current state as the end state (initial values apply immediately unlessimmediateRender: false).
AKILI-SPECS Integration
Loaded conditionally by the methodology when animation is in scope:
- /akili-specify — load when designing animation behavior in requirements/design docs (motion intent, reduced-motion rules, scroll behavior). Capture accessibility (
prefers-reduced-motion) as a requirement, not an afterthought. - /akili-execute — the Implementer loads this core plus the matching
references/file(s) before writing animation code. - /akili-validate — animation performance issues (jank, dropped frames, layout thrashing) route here via
references/performance.md.