← Back to Blog

Web Animation Best Practices: Smooth, Performant Motion

Create delightful animations that enhance user experience without compromising performance or accessibility

Well-crafted animations make interfaces feel responsive, polished, and alive. They guide users, provide feedback, and create memorable experiences. But poorly implemented animations do the opposite—they cause janky scrolling, slow interactions, and frustrate users. This guide covers the principles and techniques for creating smooth, performant animations that enhance rather than hinder user experience.

The Golden Rule: Animate Cheaply

Not all CSS properties are equal when it comes to animation performance. Browsers can animate some properties very efficiently, while others force expensive recalculations on every frame:

For more insights on this topic, see our guide on JAMstack Architecture Guide: Modern Web Development.

Cheap properties (composite layer): Transform and opacity are the only properties that can be animated on the compositor thread, completely bypassing the main thread. This means they remain smooth even when JavaScript is busy. Use transform: translate() instead of animating left/top. Use transform: scale() instead of width/height. Use opacity for fade effects.

Expensive properties (layout/paint): Properties like width, height, margin, padding, left, top trigger layout recalculation. The browser must recalculate the position of every affected element. Properties like background-color, box-shadow, border trigger paint operations. These are less expensive than layout but still force the main thread to do work.

The difference is dramatic. Animating opacity and transform can maintain 60fps on low-end devices. Animating width or margin often drops to 30fps or worse, creating visible jank.

CSS Animations vs JavaScript

Choose the right tool for the job. CSS animations and JavaScript animations each have appropriate use cases:

Use CSS when: The animation is decorative or state-based. Examples include hover effects, loading spinners, fade-ins on page load. CSS animations are declarative, easier to maintain, and the browser can optimize them automatically. They continue running even if JavaScript is blocked or busy.

Use JavaScript when: You need dynamic values, complex sequencing, or precise control. JavaScript libraries like GSAP or Framer Motion provide features CSS can't match—timeline sequencing, spring physics, gesture integration. They're essential for interactive animations that respond to user input.

For many projects, a combination works best. Use CSS for simple transitions and states, JavaScript for complex interactive sequences.

Easing and Timing

How an animation moves through time is as important as what it animates. Easing functions create natural, believable motion:

Linear easing: Constant speed throughout. Feels mechanical and unnatural. Only use for continuous looping animations like loading spinners where consistency is the goal.

Ease-out: Starts fast, slows at the end. Perfect for elements entering the screen—they snap into attention then settle gently. This is the most common easing for UI animations.

Ease-in: Starts slow, accelerates. Use for elements leaving the screen—they hesitate then whoosh away. Less common than ease-out but useful for exits.

Ease-in-out: Slow start and end, fast middle. Good for elements moving across the screen or changing states. Feels balanced and smooth.

Custom cubic-bezier: For maximum control, define custom curves. Tools like cubic-bezier.com let you visualize and tweak. High-end animations often use custom curves tuned precisely for the specific motion.

Duration Guidelines

Animation duration dramatically affects perceived responsiveness. Too fast feels jarring, too slow feels sluggish:

Micro-interactions (100-200ms): Button presses, checkbox toggles, small state changes. These should be nearly instant—just enough motion to provide feedback without feeling delayed.

UI transitions (200-400ms): Panel slides, dropdown menus, modal dialogs. Noticeable motion that feels deliberate but doesn't make users wait.

Large movements (400-600ms): Page transitions, full-screen takeovers, major layout shifts. These can be slightly longer because they involve more change, but stay under 600ms to avoid feeling slow.

Decorative animations: Can be longer if they're optional and don't block interaction. But make them skippable—never force users to wait for animation to complete.

Accessibility Considerations

Animations that delight some users can make sites unusable for others. Build inclusively from the start:

Respect prefers-reduced-motion: This CSS media query detects users who've indicated they want minimal motion. Respect it by disabling or significantly reducing animations. This isn't optional—it's an accessibility requirement. Users with vestibular disorders can become physically ill from parallax scrolling or large motion effects.

Never convey information only through animation: If an animation indicates status or provides feedback, provide a text alternative. Color changes or motion alone aren't accessible to all users.

Don't animate focus indicators: Let focus rings appear instantly. Users relying on keyboard navigation need immediate visual feedback about where they are.

Provide pause/play controls: For auto-playing animations that last longer than 5 seconds, WCAG requires user controls. This includes carousels, video backgrounds, and looping animations.

Performance Optimization

Smooth animations require hitting 60fps consistently. Here's how to achieve that:

Use will-change sparingly: The will-change property hints to the browser that an element will animate, allowing it to optimize. But overuse creates memory issues. Apply it just before animating, remove it when done. Never apply it to dozens of elements at once.

Avoid animating many elements simultaneously: Animating 100 list items creates 100 calculations per frame. Consider staggering animations so only a few elements move at once, or animate a container instead of children.

Use transform instead of position: Cannot stress this enough. Transform doesn't trigger layout, position properties do. This single change often fixes janky animations.

Batch DOM reads and writes: When using JavaScript, read all measurements first, then apply all changes. Interleaving reads and writes forces synchronous layout calculations (forced reflow), killing performance.

Loading and Skeleton Screens

Loading states are where animation often shines, providing feedback while content loads:

Skeleton screens: Show layout structure with subtle shimmer animations while content loads. This feels faster than spinners because users see the interface taking shape. Use transform and opacity for the shimmer effect to keep it smooth.

Progress indicators: When loading time is known, show determinate progress bars. When unknown, use indeterminate spinners. Keep them simple—complex animations while JavaScript is busy loading can cause jank.

Optimistic UI: Show the result immediately, update if it fails. Feels instant even when network is slow. Animation can smooth the transition if optimistic UI needs to be rolled back.

Scroll-Based Animations

Animations triggered by scroll position can create engaging experiences, but they're easy to overdo:

Use Intersection Observer: This API efficiently detects when elements enter the viewport without scroll event listeners that tank performance. Trigger animations when elements become visible, creating progressive reveals as users scroll.

Parallax with caution: Parallax scrolling (background moves slower than foreground) can look impressive but causes motion sickness for some users. Always provide reduced-motion alternatives. Keep parallax subtle—dramatic speed differences are more likely to cause problems.

Avoid scroll hijacking: Let users scroll at their own pace. Don't override natural scroll behavior with JavaScript-controlled animations. Users hate losing control of scrolling.

Tools and Libraries

You don't always need to build from scratch. Excellent tools exist for different needs:

  • GSAP — Industry standard for complex JavaScript animations. Robust, performant, well-documented. The free version covers most needs.
  • Framer Motion — React-specific animation library with elegant API. Great for component-based animations and gestures.
  • Animate.css — Collection of ready-to-use CSS animations. Great for quick prototypes or simple effects.
  • Lottie — Renders After Effects animations in the browser. Perfect for complex illustrations and micro-interactions designed by animators.

Testing Animation Performance

Always test on real devices, especially low-end hardware. Chrome DevTools provides helpful tools:

Performance tab: Record animations and analyze frame rates. Look for frames that take longer than 16ms (60fps threshold). Identify which operations are expensive.

Rendering panel: Enable "Paint flashing" to see what repaints. Enable "FPS meter" for real-time performance monitoring. Enable "Scrolling performance issues" to identify problems.

CPU throttling: Simulate slower devices by throttling CPU. Your MacBook Pro might hit 60fps easily, but budget Android phones won't.

Getting Started

Start simple. Master transform and opacity animations before moving to complex libraries. Learn to see performance problems—once you can identify jank, you'll naturally avoid patterns that cause it.

Remember: animation should enhance, not distract. If users notice your animations, they're probably too much. The best animations are felt, not seen—they make the interface feel responsive and alive without drawing attention to themselves.

Related Reading

Create Delightful, Performant Interfaces

We specialize in crafting smooth, accessible animations that enhance user experience across all devices and abilities.

Discuss Your Project