CSS Keyframes & Animations: Multi-Step Motion and Performance

Master CSS Keyframes and animations. Learn how to write keyframe steps, configure duration, cycles, and optimize rendering performance.

Introduction

Transitions work well for animating changes between two states (like a hover effect), but what if you need to create a complex, multi-step animation? For example, a loading spinner that spins infinitely, a floating badge that moves up and down, or a multi-stage entrance animation when a page loads.

To build these multi-step animations, CSS provides Keyframes and Animations. By defining the visual stages of an animation using the @keyframes rule and linking it to an element using animation control properties, you can create complex, looping animations. Optimizing these animations to prevent browser lag is a key frontend development skill.


What You Will Learn

  • How to define multi-step timelines using @keyframes.
  • Binding animations to elements and setting durations.
  • Controlling repeat loops using animation-iteration-count.
  • Setting final layout states using animation-fill-mode.
  • The shorthand syntax for CSS animations.
  • Performance optimization tips for smooth, hardware-accelerated animations.

Prerequisites


Detailed Explanation: Keyframes and Rules

Creating a CSS animation is a two-step process:

  1. Define the animation timeline using the @keyframes rule.
  2. Apply the timeline to an element and configure its playback settings.

Step 1: Defining the @keyframes Timeline

The @keyframes rule defines the styles of the element at specific percentages of the animation's duration. You can use the keywords from (equivalent to 0%) and to (equivalent to 100%), or define intermediate percentages:

@keyframes bounce-scale {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1); /* Enlarge in the middle */
  }
  100% {
    transform: scale(1);
  }
}

Step 2: Applying Animation Properties

Once defined, you apply the animation to an element using several key properties:

  • animation-name: The name of the @keyframes timeline to use (e.g. bounce-scale).
  • animation-duration: How long the animation takes to complete one cycle (e.g., 2s, 800ms).
  • animation-iteration-count: How many times the animation should repeat (infinite or a specific integer).
  • animation-direction: Controls whether the animation plays forward, backward (reverse), or alternates directions (alternate).
  • animation-fill-mode: Defines which styles are applied to the element before the animation starts or after it finishes:
    • none: (Default) No styles are retained. The element resets to its base CSS rules.
    • forwards: The element retains the styles defined in the final keyframe (100%).
    • backwards: The element applies the styles defined in the first keyframe (0%) during the animation delay period.
    • both: Applies both forwards and backwards rules.

The Animation Shorthand

Combine all animation properties into a single declaration:

$$\text{animation: name | duration | timing-function | delay | iteration-count | direction | fill-mode};$$

.badge-alert {
  /* Shorthand declaration */
  animation: bounce-scale 1.5s ease-in-out infinite alternate;
}

Visual Learning Diagram (Mermaid)

graph LR
    Timeline[Animation Timeline] --> P0[0% / from <br> Start style]
    Timeline --> P50[50% <br> Intermediate style]
    Timeline --> P100[100% / to <br> Final style]

Code Examples

Example 1: Infinite Loading Spinner

Build a circular loading icon that spins continuously:

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(59, 130, 246, 0.1);
  border-top-color: #3b82f6; /* Colored indicator segment */
  border-radius: 50%;
  
  /* Apply spinner animation: 0.8s, constant speed, infinite loop */
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

Example 2: Slide-in Entrance Card (using forwards fill-mode)

Build an entrance animation for a dashboard card, ensuring it stays visible at its final state when the animation completes:

.entrance-card {
  opacity: 0;
  transform: translateY(30px);
  
  /* Play slide-in animation once, and retain final styles */
  animation: slide-in 0.5s cubic-bezier(0.25, 1, 0.5, 1) 0.2s 1 forwards;
}

@keyframes slide-in {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Performance Optimization & Hardware Acceleration

Animations that lag look unprofessional. To keep your animations running at a smooth 60fps, follow these performance guidelines:

  1. Only animate transform and opacity: These properties do not trigger page layout calculations (reflow) or repaints. The browser GPU handles them directly (compositing), keeping animations smooth.
  2. Avoid animating layout properties: Animating properties like width, height, margin, top, or box-shadow forces the browser to recalculate the page layout on every single frame, causing frame drops (lag).
  3. Use the will-change property: For complex animations, warn the browser ahead of time so it can optimize rendering resources:
    .complex-anim {
      will-change: transform, opacity;
    }
    

FAQs

Q: Why does my element snap back to its original style when the animation ends? A: By default, elements reset to their base CSS rules when an animation completes (animation-fill-mode: none). To keep the element at its final animation state, declare animation-fill-mode: forwards;.

Q: Can I pause a CSS animation? A: Yes! Use the animation-play-state property. You can toggle it between running and paused (for example, pausing a loader when a user hovers over it: .loader:hover { animation-play-state: paused; }).


Summary

CSS Keyframes and animations drive multi-stage movements. Defined using @keyframes percent intervals, playback is customized using duration, delay, direction, repetition counts, and fill-mode targets, consolidating rules via the animation shorthand.


Related Articles

Next Tutorial

How do we write clean layouts using modern CSS functions and nesting? Let's check: CSS Variables & Math Functions.