web-animation-design
A comprehensive guide for creating web animations that feel natural and purposeful, based on Emil Kowalski's "Animations on the Web" course.
Overview
This skill provides comprehensive guidance for designing and implementing web animations that feel right. It covers easing functions, spring physics, timing, duration, and accessibility considerations for motion.
Based on Emil Kowalski's animations.dev course, this skill helps you make informed decisions about when and how to animate, which easing curves to use, and how to ensure your animations are performant and accessible.
Quick Start: Four Questions
Is this element entering or exiting?
→ Use ease-out
Is an on-screen element moving?
→ Use ease-in-out
Is this a hover/color transition?
→ Use ease
Will users see this 100+ times daily?
→ Don't animate it
The Easing Blueprint
ease-out (Most Common)
Use for user-initiated interactions: dropdowns, modals, tooltips, any element entering or exiting the screen.
Why it works: Acceleration at the start creates an instant, responsive feeling. The element "jumps" toward its destination then settles in.
/* Sorted weak to strong */ --ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94); --ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1); --ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1); --ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1); --ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
ease-in-out (For Movement)
Use when elements already on screen need to move or morph. Mimics natural motion like a car accelerating then braking.
/* Sorted weak to strong */ --ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955); --ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1); --ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1); --ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1); --ease-in-out-expo: cubic-bezier(1, 0, 0, 1); --ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
ease (For Hover Effects)
Use for hover states and color transitions. The asymmetrical curve (faster start, slower end) feels elegant for gentle animations.
transition: background-color 150ms ease;
Duration Guidelines
Rules:
- • UI animations should stay under 300ms
- • Larger elements animate slower than smaller ones
- • Exit animations can be ~20% faster than entrance
- • Match duration to distance - longer travel = longer duration
When to Animate
✓ Do Animate
- • Enter/exit transitions for spatial consistency
- • State changes benefiting from visual continuity
- • Responses to user actions (feedback)
- • Rarely-used interactions where delight adds value
✗ Don't Animate
- • Keyboard-initiated actions
- • Hover effects on frequently-used elements
- • Anything users interact with 100+ times daily
- • When speed matters more than smoothness
Spring Animations
Springs feel more natural because they don't have fixed durations—they simulate real physics.
When to Use Springs
- • Drag interactions with momentum
- • Elements that should feel "alive"
- • Gestures that can be interrupted mid-animation
- • Organic, playful interfaces
Bounce Guidelines
- • Avoid bounce in most UI contexts
- • Use bounce for drag-to-dismiss, playful interactions
- • Keep bounce subtle (0.1-0.3) when used
Performance: The Golden Rule
Only animate transform and opacity
These properties skip layout and paint stages, running entirely on the GPU.
Avoid animating:
- •
padding,margin,height,width(trigger layout) - •
blurfilters above 20px (expensive, especially Safari) - • CSS variables in deep component trees
Accessibility: prefers-reduced-motion
Animations can cause motion sickness or distraction for some users. Whenever you add an animation, also add a media query to disable it:
.modal {
animation: fadeIn 200ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
.modal {
animation: none;
}
}Guidelines:
- • Every animated element needs its own
prefers-reduced-motionmedia query - • Set
animation: noneortransition: none - • No exceptions for opacity or color - disable all animations
- • Show play buttons instead of autoplay videos
Practical Tips
| Scenario | Solution |
|---|---|
| Make buttons feel responsive | Add transform: scale(0.97) on :active |
| Element appears from nowhere | Start from scale(0.95), not scale(0) |
| Shaky/jittery animations | Add will-change: transform |
| Hover causes flicker | Animate child element, not parent |
| Popover scales from wrong point | Set transform-origin to trigger location |
| Sequential tooltips feel slow | Skip delay/animation after first tooltip |