CSS Keyframes: A Complete Guide to Creating Web Animations

Master the art of declarative animations without JavaScript. Learn @keyframes syntax, animation properties, and performance optimization techniques.

What Are CSS Keyframes?

CSS keyframe animations are one of the most powerful tools in a web developer's toolkit, enabling smooth, declarative animations without requiring JavaScript. Unlike simple transitions, keyframe animations allow you to define multiple points throughout an animation sequence, giving you precise control over every moment of movement, transformation, and visual change. Whether you're creating subtle hover effects, complex loading indicators, or immersive user interface experiences, understanding @keyframes is essential for modern web development.

The keyframe animation system consists of two main components that work together to bring elements to life. First, the @keyframes rule defines the animation's stages by specifying styles at multiple percentage points through the animation lifecycle. Second, animation properties applied to elements trigger those animations and control their timing, duration, and behavior. This separation of concerns allows you to create reusable animation definitions that can be applied to multiple elements throughout your website, maintaining consistency while reducing code duplication. The browser automatically handles all interpolation between keyframes, smoothly calculating intermediate values to create fluid motion without any JavaScript required.

Key Concepts

Understanding the fundamentals of CSS keyframe animations

@keyframes Rule

The at-rule that defines animation sequences by specifying styles at multiple percentage points through the animation lifecycle.

Animation Properties

Eight properties controlling timing, duration, direction, and behavior of animations on any element.

Hardware Acceleration

How transform and opacity properties enable GPU-accelerated animations for smooth 60fps performance.

Fill Modes

Control over element styling before animation starts and after it completes using forwards, backwards, and both.

The @keyframes Syntax

The @keyframes rule defines an animation by specifying styles at specific points during the animation sequence. You use the from keyword for the start (0%), to for the end (100%), or any percentage between for intermediate states.

The from selector represents the starting state of the animation where the element begins positioned off-screen to the left and completely transparent. The to selector defines the final state where the element reaches its natural position and full visibility. The browser automatically calculates all intermediate values, creating smooth motion between these two states without requiring any JavaScript or manual interpolation calculations. Each keyframe selector can contain multiple CSS properties that animate simultaneously, enabling complex multi-dimensional effects.

Basic @keyframes Example
1@keyframes slide-in {2 from {3 transform: translateX(-100%);4 opacity: 0;5 }6 to {7 transform: translateX(0);8 opacity: 1;9 }10}

Using Percentages for Complex Animations

For animations with more than two states, percentage-based keyframes provide precise control. Percentages refer to the animation's progress, with 0% being the start and 100% being the end. This allows you to define as many intermediate states as needed for complex motion sequences.

A bounce effect requires keyframes at multiple points to create the up-and-down motion. The 0% keyframe sets the element at its starting position, the 50% keyframe moves it upward to create the peak of the bounce, and the 100% keyframe returns it to the ground. The browser interpolates values between each keyframe, creating natural-looking motion that mimics real-world physics. You can add as many percentage keyframes as needed for more complex animation sequences.

Multi-step Animation with Percentages
1@keyframes bounce {2 0% {3 transform: translateY(0);4 }5 50% {6 transform: translateY(-30px);7 }8 100% {9 transform: translateY(0);10 }11}

Animation Properties

Once you've defined @keyframes, you apply them using animation properties. These properties control how, when, and for how long the animation plays. Understanding each of the eight animation properties is crucial for creating polished, professional animations that behave exactly as intended. The properties work together to define the complete animation behavior, from which keyframes to use to how many times the animation should repeat.

CSS Animation Properties
PropertyDescriptionExample Value
animation-nameReferences the @keyframes to use"slide-in"
animation-durationHow long one cycle takes1.5s or 1500ms
animation-timing-functionThe acceleration curveease-out, linear, cubic-bezier()
animation-delayWait before starting0.5s or -1s
animation-iteration-countHow many times to play3, infinite
animation-directionForward, backward, or alternatingnormal, reverse, alternate
animation-fill-modeStyles before/after animationnone, forwards, backwards, both
animation-play-statePause or resume controlrunning, paused

Timing Functions

Timing functions control the acceleration curve of the animation, determining how quickly or slowly the animation progresses at different points. The default ease provides a natural start and end with faster movement in the middle. Linear creates constant speed without acceleration, which works well for continuous rotations or progress indicators where uniform motion is preferred.

More advanced timing functions include ease-in which starts slow and accelerates toward the end, ease-out which starts fast and decelerates for smooth landings, and ease-in-out which combines slow starts and ends with faster movement in the middle. For maximum control over the motion curve, cubic-bezier() accepts four values defining a custom Bézier curve, enabling precise easing adjustments. The steps() function creates animation with distinct discrete steps rather than smooth interpolation, useful for frame-by-frame effects.

The Animation Shorthand

All animation properties can be combined into a single animation declaration for cleaner, more maintainable code. The shorthand follows this pattern: animation: name duration timing-function delay iteration-count direction fill-mode play-state;

The order matters for the timing values: the first time value is always duration, and the second time value is always delay. This means specifying two values like animation: slide 2 1s means a 2-second duration with 1-second delay, not the other way around. For optional properties you don't want to specify, you can rely on their default values, but be aware that the shorthand requires at minimum an animation name and duration.

Animation Shorthand Example
1/* Longhand */2.element {3 animation-name: fade-in;4 animation-duration: 1.5s;5 animation-timing-function: ease-out;6 animation-delay: 0.5s;7 animation-iteration-count: 1;8 animation-fill-mode: forwards;9}10 11/* Shorthand */12.element {13 animation: fade-in 1.5s ease-out 0.5s forwards;14}

Performance Optimization

Not all CSS animations perform equally. Some properties trigger expensive browser operations, while others benefit from hardware acceleration. Understanding the difference is crucial for maintaining smooth frame rates, especially on mobile devices or complex pages with multiple animated elements.

When building responsive web experiences, animating efficiently becomes even more important since mobile devices have limited processing power. The same principles that apply to desktop animations apply doubly so on smaller screens where users are more sensitive to jank and dropped frames.

GPU-Accelerated Properties

Browsers can offload certain animations to the GPU (graphics processing unit), resulting in dramatically better performance. The transform and opacity properties are the primary beneficiaries of hardware acceleration because they don't affect the layout of surrounding elements. When you animate these properties, the GPU handles the computation independently, keeping the animation smooth even on complex pages.

Properties like width, height, top, left, margin, and padding force the browser to recalculate layout--the positions and sizes of all elements on the page. This process, called reflow, is computationally expensive. Animating layout properties can cause dropped frames and visible jank, especially on mobile devices with limited processing power. For smooth 60fps animations, always prefer transform and opacity over layout-triggering properties.

The will-change Property

The will-change property hints to the browser that an element will be animated, allowing it to optimize rendering ahead of time by promoting the element to its own compositor layer. However, this optimization comes with memory costs, so the property should be used sparingly and only when you're certain an animation will occur.

When using will-change, specify only the exact properties you plan to animate, such as will-change: transform, opacity;. Avoid applying it broadly or to parent elements that don't need optimization. Once your animation completes, consider removing the property to free up memory. The most common mistake is overusing will-change on elements that would animate efficiently without it, which can actually hurt performance by consuming excessive GPU memory.

Using will-change Appropriately
1.optimized-element {2 will-change: transform, opacity;3}

Common Animation Patterns

Loading Spinner

Continuous rotation animations make excellent loading indicators. The key to smooth spinning is combining the linear timing function, which provides constant speed without acceleration or deceleration, with infinite repetition to keep the spinner moving perpetually. This combination creates the seamless, continuous motion that users expect from loading indicators.

The linear timing function is essential for rotation animations because it maintains constant angular velocity throughout the full 360-degree rotation. Any easing function would cause visible speed variations that make the spinner appear to stutter as it completes each cycle.

Continuous Rotation Animation
1@keyframes spin {2 from { transform: rotate(0deg); }3 to { transform: rotate(360deg); }4}5 6.loading-spinner {7 animation: spin 1s linear infinite;8}

Entrance Animations

Entrance animations guide user attention and create polished first impressions on websites and applications. A common and effective pattern combines opacity and transform for a subtle slide-in effect that draws the eye while maintaining professionalism.

The forwards fill mode is crucial for entrance animations because it ensures the element maintains its final animated state after the animation completes. Without this, the element would snap back to its original pre-animation state, creating an jarring effect. By combining forwards with an appropriate easing function like ease-out, you create smooth entrance animations that leave elements in their intended final positions.

Entrance Animation Example
1@keyframes fade-in-up {2 from {3 opacity: 0;4 transform: translateY(20px);5 }6 to {7 opacity: 1;8 transform: translateY(0);9 }10}11 12.entrance {13 animation: fade-in-up 0.6s ease-out forwards;14}

Advanced Techniques

CSS Variables in Keyframes

CSS custom properties (variables) can make keyframe animations flexible and reusable across different contexts. By referencing variables within animations using the var() function, you create animation templates that can be customized for different elements without writing separate keyframes.

The --bounce-height variable in this example allows the same animation to be reused with different bounce heights. Each element can define its own value for the variable, and the animation will adapt accordingly. The fallback value of -20px ensures the animation still works even if a variable isn't defined. This approach reduces code duplication and makes it easy to maintain consistent animation behavior across your design system.

Reusable Animation with CSS Variables
1@keyframes bounce {2 from { transform: translateY(0); }3 to { transform: translateY(var(--bounce-height, -20px)); }4}5 6.small-bounce { --bounce-height: -10px; animation: bounce 0.5s; }7.large-bounce { --bounce-height: -40px; animation: bounce 0.5s; }

Staggered Animations

Staggered animations create elegant cascading effects that guide user attention sequentially through a group of elements. This pattern works particularly well for lists, card grids, and any collection of related items where you want to create visual interest without overwhelming the user.

The :nth-child() pseudo-class selector enables staggered delays by targeting specific children in a sequence. By incrementing the animation-delay for each subsequent child--for example, 0.1s, 0.2s, 0.3s--you create a wave-like effect where each element begins animating just as the previous one completes. This creates a natural, flowing motion that feels intentional and polished.

Staggered List Animation
1@keyframes fade-in {2 from { opacity: 0; transform: translateY(10px); }3 to { opacity: 1; transform: translateY(0); }4}5 6.stagger-item { animation: fade-in 0.5s ease-out forwards; }7.stagger-item:nth-child(1) { animation-delay: 0.1s; }8.stagger-item:nth-child(2) { animation-delay: 0.2s; }9.stagger-item:nth-child(3) { animation-delay: 0.3s; }10.stagger-item:nth-child(4) { animation-delay: 0.4s; }

Accessibility Considerations

Motion-sensitive users may experience discomfort, dizziness, or even physical reactions from animations. The prefers-reduced-motion media query allows you to detect when users have indicated they want less motion in their operating system settings, enabling you to provide alternative experiences.

When respecting reduced motion preferences, consider providing static alternatives or significantly simplified animations. For a spinning loader, you might show a static progress bar or a simple pulsing indicator instead. The key is ensuring all users can access your content and functionality without experiencing discomfort. Implementing reduced motion support is both an accessibility best practice and increasingly expected by users who are mindful of their visual comfort.

Respecting Reduced Motion Preference
1@keyframes spin {2 from { transform: rotate(0deg); }3 to { transform: rotate(360deg); }4}5 6.spinner { animation: spin 1s linear infinite; }7 8@media (prefers-reduced-motion: reduce) {9 .spinner { animation: none; opacity: 0.5; }10}

Frequently Asked Questions

Conclusion

CSS keyframe animations unlock a world of creative possibilities for web interfaces. By understanding the @keyframes syntax, animation properties, and performance considerations, you can create smooth, engaging animations that enhance user experience without sacrificing performance.

Start with simple animations, experiment with timing functions, and gradually build toward complex multi-step sequences. The key to mastery is practice and attention to the subtle details that distinguish adequate animations from exceptional ones.

For further learning, explore our guides on CSS transitions for simple state-based animations and hover effects for interactive user experiences.


Sources

  1. MDN Web Docs - Using CSS Animations
  2. web.dev - High-Performance CSS Animations
  3. Josh W. Comeau - Interactive Guide to Keyframe Animations

Ready to Create Stunning Web Animations?

Our team specializes in building performant, accessible animations that enhance user experience.