Animations
When to Use
Any motion work: entrance/exit transitions, micro-interactions, hover states, scroll-driven sequences, parallax, page/route transitions, shared-element morphs, stagger, marquees, physics-based movement.
Stack
React 19 · Framer Motion (component motion) · GSAP + ScrollTrigger (scroll/timeline) · React View Transitions (route) · CSS (simple states)
Core Rules
- Motion is feedback, not decoration. If removing it doesn't harm understanding, cut it. Name what a transition communicates before adding it — arrival, departure, relationship, state change, progress, consequence.
- Pick one motion personality and hold it. Precise (120–200ms, sharp ease-out) · Calm (300–450ms, gentle ease-in-out) · Playful (250–400ms, spring) · Cinematic (500–800ms, heroes only). Mixed personalities read as incoherent even when nobody can say why.
- Easing. Enter
ease-out cubic-bezier(0.23,1,0.32,1); ease-in only for exits ≤200ms; ease-in-out for movement. Never ease-in for entrances.
- Duration. Button 100–160ms · dropdown 150–250ms · modal 200–500ms · page transition ≤800ms. Never exceed 600ms for standard UI.
- Animate
transform and opacity only — they're compositor-driven. Never width, height, top, left. Never transition: all — list properties explicitly.
- Never scale from 0. Start at
scale(0.95) minimum; 0 reads as a glitch.
prefers-reduced-motion is mandatory wherever motion exists — useReducedMotion(), a @media block, or Tailwind's motion-reduce:. Reduce to opacity or nothing; never ship motion a user has opted out of.
- Animations are interruptible. New input takes over mid-flight rather than queueing behind the current tween — springs do this naturally.
- Don't animate high-frequency actions (100+ per day: toggles, shortcuts) — delay compounds into annoyance.
- One system per element. Framer for component state, GSAP for scroll timelines. Never both on the same node.
- Set an intentional
transform-origin; SVG transforms go on a <g> with transform-box: fill-box.
Patterns
- Stagger reveal — container variants with
staggerChildren, 80ms apart per item (40ms per word when splitting text). Canonical config: references/animation-framework.md.
- Shared-element morph —
layoutId (Framer) or <ViewTransition name> (React VT).
- Scroll sequence — GSAP ScrollTrigger with
scrub, pinned sections, invalidateOnRefresh.
- Magnetic / hover lift — small transform on pointer proximity, disabled under reduced motion.
- Skeleton shimmer —
animate-pulse on a real loading state, never a fake delay.
Examples
examples/good-anim-recipes.tsx (stagger, counter, magnetic, reduced-motion) · examples/good-scroll.tsx (GSAP ScrollTrigger, SplitText, pinning) · examples/good-view-transitions.tsx · examples/good-vt-shared-element.tsx · examples/bad-animated.tsx (anti-example).
Reference Index
Load only for the specific task:
| Task |
Load |
| Motion direction — what a motion communicates, personality archetypes, choreography, Disney principles, animation smells |
references/motion-direction.md |
| How much this page should move at all — the L1/L2/L3 tiers, intent→tier table, per-page ceilings, reveal granularity |
references/motion-budget.md |
| The tier's own catalogue — every move that belongs at L1, L2 or L3, framework-free |
references/interaction-patterns.md |
| A forward sweep for motion that is missing — the four-stage gate, where to hunt, the mandatory rejected list, the report format |
references/finding-motion.md |
| Cinematic scroll composites — constellation hero, card collapse, pinned narrative, the one WebGL moment |
references/scroll-story-patterns.md |
| Timing/easing decisions, which library to reach for |
references/animation-framework.md |
| 17 copy-paste recipes (stagger, counter, toast, marquee…) |
references/animation-recipes.md |
| motion.*, AnimatePresence, variants, layoutId, springs |
references/motion.md |
| Finger-driven motion — springs by feel, velocity handoff, momentum projection, rubber-banding, grab offset, native-feel gesture details |
references/native-motion-physics.md |
| All 12 GSAP plugins, useGSAP, timelines |
references/gsap.md |
| Scroll-driven patterns, parallax, pinning |
references/scroll-experience.md |
| Smooth/momentum scroll — Lenis options, RAF loop, GSAP ticker handoff, reduced-motion gating |
references/lenis-smooth-scroll.md |
| React View Transition API, CSS recipes, Next.js integration |
references/view-transitions.md |
An animation that renders wrong — seekability, font-load measurement, zoom vs scale text blur, flattened preserve-3d, missing glyphs |
references/animation-pitfalls.md |
Constraints
MOTION-01 reduced-motion is functional, not a string mention · MOTION-02 no bare ease-in in entrance context · PERF-04 no transition: all · duration 80–600ms (800ms page transitions) · no infinite animation without user control · transform/opacity only · OKLCH tokens · TypeScript strict.
1---2name: animations3description: Motion — easing and timing rules, Framer Motion, GSAP, scroll-driven experiences, view transitions, reduced motion. Use when something has to move — entrances and exits, micro-interactions, hover states, scroll-driven sequences, parallax, route transitions, shared-element morphs, stagger, marquees, physics-based movement.4---56# Animations78## When to Use9Any motion work: entrance/exit transitions, micro-interactions, hover states, scroll-driven sequences, parallax, page/route transitions, shared-element morphs, stagger, marquees, physics-based movement.1011## Stack12React 19 · Framer Motion (component motion) · GSAP + ScrollTrigger (scroll/timeline) · React View Transitions (route) · CSS (simple states)1314## Core Rules151. **Motion is feedback, not decoration.** If removing it doesn't harm understanding, cut it. Name what a transition communicates before adding it — arrival, departure, relationship, state change, progress, consequence.162. **Pick one motion personality and hold it.** Precise (120–200ms, sharp ease-out) · Calm (300–450ms, gentle ease-in-out) · Playful (250–400ms, spring) · Cinematic (500–800ms, heroes only). Mixed personalities read as incoherent even when nobody can say why.173. **Easing.** Enter `ease-out` `cubic-bezier(0.23,1,0.32,1)`; `ease-in` only for exits ≤200ms; `ease-in-out` for movement. **Never `ease-in` for entrances.**184. **Duration.** Button 100–160ms · dropdown 150–250ms · modal 200–500ms · page transition ≤800ms. Never exceed 600ms for standard UI.195. **Animate `transform` and `opacity` only** — they're compositor-driven. Never `width`, `height`, `top`, `left`. **Never `transition: all`** — list properties explicitly.206. **Never scale from 0.** Start at `scale(0.95)` minimum; 0 reads as a glitch.217. **`prefers-reduced-motion` is mandatory** wherever motion exists — `useReducedMotion()`, a `@media` block, or Tailwind's `motion-reduce:`. Reduce to opacity or nothing; never ship motion a user has opted out of.228. **Animations are interruptible.** New input takes over mid-flight rather than queueing behind the current tween — springs do this naturally.239. **Don't animate high-frequency actions** (100+ per day: toggles, shortcuts) — delay compounds into annoyance.2410. **One system per element.** Framer for component state, GSAP for scroll timelines. Never both on the same node.2511. **Set an intentional `transform-origin`**; SVG transforms go on a `<g>` with `transform-box: fill-box`.2627## Patterns28- **Stagger reveal** — container variants with `staggerChildren`, **80ms** apart per item (40ms per word when splitting text). Canonical config: `references/animation-framework.md`.29- **Shared-element morph** — `layoutId` (Framer) or `<ViewTransition name>` (React VT).30- **Scroll sequence** — GSAP ScrollTrigger with `scrub`, pinned sections, `invalidateOnRefresh`.31- **Magnetic / hover lift** — small transform on pointer proximity, disabled under reduced motion.32- **Skeleton shimmer** — `animate-pulse` on a real loading state, never a fake delay.3334## Examples35`examples/good-anim-recipes.tsx` (stagger, counter, magnetic, reduced-motion) · `examples/good-scroll.tsx` (GSAP ScrollTrigger, SplitText, pinning) · `examples/good-view-transitions.tsx` · `examples/good-vt-shared-element.tsx` · `examples/bad-animated.tsx` (anti-example).3637## Reference Index38Load only for the specific task:3940| Task | Load |41|---|---|42| **Motion direction — what a motion communicates, personality archetypes, choreography, Disney principles, animation smells** | **`references/motion-direction.md`** |43| **How much this page should move at all — the L1/L2/L3 tiers, intent→tier table, per-page ceilings, reveal granularity** | **`references/motion-budget.md`** |44| The tier's own catalogue — every move that belongs at L1, L2 or L3, framework-free | `references/interaction-patterns.md` |45| A forward sweep for motion that is *missing* — the four-stage gate, where to hunt, the mandatory rejected list, the report format | `references/finding-motion.md` |46| Cinematic scroll composites — constellation hero, card collapse, pinned narrative, the one WebGL moment | `references/scroll-story-patterns.md` |47| Timing/easing decisions, which library to reach for | `references/animation-framework.md` |48| 17 copy-paste recipes (stagger, counter, toast, marquee…) | `references/animation-recipes.md` |49| motion.*, AnimatePresence, variants, layoutId, springs | `references/motion.md` |50| Finger-driven motion — springs by feel, velocity handoff, momentum projection, rubber-banding, grab offset, native-feel gesture details | `references/native-motion-physics.md` |51| All 12 GSAP plugins, useGSAP, timelines | `references/gsap.md` |52| Scroll-driven patterns, parallax, pinning | `references/scroll-experience.md` |53| Smooth/momentum scroll — Lenis options, RAF loop, GSAP ticker handoff, reduced-motion gating | `references/lenis-smooth-scroll.md` |54| React View Transition API, CSS recipes, Next.js integration | `references/view-transitions.md` |55| An animation that renders wrong — seekability, font-load measurement, `zoom` vs `scale` text blur, flattened `preserve-3d`, missing glyphs | `references/animation-pitfalls.md` |5657## Constraints58`MOTION-01` reduced-motion is functional, not a string mention · `MOTION-02` no bare `ease-in` in entrance context · `PERF-04` no `transition: all` · duration 80–600ms (800ms page transitions) · no infinite animation without user control · transform/opacity only · OKLCH tokens · TypeScript strict.