# Gsap Animation

> Trigger: GSAP animation work — tweens, timelines, scroll-driven animation, plugins, React/framework integration, performance. Unified GSAP skill: read the core guidance here, then load the matching references/ file for the task.

- Skill: `juankcadavid/gsap-animation` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add juankcadavid/gsap-animation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/juankcadavid/gsap-animation/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: JuankCadavid (https://skillmd.com/u/juankcadavid)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/juankcadavid/gsap-animation

---


# 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:

```javascript
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 `vars` to 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 `-1` for infinite. **yoyo** — with repeat, alternates direction.
- **overwrite** — `false` (default); `true` kills 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** — `true` by default for `from()`/`fromTo()` (start state applied on creation; avoids FOUC). When **stacking multiple `from()`/`fromTo()` on the same property of the same element**, set **`immediateRender: false`** on 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 `opacity` for fade in/out: at `0` it also sets `visibility: hidden` (no pointer events); non-zero sets `visibility: 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 `transformOrigin` but in the SVG's global coordinate space (`svgOrigin: "250 100"`); use for common rotate/scale pivot. Do **not** combine with `transformOrigin` on 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.

```javascript
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

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

```javascript
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**.

```javascript
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 `delay` for 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`/`left` when a transform (`x`, `y`, `scale`, `rotation`) achieves the same effect.
- ❌ Combine **svgOrigin** and **transformOrigin** on the same element.
- ❌ Rely on default `immediateRender: true` when stacking `from()`/`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 unless `immediateRender: 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`.

