Motion Design Principles
Easing curves, duration guidelines, choreography, spring physics, 12 principles.
animationdesignuxmotion
# Motion Design Principles
## Disney's 12 principles (UI application)
- **Squash & Stretch**: scale buttons on press `scale(0.95)`, bounce on release.
- **Anticipation**: slight wind-up before action (button dip before launch).
- **Staging**: direct attention -- animate the important thing, keep rest still.
- **Follow-through**: elements overshoot then settle (spring easing).
- **Ease in / Ease out**: never use `linear` for physical objects.
- **Arc**: curved paths feel natural. Use `motionPath` for element travel.
- **Secondary action**: supporting animations reinforce the main one.
- **Timing**: short = energetic, long = heavy. Match object weight.
- **Exaggeration**: slightly over-animate for emphasis, but stay tasteful in UI.
## Easing reference
```ts
// Standard eases (Material Design / Apple HIG)
const EASES = {
standard: 'cubic-bezier(0.2, 0, 0, 1)', // most UI transitions
decelerate: 'cubic-bezier(0, 0, 0, 1)', // enter screen
accelerate: 'cubic-bezier(0.3, 0, 1, 1)', // exit screen
emphasized: 'cubic-bezier(0.2, 0, 0, 1)', // hero transitions
spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)', // overshoot
bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
}
// GSAP equivalents
// power2.out = decelerate, power2.in = accelerate, elastic.out = spring
```
## Duration guidelines
| Element size | Duration |
|---|---|
| Micro (icon, badge) | 100-150ms |
| Small (tooltip, chip) | 150-200ms |
| Medium (card, modal) | 250-350ms |
| Large (drawer, page) | 350-500ms |
| Complex choreography | 500-700ms |
- Fast in, slow out: elements entering take slightly longer than leaving.
- Mobile: 20-30% shorter than desktop.
## Spring physics (Framer Motion)
```tsx
// Spring feels physical, responds to interruption
<motion.div
animate={{ x: 100 }}
transition={{ type: 'spring', stiffness: 300, damping: 30, mass: 1 }}
/>
// Stiffness: higher = faster/snappier
// Damping: lower = more bouncy
// Mass: higher = heavier, slower
// Common presets
// Snappy: stiffness:400, damping:40
// Gentle: stiffness:150, damping:20
// Bouncy: stiffness:300, damping:10
```
## Choreography (stagger)
```ts
// Items should enter like a wave, not all at once
// Stagger = 20-60ms between each item
const variants = {
hidden: {},
show: { transition: { staggerChildren: 0.05, delayChildren: 0.1 } },
}
// Rule: stagger * num_items < 300ms total
// For 10 items: 30ms stagger = 300ms total ✓
// For 10 items: 100ms stagger = 1s total ✗ (too slow)
```
## Reduced motion
```ts
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
// Framer Motion
const transition = prefersReduced
? { duration: 0.01 }
: { type: 'spring', stiffness: 300 }
// GSAP
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
gsap.globalTimeline.timeScale(100) // instant
}
```
## Page transitions (Next.js App Router)
```tsx
// layout.tsx -- wrap page content
<AnimatePresence mode="wait">
<motion.div
key={pathname}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.25, ease: [0.2, 0, 0, 1] }}
>
{children}
</motion.div>
</AnimatePresence>
```
## Don'ts
- Never animate `width`/`height` (use `transform: scaleX/scaleY` instead)
- Don't animate more than 3 properties simultaneously
- Avoid `all` in CSS transitions (animates hidden properties too)
- Never block user input during animation
- Don't use the same duration for all elementsAPI: /api/skills/motion-design-principles