| name | animation-principles |
| description | Apply Disney's 12 animation principles to web and app interfaces — squash-and-stretch, anticipation, staging, follow-through, slow-in/slow-out, arcs, secondary action, timing, exaggeration, solid form, straight-ahead vs pose-to-pose, and appeal. Use when asked about "animation principles", "12 principles of animation", "Disney animation", "squash and stretch", "anticipation in UI", "follow through animation", "animation feels lifeless", "make animation feel natural", "physics-based animation", or "why does my animation feel robotic". Do NOT use for: animation performance fixes — see fixing-motion-performance. Do NOT use for: easing curves and duration — see motion-design.
|
| origin | adapted:MIT © raphaelsalaja |
| license | MIT © 2026 Vũ Văn Tâm |
| version | 1.0.0 |
| compatibility | CSS animations, Framer Motion v11, GSAP v3. Principles are tool-agnostic. |
When to Use
- Use when: an animation exists but feels mechanical or lifeless
- Use when: a UI element needs to communicate weight, speed, or emotion
- Use when: building onboarding flows, success states, or playful micro-interactions
- Do NOT use for: performance problems — see fixing-motion-performance
- Do NOT use for: easing function selection — see motion-design
The 12 Principles Applied to Web
1. Squash and Stretch — Conveys weight and elasticity
<motion.button
whileTap={{ scaleX: 1.15, scaleY: 0.85 }}
transition={{ type: 'spring', stiffness: 600, damping: 15 }}
>
Submit
</motion.button>
2. Anticipation — Prepares viewer for action
<motion.div
whileHover={{ y: -2 }}
whileTap={{ y: 2, scale: 0.98 }}
transition={{ duration: 0.1 }}
/>
3. Staging — Draw attention to what matters
.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 60ms; }
.list-item:nth-child(3) { animation-delay: 120ms; }
const container = { hidden: {}, show: { transition: { staggerChildren: 0.06 } } };
const item = { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0 } };
<motion.ul variants={container} initial="hidden" animate="show">
{items.map(i => <motion.li key={i.id} variants={item} />)}
</motion.ul>
4. Slow In / Slow Out (Ease In-Out) — Natural deceleration
transition: transform 300ms linear;
transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1);
5. Follow Through & Overlapping Action — Parts settle at different times
const modal = {
hidden: { opacity: 0, scale: 0.95, y: 8 },
show: {
opacity: 1, scale: 1, y: 0,
transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] }
}
};
const content = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { delay: 0.1, duration: 0.15 } }
};
6. Arcs — Movement along curved paths feels natural
<motion.div
animate={{ x: 100, y: -50 }}
transition={{
x: { duration: 0.4 },
y: { duration: 0.2, repeat: 1, repeatType: 'reverse' }
}}
/>
7. Secondary Action — Supporting motions add life
<motion.div animate={{ rotateY: isOpen ? 180 : 0 }}>
<motion.span
animate={{ rotate: isOpen ? 180 : 0 }} // icon follows, but independently
transition={{ delay: 0.05 }}
>
▾
</motion.span>
</motion.div>
8. Timing — Duration communicates weight
< 100ms : instant — system response, toggle switch
100–200ms : fast — micro-interactions, hover, button press
200–400ms : medium — modal open, page transition, drawer
400–700ms : slow — hero entrance, celebration, loading complete
> 700ms : deliberate — onboarding, empty state, first-load reveal
Heavier objects move slower. Lighter objects move faster.
9. Exaggeration — Amplify to clarify
const shake = {
x: [0, -8, 8, -8, 8, -4, 4, 0],
transition: { duration: 0.4, ease: 'easeInOut' }
};
<motion.form animate={hasError ? shake : {}} />
10. Appeal — Give elements personality
<motion.div
animate={{ rotate: 360 }}
transition={{
repeat: Infinity,
duration: 1,
ease: [0.4, 0, 0.6, 1]
}}
/>
11. Solid Drawing — Maintain spatial consistency
<motion.div
initial={{ rotateX: -10, opacity: 0 }}
animate={{ rotateX: 0, opacity: 1 }}
style={{ transformPerspective: 800 }}
/>
12. Straight Ahead vs Pose-to-Pose
Straight ahead = animate frame-by-frame (CSS keyframes, complex JS paths)
Pose-to-pose = define start + end state, let engine interpolate (Framer Motion, GSAP)
Web UI default: use pose-to-pose. Straight ahead only for complex paths (GSAP MotionPath).
Anti-Fake-Pass Rules
Before claiming animation feels natural, you MUST show:
Reference: gates/anti-fake-pass-gate.md