html
/

CSS Animations

Last Sync: Today

On this page

15
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

html

CSS Animations

What are CSS Animations?

CSS animations allow you to create complex, multi-step animations using @keyframes. Unlike transitions which require triggers (like hover), animations can run automatically, loop infinitely, and have fine-grained control over each step of the animation sequence.

Basic Syntax

CSSRead-only
1
@keyframes slideIn {
  from {
    transform: translateX(-100px);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.element {
  animation: slideIn 0.5s ease forwards;
}

Keyframes with Percentages

CSSRead-only
1
@keyframes bounce {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-50px);
  }
  100% {
    transform: translateY(0);
  }
}

.ball {
  animation: bounce 1s ease-in-out infinite;
}

Animation Properties

PropertyDescriptionExample
animation-nameName of the @keyframes rulebounce, fadeIn
animation-durationLength of one cycle0.5s, 2000ms
animation-timing-functionSpeed curveease, linear, cubic-bezier()
animation-delayDelay before start0.2s, 1s
animation-iteration-countNumber of cycles1, 3, infinite
animation-directionPlay directionnormal, reverse, alternate
animation-fill-modeStyles before/afternone, forwards, backwards
animation-play-statePause or playrunning, paused

Shorthand Syntax

CSSRead-only
1
/* name duration timing-function delay iteration-count direction fill-mode */
.element {
  animation: bounce 1s ease 0.2s infinite alternate forwards;
}

/* Multiple animations */
.element {
  animation: 
    slideIn 0.5s ease forwards,
    pulse 2s ease-in-out infinite 0.5s;
}

Animation Directions

CSSRead-only
1
.normal {
  animation-direction: normal; /* 0% → 100% */
}

.reverse {
  animation-direction: reverse; /* 100% → 0% */
}

.alternate {
  animation-direction: alternate; /* 0% → 100% → 0% */
}

.alternate-reverse {
  animation-direction: alternate-reverse; /* 100% → 0% → 100% */
}

Fill Modes Explained

ValueBefore AnimationAfter Animation
noneNo stylesReverts to original
forwardsNo stylesRetains last keyframe
backwardsFirst keyframeReverts to original
bothFirst keyframeRetains last keyframe

Practical Examples

CSSRead-only
1
/* Loading spinner */
@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top-color: #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

/* Fade in sequence for list items */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.list-item {
  opacity: 0;
  animation: fadeInUp 0.4s ease forwards;
}

.list-item:nth-child(1) { animation-delay: 0s; }
.list-item:nth-child(2) { animation-delay: 0.1s; }
.list-item:nth-child(3) { animation-delay: 0.2s; }

/* Pulsing notification badge */
@keyframes pulse {
  0% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.2); opacity: 0.7; }
  100% { transform: scale(1); opacity: 1; }
}

.badge {
  animation: pulse 1.5s ease-in-out infinite;
}

Controlling Animation with JavaScript

JavaScriptRead-only
1
// Pause an animation
const element = document.querySelector('.element');
element.style.animationPlayState = 'paused';

// Resume
element.style.animationPlayState = 'running';

// Restart animation
function restartAnimation(el) {
  el.style.animation = 'none';
  el.offsetHeight; // Force reflow
  el.style.animation = null;
}

Animation Events

JavaScriptRead-only
1
const element = document.querySelector('.element');

element.addEventListener('animationstart', () => {
  console.log('Animation started');
});

element.addEventListener('animationiteration', () => {
  console.log('Animation repeated');
});

element.addEventListener('animationend', () => {
  console.log('Animation finished');
});

Performance Best Practices

  • Animate transforms and opacity – These use compositor thread (GPU accelerated)
  • Avoid animating layout properties – width, height, margin, padding trigger reflows
  • Use will-change property – Hints browser to optimize: will-change: transform;
  • Keep animations under 60fps – Test with Chrome DevTools → Performance tab
  • Respect prefers-reduced-motion – Provide accessible alternatives

Accessibility: Reduced Motion

CSSRead-only
1
@keyframes subtlePulse {
  0% { opacity: 0.8; }
  100% { opacity: 1; }
}

/* Full animation by default */
.animated {
  animation: bounce 1s ease infinite;
}

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
  .animated {
    animation: subtlePulse 2s ease infinite;
  }
}

Animation vs Transition Comparison

FeatureTransitionsAnimations
Trigger RequiredYes (hover, focus, class change)No (can auto-start)
KeyframesOnly start/end (from/to)Multiple steps (0% to 100%)
LoopingNo (single cycle)Yes (infinite)
ControlSimpleFine-grained
Best ForInteractive feedbackDecorative/loading animations

Common Mistakes

  • Animating too many properties – Hurts performance
  • Too long durations – Frustrating for users (loading spinners excepted)
  • Forgetting fill-mode – Animations jump back to start without forwards
  • Not testing on real devices – Desktop smooth ≠ mobile smooth
  • Ignoring accessibility – Motion sensitivity is real

Conclusion

CSS animations bring websites to life with smooth, performant motion. They're perfect for loading indicators, entrance animations, attention-grabbing effects, and interactive storytelling. Combine with transforms and opacity for the best performance, and always respect reduced motion preferences.

Try it yourself

@keyframes float {
  0% { transform: translateY(0px); }
  50% { transform: translateY(-20px); }
  100% { transform: translateY(0px); }
}

@keyframes colorShift {
  0% { background: #3498db; }
  50% { background: #e74c3c; }
  100% { background: #3498db; }
}

.box {
  width: 100px;
  height: 100px;
  background: #3498db;
  border-radius: 8px;
  animation: 
    float 2s ease-in-out infinite,
    colorShift 4s ease infinite;
}

.box:hover {
  animation-play-state: paused;
}

Test Your Knowledge

Q1
of 5

Which @rule defines CSS animations?

A
@animation
B
@keyframe
C
@keyframes
D
@animate
Q2
of 5

How do you make an animation loop forever?

A
animation-loop: infinite
B
animation-iteration-count: infinite
C
animation-repeat: forever
D
loop: infinite
Q3
of 5

Which property keeps the final state after animation ends?

A
animation-keep
B
animation-fill-mode: forwards
C
animation-end-state: keep
D
animation-retain
Q4
of 5

Which properties are best for performance?

A
width and height
B
margin and padding
C
transform and opacity
D
top and left
Q5
of 5

Which media feature respects user motion preferences?

A
@media (motion: reduce)
B
@media (prefers-less-motion)
C
@media (reduced-motion)
D
@media (prefers-reduced-motion)

Frequently Asked Questions

What's the difference between animation and transition?

Transitions require a trigger (like hover) and only animate between two states. Animations can run automatically, loop infinitely, and have multiple keyframe steps.

How do I make an animation loop forever?

Set animation-iteration-count: infinite;

Why does my animation jump back at the end?

You need animation-fill-mode: forwards; to retain the last keyframe styles.

Can I pause an animation on hover?

Yes: .element:hover { animation-play-state: paused; }

What's the best way to restart a CSS animation?

Remove and re-add the animation using JavaScript, or toggle a class that triggers a reflow.

Previous

css transitions

Next

css transform

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.