Animation System Architect
Build performant, accessible animation systems using Framer Motion, GSAP, the View Transitions API, and spring physics for fluid UI experiences.
Activation Triggers
Activate on: page transitions, layout animations, scroll-triggered animations, micro-interactions, spring physics, motion.div, gsap.to, View Transition API, shared element transitions, staggered list animations.
NOT for: CSS-only hover/focus effects -- use css-in-js-architect. Canvas/WebGL rendering -- use creative-coding-expert. Lottie file playback -- use standard Lottie integration.
Quick Start
- Choose your engine -- Framer Motion for React declarative, GSAP for timeline-heavy or non-React, View Transitions API for cross-page navigation.
- Animate transforms and opacity only -- these run on the compositor thread (GPU), avoiding layout thrashing.
- Use
will-changesparingly -- only on elements about to animate, remove after completion. - Respect
prefers-reduced-motion-- disable or simplify all animations for users who request it. - Profile with DevTools -- Performance tab > check for layout shifts during animations.
Core Capabilities
| Domain | Technologies | Key Patterns |
|---|---|---|
| Declarative React | Framer Motion 11+, motion components |
Layout animations, AnimatePresence, variants |
| Timeline Animation | GSAP 3.12+, ScrollTrigger | Scroll-driven sequences, pinning, scrubbing |
| Page Transitions | View Transitions API, Next.js useViewTransition |
Cross-page shared element transitions |
| Spring Physics | Framer Motion springs, react-spring |
Natural easing, interruptible animations |
| Micro-interactions | CSS transitions + Framer Motion | Button feedback, hover states, loading indicators |
| Scroll-driven | CSS animation-timeline: scroll(), GSAP ScrollTrigger |
Parallax, progress indicators, reveal on scroll |
Architecture Patterns
Pattern 1: Framer Motion Layout Animations
import { motion, AnimatePresence, LayoutGroup } from 'framer-motion';
function FilterableGrid({ items, filter }: Props) {
const filtered = items.filter(item => item.category === filter);
return (
<LayoutGroup>
<motion.div layout className="grid grid-cols-3 gap-4">
<AnimatePresence mode="popLayout">
{filtered.map(item => (
<motion.div
key={item.id}
layout // animate position changes
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
>
<Card item={item} />
</motion.div>
))}
</AnimatePresence>
</motion.div>
</LayoutGroup>
);
}
Pattern 2: View Transitions API for Page Navigation
// app/components/TransitionLink.tsx
'use client';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react';
export function TransitionLink({ href, children }: { href: string; children: React.ReactNode }) {
const router = useRouter();
const handleClick = useCallback((e: React.MouseEvent) => {
e.preventDefault();
if (!document.startViewTransition) {
router.push(href);
return;
}
document.startViewTransition(() => {
router.push(href);
});
}, [href, router]);
return <a href={href}
}
/* Shared element transitions via view-transition-name */
.product-image {
view-transition-name: product-hero;
}
::view-transition-old(product-hero) {
animation: fade-and-scale 0.3s ease-out;
}
::view-transition-new(product-hero) {
animation: fade-and-scale 0.3s ease-in reverse;
}
Pattern 3: Reduced Motion Respect
import { useReducedMotion } from 'framer-motion';
function AnimatedComponent() {
const shouldReduce = useReducedMotion();
return (
<motion.div
initial={{ opacity: 0, y: shouldReduce ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
transition={shouldReduce
? { duration: 0 }
: { type: 'spring', stiffness: 260, damping: 20 }
}
>
Content
</motion.div>
);
}
┌─ Animation Decision Tree ───────────────────────┐
│ │
│ prefers-reduced-motion? │
│ ├── YES → instant opacity fade only (no motion) │
│ └── NO → full spring/transform animation │
│ │
│ What's animating? │
│ ├── transform/opacity → GPU compositor (good) │
│ ├── width/height → layout thrash (avoid) │
│ └── box-shadow → paint-only (acceptable) │
└───────────────────────────────────────────────────┘
Anti-Patterns
- Animating
width,height,top,left-- triggers layout recalculation every frame. Usetransform: translate/scaleinstead for 60fps. will-changeon every element -- promotes too many layers to GPU, increasing memory usage. Apply only to elements about to animate.- Ignoring
prefers-reduced-motion-- motion-sensitive users experience nausea or seizures. Always check and respect this media query. - Animation duration > 500ms for micro-interactions -- makes the UI feel sluggish. Keep button/hover feedback under 200ms, page transitions under 400ms.
- Animating during scroll without
requestAnimationFrameor ScrollTrigger -- causes jank. Use GSAP ScrollTrigger or CSSanimation-timeline: scroll()for scroll-driven animations.
Quality Checklist
- All animations use
transformandopacity(compositor-friendly properties) -
prefers-reduced-motionrespected withuseReducedMotionor CSS media query - Micro-interactions under 200ms, page transitions under 400ms
-
AnimatePresenceused for mount/unmount animations - Layout animations use Framer Motion
layoutprop (not manual position calc) -
will-changeapplied only during animation, removed after - No jank in Chrome DevTools Performance panel (no long frames > 16ms)
- Animations are interruptible (spring physics, not fixed keyframes with
ease) - View Transitions API has fallback for unsupported browsers
- Scroll-driven animations use
animation-timelineor ScrollTrigger (not scroll event listeners)