Emil Kowalski Animation Best Practices
Comprehensive animation guide for web interfaces based on Emil Kowalski's teachings, open-source libraries (Sonner, Vaul), and his animations.dev course. Contains 58 rules across 8 categories, prioritized by impact.
When to Apply
Reference these guidelines when:
- Adding animations to React components
- Choosing easing curves or timing values
- Implementing gesture-based interactions (swipe, drag)
- Building toast notifications or drawer components
- Optimizing animation performance
- Ensuring animation accessibility
- Expressing any of the above with Tailwind CSS v4 utilities (see the
tw-category)
Rule Categories by Priority
Quick Reference
1. Easing Selection (CRITICAL)
ease-out-default[blocked] - Use ease-out as your default easingease-custom-curves[blocked] - Use custom cubic-bezier over built-in CSSease-in-out-onscreen[blocked] - Use ease-in-out for on-screen movementease-spring-natural[blocked] - Use spring animations for natural motionease-spring-config[blocked] - Configure springs with duration and bounceease-ios-drawer[blocked] - Use iOS-style easing for drawer componentsease-context-matters[blocked] - Match easing to animation context
2. Timing & Duration (CRITICAL)
timing-300ms-max[blocked] - Keep UI animations under 300mstiming-faster-better[blocked] - Faster animations improve perceived performancetiming-asymmetric[blocked] - Use asymmetric timing for press and releasetiming-tooltip-delay[blocked] - Delay initial tooltips, instant subsequent onestiming-drawer-500ms[blocked] - Use 500ms duration for drawer animations
3. Property Selection (HIGH)
props-transform-opacity[blocked] - Animate only transform and opacityprops-hardware-accelerated[blocked] - Use hardware-accelerated animations when main thread is busyprops-will-change[blocked] - Use will-change to prevent 1px shiftprops-avoid-css-variables[blocked] - Avoid CSS variables for drag animationsprops-clip-path-performant[blocked] - Use clip-path for layout-free reveals
4. Transform Techniques (HIGH)
transform-scale-097[blocked] - Scale buttons to 0.97 on presstransform-never-scale-zero[blocked] - Never animate from scale(0)transform-percentage-translate[blocked] - Use percentage values for translateYtransform-origin-aware[blocked] - Make animations origin-awaretransform-scale-children[blocked] - Scale transforms affect childrentransform-3d-preserve[blocked] - Use preserve-3d for 3D transform effectstransform-starting-style[blocked] - Animate enter states with @starting-style
5. Interaction Patterns (MEDIUM-HIGH)
interact-interruptible[blocked] - Make animations interruptibleinteract-momentum-dismiss[blocked] - Use momentum-based dismissalinteract-damping[blocked] - Damp drag at boundariesinteract-scroll-drag-conflict[blocked] - Resolve scroll and drag conflictsinteract-snap-points[blocked] - Implement velocity-aware snap pointsinteract-friction-upward[blocked] - Allow upward drag with frictioninteract-pointer-capture[blocked] - Use pointer capture for drag operationsinteract-multitouch[blocked] - Ignore extra touch points during draginteract-touch-hover[blocked] - Gate hover animations behind a pointer media query
6. Strategic Animation (MEDIUM)
strategy-keyboard-no-animate[blocked] - Never animate keyboard-initiated actionsstrategy-frequency-matters[blocked] - Consider interaction frequency before animatingstrategy-purpose-required[blocked] - Every animation must have a purposestrategy-feedback-immediate[blocked] - Provide immediate feedback on all actionsstrategy-marketing-exception[blocked] - Marketing sites are the exceptionstrategy-cohesion[blocked] - Match motion to the component's personalitystrategy-review-fresh-eyes[blocked] - Review animations in slow motion and the next day
7. Accessibility & Polish (MEDIUM)
polish-reduced-motion[blocked] - Respect prefers-reduced-motionpolish-opacity-fallback[blocked] - Use opacity as reduced motion fallbackpolish-framer-hook[blocked] - Use useReducedMotion hook in Framer Motionpolish-dont-remove-all[blocked] - Keep gentle animation for reduced motionpolish-blur-bridge[blocked] - Use blur to bridge animation statespolish-clip-path-tabs[blocked] - Use clip-path for tab transitionspolish-toast-stacking[blocked] - Implement toast stacking with scale and offsetpolish-scroll-reveal[blocked] - Trigger scroll animations at appropriate thresholdpolish-hover-gap-fill[blocked] - Fill gaps between hoverable elementspolish-stagger-children[blocked] - Stagger child animations for orchestration
8. Tailwind v4 Utilities (MEDIUM)
Express the principles above with proper Tailwind CSS v4 utilities. Applies only to Tailwind v4 projects; the raw-CSS and Framer Motion rules above remain the source of truth.
tw-ease-duration-defaults[blocked] - Set ease-out and a sub-300ms duration explicitlytw-custom-easing-theme[blocked] - Register custom easing curves as @theme tokenstw-press-scale[blocked] - Use active:scale-[0.97] for button press feedbacktw-asymmetric-timing[blocked] - Split press and release timing with active:durationtw-starting-enter[blocked] - Animate enter states with the starting: varianttw-reduced-motion[blocked] - Gate movement behind motion-safe / motion-reducetw-origin-aware[blocked] - Set transform-origin with origin-* utilitiestw-will-change[blocked] - Scope will-change-transform to the active gesture
Key Values Reference
Duration by Element
Pick duration by how often the element is seen and how much it moves. Keep UI animations under 300ms.

