CSS animations bring interfaces to life. A button that subtly responds to a hover. A loading indicator that communicates progress. A page transition that guides attention. When done well, animations make software feel polished, intentional, and responsive. When done poorly, they distract, confuse, and slow things down.

This guide covers CSS animations from first principles to advanced techniques. You will learn how transitions and keyframe animations work under the hood, how to choose the right timing function for every situation, how to write animations that perform at 60 frames per second, and how to build real-world patterns that you can use in production today. Every concept includes working code that you can copy, modify, and deploy.

If you prefer a visual approach, NexTool's CSS Animation Generator lets you design animations interactively and export production-ready code. But understanding the fundamentals makes you a better animator regardless of what tools you use.

CSS Transitions vs. Keyframe Animations

CSS provides two distinct mechanisms for creating animations. Understanding when to use each one is the first step to writing effective motion.

Transitions

A CSS transition animates the change of a property from one value to another. Transitions are triggered by state changes: hover, focus, active, class additions, or media query matches. They are the simplest form of CSS animation and the right choice for the majority of UI interactions.

CSS .button { background: #6366f1; transform: scale(1); transition: background 0.2s ease, transform 0.2s ease; } .button:hover { background: #a855f7; transform: scale(1.05); }

The transition property takes four values: the property to animate (or all), the duration, the timing function, and an optional delay. When the button is hovered, the background color and scale change smoothly over 200 milliseconds instead of snapping instantly. When the hover ends, the transition plays in reverse.

Use transitions when: you need to animate between two states (hover/focus/active), the animation is simple (one or two property changes), and the trigger is a user interaction or state change.

Keyframe Animations

Keyframe animations are more powerful and more complex. They define multiple steps in an animation sequence using the @keyframes rule, and they can run independently of user interaction. Keyframes support looping, alternating direction, pausing, and precise control over intermediate states.

CSS @keyframes fadeSlideIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .card { animation: fadeSlideIn 0.6s ease-out forwards; }

Use keyframe animations when: you need more than two states, the animation should loop or repeat, you want it to play automatically on load, or you need precise control over intermediate steps.

Live Demo: Bounce

The Animation Properties: A Complete Reference

The animation shorthand property is actually eight individual properties combined. Understanding each one gives you precise control over every aspect of your animations.

Property Values Description
animation-name Name of @keyframes rule References the keyframes block that defines the animation steps
animation-duration Time (e.g., 0.3s, 500ms) How long one cycle of the animation takes to complete
animation-timing-function ease, linear, ease-in, ease-out, ease-in-out, cubic-bezier() The acceleration curve that controls the animation's speed over time
animation-delay Time (e.g., 0.2s, 100ms) Time to wait before the animation starts. Negative values start mid-animation
animation-iteration-count Number or infinite How many times the animation plays. Use infinite for continuous animations
animation-direction normal, reverse, alternate, alternate-reverse Whether the animation plays forward, backward, or alternates between both
animation-fill-mode none, forwards, backwards, both What styles apply before/after the animation. forwards keeps the end state
animation-play-state running, paused Pause and resume animations, useful for hover-to-pause patterns

The shorthand syntax combines these properties in order:

CSS /* Shorthand */ animation: name duration timing-function delay iteration-count direction fill-mode play-state; /* Example */ animation: fadeIn 0.6s ease-out 0.2s 1 normal forwards running; /* Minimal (name and duration are required) */ animation: fadeIn 0.6s;

Timing Functions: The Secret to Natural Motion

Timing functions are arguably the most important aspect of animation quality. They determine how an animation accelerates and decelerates over time. The difference between an animation that feels mechanical and one that feels natural almost always comes down to the timing function.

Built-in Timing Functions

CSS provides five built-in timing functions. Each produces a different feel.

Custom Cubic Bezier Curves

For precise control, use cubic-bezier(x1, y1, x2, y2) to define a custom acceleration curve. The four values represent the two control points of a bezier curve. This is where animations go from good to great.

CSS /* Snappy interaction (quick start, soft landing) */ transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); /* Elastic overshoot */ transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55); /* Material Design standard curve */ transition-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1); /* Apple iOS spring approximation */ transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1.0);
Pro Tip: Use NexTool's Animation Generator

Designing cubic-bezier curves by hand is tedious. NexTool's CSS Animation Generator provides a visual curve editor where you drag control points and see the animation update in real time. Export the exact cubic-bezier values when you are satisfied with the feel.

Real-World Animation Patterns

Theory is useful, but practical patterns are what you actually ship. Here are the most common animation patterns in modern web interfaces, each with production-ready code.

1. Fade and Slide Entrance

The most common entrance animation. Elements fade in while sliding up from a slight offset. This pattern works for cards, list items, sections, and any content that appears on scroll or load.

CSS @keyframes fadeSlideUp { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } } .animate-in { animation: fadeSlideUp 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; } /* Stagger children */ .animate-in:nth-child(1) { animation-delay: 0.0s; } .animate-in:nth-child(2) { animation-delay: 0.1s; } .animate-in:nth-child(3) { animation-delay: 0.2s; } .animate-in:nth-child(4) { animation-delay: 0.3s; }
Live Demo: Stagger Bars

2. Loading Spinner

Every application needs a loading indicator. This spinner uses a single element with a partially colored border and infinite rotation. It is lightweight, accessible, and works at any size.

CSS .spinner { width: 40px; height: 40px; border: 4px solid rgba(99, 102, 241, 0.15); border-top-color: #6366f1; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }
Live Demo: Spinner

3. Pulse / Attention Indicator

A pulsing animation draws attention to important elements like notification badges, live indicators, or call-to-action buttons. The scale and opacity changes create a breathing effect that is noticeable but not distracting.

CSS .pulse { animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.8; } } /* Notification dot */ .notification-dot { width: 12px; height: 12px; background: #ec4899; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }
Live Demo: Pulse

4. Smooth Slide Transition

Sliding elements across the screen is fundamental for carousels, tab panels, and drawer menus. Using transform: translateX() instead of animating left or margin ensures GPU acceleration and smooth 60fps motion.

CSS .drawer { position: fixed; top: 0; right: 0; width: 320px; height: 100vh; background: #111118; transform: translateX(100%); transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1); } .drawer.open { transform: translateX(0); }
Live Demo: Slide

5. Skeleton Loading Screen

Skeleton screens are a better loading pattern than spinners for content-heavy pages. They show a placeholder layout that mimics the shape of the actual content, creating the perception of faster loading. The shimmer animation gives a sense of activity.

CSS .skeleton { background: linear-gradient( 90deg, #1a1a24 25%, #252530 50%, #1a1a24 75% ); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; border-radius: 8px; } .skeleton-text { height: 16px; margin-bottom: 12px; } .skeleton-text:last-child { width: 60%; } .skeleton-avatar { width: 48px; height: 48px; border-radius: 50%; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

Performance: Writing Animations That Don't Jank

The number one rule of animation performance: only animate transform and opacity. These two properties are composited by the GPU, which means the browser does not need to recalculate layout or repaint pixels for every frame. Animating any other property (width, height, margin, padding, top, left, background-color, border) triggers layout recalculation or repainting on every frame, which causes jank on lower-end devices.

Performance Pitfall

Animating width, height, top, left, margin, or padding triggers a layout recalculation on every animation frame. On a 60fps animation, that means 60 layout recalculations per second. Use transform: scale() instead of width/height, and transform: translate() instead of top/left positioning.

CSS /* BAD: Animating width triggers layout */ .bad-example { width: 100px; transition: width 0.3s ease; } .bad-example:hover { width: 200px; /* Triggers layout on every frame */ } /* GOOD: Animating transform is GPU-composited */ .good-example { transform: scaleX(1); transition: transform 0.3s ease; transform-origin: left center; } .good-example:hover { transform: scaleX(2); /* GPU-composited, smooth */ }

The will-change Property

For animations that are known in advance (not triggered by unpredictable interactions), the will-change property hints to the browser that an element will be animated, allowing it to optimize ahead of time. Use it sparingly, as overuse actually degrades performance by consuming additional GPU memory.

CSS /* Hint that these properties will animate */ .will-animate { will-change: transform, opacity; } /* Remove the hint when animation is done (via JS) */ .animation-complete { will-change: auto; }

Accessibility: Respecting User Preferences

Some users experience motion sickness, seizures, or discomfort from animations. The prefers-reduced-motion media query lets you detect whether the user has requested reduced motion in their operating system settings. Always respect this preference.

CSS /* Default: full animations */ .card { animation: fadeSlideUp 0.6s ease-out forwards; transition: transform 0.3s ease, box-shadow 0.3s ease; } /* Reduced motion: instant transitions only */ @media (prefers-reduced-motion: reduce) { .card { animation: none; transition: none; } /* Or keep very subtle transitions */ .button { transition-duration: 0.01ms; } }

The pattern above removes animations entirely for users who prefer reduced motion, while preserving the end state of the UI. A less aggressive approach keeps transitions but reduces their duration to near-instant, maintaining the visual consistency of state changes without the motion.

"An animation that makes one user happy and another user nauseous is not a good animation. Always provide a graceful fallback for users who prefer reduced motion." -- Web Content Accessibility Guidelines (WCAG) 2.2

Advanced Techniques

Multi-Step Keyframes

Keyframes are not limited to from and to. You can define as many intermediate states as you need using percentage values. This enables complex sequences like typing animations, multi-stage reveals, and choreographed transitions.

CSS @keyframes complexEntrance { 0% { opacity: 0; transform: translateY(40px) scale(0.95); } 40% { opacity: 1; transform: translateY(-8px) scale(1.02); } 70% { transform: translateY(4px) scale(0.99); } 100% { transform: translateY(0) scale(1); } }

Animating with CSS Custom Properties

In 2026, CSS custom properties (variables) can be animated using @property registration. This unlocks animations that were previously impossible in pure CSS, like gradient transitions and color interpolation along custom paths.

CSS @property --gradient-angle { syntax: "<angle>"; initial-value: 0deg; inherits: false; } .gradient-border { --gradient-angle: 0deg; background: conic-gradient( from var(--gradient-angle), #6366f1, #a855f7, #ec4899, #6366f1 ); animation: rotateGradient 3s linear infinite; } @keyframes rotateGradient { to { --gradient-angle: 360deg; } }

Scroll-Driven Animations

CSS Scroll-Driven Animations (supported in Chrome, Edge, and Firefox in 2026) let you tie animation progress to scroll position without JavaScript. This enables parallax effects, progress indicators, and scroll-linked reveals with zero JS overhead.

CSS /* Progress bar tied to page scroll */ .progress-bar { position: fixed; top: 0; left: 0; height: 3px; background: linear-gradient(90deg, #6366f1, #a855f7, #ec4899); transform-origin: left; animation: growWidth linear; animation-timeline: scroll(); } @keyframes growWidth { from { transform: scaleX(0); } to { transform: scaleX(1); } } /* Elements animate as they enter the viewport */ .reveal-on-scroll { animation: fadeSlideUp linear both; animation-timeline: view(); animation-range: entry 0% entry 100%; }
Generate Complex Animations Visually

Designing multi-step keyframe animations, custom timing curves, and scroll-driven effects by writing raw CSS is slow and error-prone. NexTool's CSS Animation Generator provides a visual timeline editor where you define keyframes by dragging handles and see the result instantly. Export production CSS with one click. Also check out the CSS Grid Generator for layout animations.

Animation Best Practices Checklist

Before shipping any animation, run through this checklist to ensure it is performant, accessible, and purposeful.

  1. Purpose: Does this animation serve a function (feedback, orientation, focus direction), or is it purely decorative? Decorative animations should be subtle and optional.
  2. Performance: Are you only animating transform and opacity? If not, can you restructure to use these compositable properties?
  3. Duration: Is the animation between 150ms and 500ms for interactions? Anything shorter feels instant. Anything longer feels sluggish. Loading animations can be longer.
  4. Timing function: Does the easing match the intent? Use ease-out for entrances, ease-in for exits, and ease-in-out for position changes.
  5. Reduced motion: Does the animation respect prefers-reduced-motion? Test with this setting enabled in your OS.
  6. Mobile: Does the animation perform smoothly on lower-end devices? Test on a real phone, not just a desktop simulator.
  7. Repetition: If the animation loops infinitely, is there a mechanism to pause it? Continuous motion is distracting and drains battery on mobile devices.
  8. Stacking: If multiple elements animate simultaneously, are delays staggered to create a choreographed feel rather than a chaotic one?

Key Takeaways

  • Use transitions for simple state changes (hover, focus, active) and keyframe animations for complex multi-step sequences.
  • Timing functions make or break the feel. ease-out for entrances, ease-in for exits. Use cubic-bezier for precise control.
  • Only animate transform and opacity for 60fps performance. Everything else triggers expensive layout recalculations.
  • Always respect prefers-reduced-motion. Some users cannot tolerate animation. Provide a graceful fallback.
  • Use NexTool's CSS Animation Generator to design animations visually and export clean, production-ready CSS.
  • Stagger delays on groups of elements to create choreographed sequences that feel intentional and polished.
NT

NexTool Team

We build free developer tools for web professionals. Our CSS Animation Generator, CSS Grid Generator, and Color Palette Generator help developers build beautiful, performant interfaces faster. All tools are free and require no signup.

Build Animations Visually

Design CSS animations with a drag-and-drop timeline editor. Customize timing, easing, and keyframes visually. Export clean CSS code instantly.

Open Animation Generator