javascriptroom guide

Learn CSS Animation from Scratch: A Step-by-Step Tutorial

In the world of web design, static pages feel lifeless. Users crave interactivity and movement—and that’s where CSS animations shine. Whether it’s a subtle hover effect, a loading spinner, or a dynamic transition between page sections, CSS animations breathe life into your website without relying on heavy JavaScript libraries. The best part? You don’t need to be a coding expert to master CSS animations. This tutorial will take you from the basics of keyframes to advanced techniques, with clear examples and step-by-step explanations. By the end, you’ll be creating smooth, engaging animations that elevate your web projects.

Table of Contents

  1. Understanding Keyframes: The Building Blocks
  2. Essential CSS Animation Properties
  3. CSS Transitions vs. Animations: What’s the Difference?
  4. Practical Examples: Let’s Animate!
  5. Advanced CSS Animation Techniques
  6. Troubleshooting Common CSS Animation Issues
  7. Conclusion: Start Animating!
  8. References

1. Understanding Keyframes: The Building Blocks

At the core of every CSS animation lies keyframes. Think of keyframes as a “script” that defines how an element should change over time. They specify the styles an element should have at specific moments during the animation.

What Are Keyframes?

Keyframes let you define styles at specific stages (e.g., start, middle, end) of an animation. For example, you might want an element to start transparent, become opaque halfway, and then fade out again. Keyframes make this possible.

Syntax of Keyframes

Keyframes are defined using the @keyframes rule, followed by a name (e.g., fadeIn) and a set of stages (using percentages or from/to).

Basic Example:

/* Define keyframes for a fade animation */
@keyframes fadeInOut {
  0% { opacity: 0; } /* Start: fully transparent */
  50% { opacity: 1; } /* Middle: fully opaque */
  100% { opacity: 0; } /* End: fully transparent */
}

/* Apply the animation to an element */
.element {
  animation-name: fadeInOut; /* Link to keyframes */
  animation-duration: 3s; /* How long the animation lasts */
}
  • 0%: The start of the animation (equivalent to from).
  • 100%: The end of the animation (equivalent to to).
  • You can add as many intermediate stages as needed (e.g., 25%, 75%).

2. Essential CSS Animation Properties

Once you’ve defined keyframes, you need to “activate” the animation using CSS properties. These properties control how the animation runs, from duration to repetition.

animation-name

Purpose: Links an element to a set of keyframes.
Value: The name of your @keyframes rule (e.g., fadeInOut).

.element {
  animation-name: fadeInOut; /* Must match the @keyframes name */
}

animation-duration

Purpose: Defines how long the animation takes to complete one cycle.
Value: Time in seconds (s) or milliseconds (ms). Default: 0s (animation won’t run).

.element {
  animation-duration: 2s; /* Animation lasts 2 seconds */
}

animation-timing-function

Purpose: Controls the “speed curve” of the animation (e.g., slow start, fast end).
Common Values:

  • ease: Slow start → fast middle → slow end (default).
  • linear: Constant speed.
  • ease-in: Slow start → fast end.
  • ease-out: Fast start → slow end.
  • ease-in-out: Slow start and end → fast middle.
  • cubic-bezier(n,n,n,n): Custom speed curve (advanced).
.element {
  animation-timing-function: ease-in; /* Slow start, then speeds up */
}

animation-delay

Purpose: Delays the start of the animation.
Value: Time in s or ms. Default: 0s (starts immediately).

.element {
  animation-delay: 1s; /* Animation starts 1 second after the page loads */
}

animation-iteration-count

Purpose: Defines how many times the animation repeats.
Values:

  • Number (e.g., 3 → runs 3 times).
  • infinite → repeats forever.
.element {
  animation-iteration-count: infinite; /* Animation loops forever */
}

animation-direction

Purpose: Controls whether the animation plays forward, backward, or alternates.
Values:

  • normal: Forward (default).
  • reverse: Backward.
  • alternate: Forward → backward → forward… (repeats).
  • alternate-reverse: Backward → forward → backward… (repeats).
.element {
  animation-direction: alternate; /* Animation reverses on each repeat */
}

animation-fill-mode

Purpose: Defines styles applied to the element before the animation starts and after it ends.
Values:

  • none: No styles applied (default).
  • forwards: Element retains the styles of the last keyframe (100%).
  • backwards: Element uses the styles of the first keyframe (0%) during animation-delay.
  • both: Combines forwards and backwards.
.element {
  animation-fill-mode: forwards; /* Element stays opaque after animation ends */
}

animation-play-state

Purpose: Pauses or resumes the animation.
Values:

  • running: Animation is active (default).
  • paused: Animation stops mid-cycle.
.element:hover {
  animation-play-state: paused; /* Pauses animation on hover */
}

The Animation Shorthand Property

Writing all these properties separately is tedious. Use the animation shorthand to combine them in one line:

/* Shorthand: name | duration | timing-function | delay | iteration-count | direction | fill-mode | play-state */
.element {
  animation: fadeInOut 2s ease-in 1s infinite alternate forwards running;
}

Note: The order matters! The first time value is duration, the second is delay.

3. CSS Transitions vs. Animations: What’s the Difference?

Beginners often confuse CSS transitions and animations. Here’s the key distinction:

TransitionsAnimations
Triggered by a state change (e.g., :hover, :focus, class added via JS).Run automatically (or via animation-play-state).
Define start/end states (no intermediate steps).Define multiple stages (via keyframes).
Simplest syntax: transition: property duration timing-function delay;.Requires @keyframes and animation properties.

Use transitions for simple effects (e.g., a button changing color on hover). Use animations for complex sequences (e.g., a bouncing ball, loading spinner).

4. Practical Examples: Let’s Animate!

Theory is great, but practice makes perfect. Let’s build 4 common animations from scratch.

Example 1: Color Fade Animation

Goal: Fade an element’s background color from red to blue and back.

Step 1: Define Keyframes

@keyframes colorFade {
  0% { background: red; }
  50% { background: purple; } /* Intermediate stage */
  100% { background: blue; }
}

Step 2: Apply Animation to an Element

<div class="color-box"></div>
.color-box {
  width: 200px;
  height: 200px;
  animation: colorFade 3s ease-in-out infinite alternate;
}

Result: A 200px square that fades from red → purple → blue, then reverses.

Example 2: Bouncing Ball Animation

Goal: Animate a ball bouncing up and down.

Step 1: Define Keyframes (with transform)

Use transform: translateY() to move the ball vertically. Add a scaleY at the bottom to mimic squashing (realism!).

@keyframes bounce {
  0% {
    transform: translateY(0); /* Start at top */
  }
  50% {
    transform: translateY(200px) scaleY(0.8); /* Bottom: squish slightly */
  }
  100% {
    transform: translateY(0); /* Back to top */
  }
}

Step 2: Style the Ball and Apply Animation

<div class="ball"></div>
.ball {
  width: 50px;
  height: 50px;
  border-radius: 50%; /* Make it a circle */
  background: orange;
  animation: bounce 0.8s ease-in-out infinite alternate;
}

Result: A bouncing orange ball with a natural “squash” at the bottom.

Example 3: Rotating Loading Spinner

Goal: Create a simple loading spinner using a border and rotation.

Step 1: Define Keyframes (with transform: rotate)

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

Step 2: Style the Spinner and Animate

<div class="spinner"></div>
.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3; /* Light gray border */
  border-top: 4px solid #3498db; /* Blue top border */
  border-radius: 50%; /* Circle */
  animation: spin 1s linear infinite; /* Rotate forever */
}

Result: A spinning circle that looks like a loading indicator.

Example 4: Interactive Hover Animation (Button)

Goal: Animate a button on hover (scale up + color change).

Step 1: Define Keyframes for Scale and Color

@keyframes buttonHover {
  0% {
    transform: scale(1); /* Original size */
    background: #4CAF50; /* Green */
  }
  100% {
    transform: scale(1.1); /* 10% larger */
    background: #2E7D32; /* Darker green */
  }
}

Step 2: Style the Button and Trigger Animation on Hover

<button class="animated-btn">Click Me</button>
.animated-btn {
  padding: 12px 24px;  
  border: none;
  border-radius: px;
  color: white;
  font-size: 16px;
  cursor: pointer;
  transition: all 0.3s ease; /* Smooth transition for non-keyframe effects */
}

.animated-btn:hover {
  animation: buttonHover 0.3s forwards; /* Animate on hover */
}

Result: The button scales up and darkens when hovered.

5. Advanced CSS Animation Techniques

Ready to level up? Try these pro tips.

Multiple Animations on a Single Element

You can apply multiple animations to one element by separating keyframe names and properties with commas:

@keyframes moveRight {
  0% { transform: translateX(0); }
  100% { transform: translateX(200px); }
}

@keyframes changeColor {
  0% { background: red; }
  100% { background: blue; }
}

.element {
  animation: moveRight 2s linear infinite alternate, changeColor 3s ease-in-out infinite alternate;
}

Result: The element moves right/left while fading between red and blue.

Mastering cubic-bezier for Custom Timing

The cubic-bezier function lets you create custom speed curves for animation-timing-function. It uses 4 values: (x1, y1, x2, y2), where (x1,y1) and (x2,y2) are control points.

Example: Bounce-like timing (fast start, bounce at the end):

.element {
  animation-timing-function: cubic-bezier(0.17, 0.67, 0.83, 0.67);
}

Tool: Use cubic-bezier.com to visualize and generate curves.

Using CSS Variables in Animations

CSS variables (--variable-name) make animations easier to maintain. Define variables for duration, color, or delay, then reuse them:

:root {
  --animation-duration: 2s;
  --primary-color: #ff6b6b;
}

@keyframes pulse {
  0%, 100% { background: var(--primary-color); }
  50% { background: #ffe66d; }
}

.element {
  animation: pulse var(--animation-duration) infinite;
}

Now you can update --animation-duration or --primary-color globally!

6. Troubleshooting Common CSS Animation Issues

Animations not working? Check these fixes:

  • Animation doesn’t run: Ensure animation-duration is not 0s and animation-name matches the @keyframes name.
  • Janky movement: Use transform (e.g., translate, scale, rotate) and opacity for smooth animations—they trigger GPU acceleration. Avoid animating width, height, or margin (causes layout recalculations).
  • Keyframes not applying: Ensure keyframes are defined before the animation property (some browsers are picky).
  • Browser compatibility: Older browsers (e.g., IE11) require prefixes like -webkit-keyframes, but modern browsers (Chrome, Firefox, Safari) support unprefixed @keyframes.

7. Conclusion: Start Animating!

CSS animations are a powerful tool for creating engaging, interactive websites. With keyframes, animation properties, and a little creativity, you can build everything from subtle hover effects to complex storytelling animations.

The key to mastery is practice. Experiment with the examples above, tweak the timing functions, and try combining animations. The more you play, the more intuitive it becomes!

8. References

Happy animating! 🚀