Introduction to CSS Animations

Animations and transitions bring life to web pages, enhancing user experience by providing visual feedback, guiding attention, and creating engaging interfaces. CSS offers powerful, performant ways to animate elements without JavaScript.

Transitions

Simple animations between two states, triggered by changes like hover or class toggles.

Keyframe Animations

Complex, multi-step animations with precise control over the animation sequence.

Why Use CSS for Animations?

Performance

CSS animations are optimized by browsers and often hardware-accelerated, making them more performant than JavaScript animations for many use cases.

Simplicity

CSS animations require less code than JavaScript alternatives and are easier to implement for common animation patterns.

Declarative

CSS animations are declarative, meaning you describe what should happen rather than how it should happen, making them easier to understand and maintain.

Progressive Enhancement

CSS animations can be added as an enhancement without breaking functionality for users with animations disabled or unsupported browsers.

CSS Transitions

Transitions provide a way to control animation speed when changing CSS properties. Instead of having property changes take effect immediately, you can cause the changes to take place over a period of time.

Basic Syntax

.element {
  /* Initial state */
  opacity: 0.5;
  transform: scale(1);
  
  /* Transition definition */
  transition-property: opacity, transform;
  transition-duration: 0.3s;
  transition-timing-function: ease-in-out;
  transition-delay: 0s;
  
  /* Shorthand */
  /* transition: property duration timing-function delay; */
  transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
}

.element:hover {
  /* Target state */
  opacity: 1;
  transform: scale(1.1);
}

Transition Properties

PropertyDescriptionExample Values
transition-propertySpecifies which CSS properties should be animatedall, opacity, transform, width, height
transition-durationDefines how long the transition takes to complete0.3s, 300ms, 2s
transition-timing-functionSpecifies the speed curve of the transitionease, linear, ease-in, ease-out, ease-in-out, cubic-bezier(0.1, 0.7, 1.0, 0.1)
transition-delayDefines when the transition will start0s, 0.5s, 500ms
transition (shorthand)Combines all transition properties into one declarationall 0.3s ease 0s, opacity 0.5s linear

Animatable Properties

Not all CSS properties can be transitioned. Here are some commonly animated properties:

Timing Functions

Timing functions control the pace of the animation, making it more natural and engaging. CSS Timing Functions Visualization
/* Predefined timing functions */
transition-timing-function: ease;        /* Default: slow start, fast middle, slow end */
transition-timing-function: linear;      /* Constant speed throughout */
transition-timing-function: ease-in;     /* Slow start, fast end */
transition-timing-function: ease-out;    /* Fast start, slow end */
transition-timing-function: ease-in-out; /* Slow start and end, fast middle */

/* Custom cubic bezier curve */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55); /* Custom bounce effect */

/* Steps function for frame-by-frame animation */
transition-timing-function: steps(5, end); /* 5 discrete steps */
You can visualize and create custom cubic-bezier curves using tools like cubic-bezier.com.

Practical Examples

Button Hover Effect

.button {
  background-color: #3498db;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.button:hover {
  background-color: #2980b9;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.button:active {
  transform: translateY(0);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

Card Expansion

.card {
  width: 300px;
  height: 200px;
  background-color: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  overflow: hidden;
  transition: height 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  height: 300px;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}

.card-content {
  opacity: 0;
  max-height: 0;
  transition: opacity 0.3s ease, max-height 0.3s ease;
}

.card:hover .card-content {
  opacity: 1;
  max-height: 100px;
}
.nav-link {
  position: relative;
  color: #333;
  text-decoration: none;
  padding: 5px 0;
}

.nav-link::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 0;
  height: 2px;
  background-color: #3498db;
  transition: width 0.3s ease;
}

.nav-link:hover::after {
  width: 100%;
}

CSS Keyframe Animations

Keyframe animations provide more control than transitions, allowing you to define multiple states throughout the animation sequence.

Basic Syntax

/* Define the animation */
@keyframes slide-in {
  0% {
    transform: translateX(-100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

/* Apply the animation */
.element {
  animation-name: slide-in;
  animation-duration: 1s;
  animation-timing-function: ease-out;
  animation-delay: 0s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-play-state: running;
  
  /* Shorthand */
  /* animation: name duration timing-function delay iteration-count direction fill-mode play-state; */
  animation: slide-in 1s ease-out 0s 1 normal forwards running;
}

Animation Properties

PropertyDescriptionExample Values
animation-nameSpecifies the name of the @keyframes ruleslide-in, fade-out, pulse
animation-durationDefines how long the animation takes to complete one cycle1s, 500ms, 2.5s
animation-timing-functionSpecifies the speed curve of the animationease, linear, ease-in, ease-out, ease-in-out, cubic-bezier(0.1, 0.7, 1.0, 0.1)
animation-delayDefines when the animation will start0s, 1s, -0.5s (negative values start the animation partway through)
animation-iteration-countSpecifies how many times the animation should run1, 3, infinite
animation-directionDefines whether the animation should play forward, backward, or alternatenormal, reverse, alternate, alternate-reverse
animation-fill-modeSpecifies what values are applied before/after the animationnone, forwards, backwards, both
animation-play-stateSpecifies whether the animation is running or pausedrunning, paused
animation (shorthand)Combines all animation properties into one declarationslide-in 1s ease-out 0s 1 normal forwards

Keyframe Syntax

Keyframes define the stages and styles of the animation sequence.
/* Using percentages (recommended for most cases) */
@keyframes fade-in-out {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* Using from and to (only for simple start/end animations) */
@keyframes grow {
  from {
    transform: scale(0);
  }
  to {
    transform: scale(1);
  }
}
You can define as many keyframe stages as needed, and you don’t need to include every property in every stage. CSS will interpolate between defined keyframes.

Animation Fill Mode

The animation-fill-mode property determines what happens before the animation starts and after it ends.
ValueBefore AnimationAfter Animation
none (default)Element displays in its normal stateElement returns to its normal state
forwardsElement displays in its normal stateElement retains the computed values set by the last keyframe
backwardsElement displays the computed values set by the first keyframeElement returns to its normal state
bothElement displays the computed values set by the first keyframeElement retains the computed values set by the last keyframe

Practical Examples

Loading Spinner

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

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

Pulsing Effect

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.1);
    opacity: 0.7;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.notification-badge {
  display: inline-block;
  background-color: #e74c3c;
  color: white;
  border-radius: 50%;
  padding: 5px 10px;
  animation: pulse 2s ease infinite;
}

Staggered Animation

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.list-item {
  opacity: 0;
  animation: fade-in 0.5s ease forwards;
}

.list-item:nth-child(1) { animation-delay: 0.1s; }
.list-item:nth-child(2) { animation-delay: 0.2s; }
.list-item:nth-child(3) { animation-delay: 0.3s; }
.list-item:nth-child(4) { animation-delay: 0.4s; }
.list-item:nth-child(5) { animation-delay: 0.5s; }

Advanced Animation Techniques

Multiple Animations

You can apply multiple animations to a single element by separating them with commas.
.element {
  animation: 
    fade-in 1s ease forwards,
    slide-up 1.2s ease-out forwards,
    pulse 2s ease 1s infinite;
}

Animating Along a Path

With CSS offset-path (part of the Motion Path Module), you can animate elements along a defined path.
@keyframes move-along-path {
  0% {
    offset-distance: 0%;
  }
  100% {
    offset-distance: 100%;
  }
}

.element {
  offset-path: path('M 0 0 C 50 -50 50 50 100 0');
  offset-rotate: auto;
  animation: move-along-path 3s linear infinite;
}

Scroll-Triggered Animations

You can trigger animations when elements enter the viewport using the Intersection Observer API with JavaScript.
<div class="animate-on-scroll fade-in">This will animate when scrolled into view</div>
.fade-in {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
    }
  });
}, { threshold: 0.1 });

document.querySelectorAll('.animate-on-scroll').forEach(element => {
  observer.observe(element);
});

CSS Variables for Dynamic Animations

CSS Custom Properties (variables) can make animations more dynamic and configurable.
:root {
  --animation-duration: 1s;
  --animation-easing: ease-in-out;
  --animation-distance: 20px;
  --primary-color: #3498db;
}

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

.element {
  animation: slide-in var(--animation-duration) var(--animation-easing) forwards;
  color: var(--primary-color);
}

/* Adjust variables for specific elements or states */
.element.slow {
  --animation-duration: 2s;
  --animation-distance: 50px;
}

Animating SVG

SVG elements can be animated with CSS, offering unique possibilities for graphics animation.
<svg width="200" height="200" viewBox="0 0 200 200">
  <circle class="circle" cx="100" cy="100" r="50" fill="none" stroke="#3498db" stroke-width="4" />
</svg>
@keyframes draw-circle {
  0% {
    stroke-dasharray: 0 314;
    stroke-dashoffset: 0;
  }
  100% {
    stroke-dasharray: 314 314;
    stroke-dashoffset: 0;
  }
}

.circle {
  animation: draw-circle 2s ease forwards;
}

Performance Optimization

Animations can impact performance if not implemented carefully. Here are some best practices:

Use Hardware-Accelerated Properties

Some CSS properties are optimized for animation and can be hardware-accelerated:
  • transform
  • opacity
  • filter
These properties don’t trigger layout or paint operations, making them ideal for animations.
/* Good - uses hardware acceleration */
.element {
  transform: translateX(100px);
  opacity: 0.5;
}

/* Avoid - triggers layout recalculation */
.element {
  left: 100px;
  height: 50%;
}

Promote Elements to Their Own Layer

You can force an element onto its own GPU layer with will-change or transform: translateZ(0).
.element {
  will-change: transform, opacity;
  /* Or */
  transform: translateZ(0);
}
Use will-change sparingly and only for elements that will actually change. Overuse can cause memory issues.

Reduce Paint Areas

Limit the size of animated elements and use contain: paint to isolate their paint areas.
.animated-element {
  contain: paint;
  animation: slide 1s ease;
}

Avoid Animating Expensive Properties

Some properties are particularly expensive to animate because they trigger layout recalculations:
  • width, height
  • top, right, bottom, left
  • margin, padding
  • font-size
  • position
When possible, use transform equivalents:
/* Instead of */
.element {
  animation: grow 1s ease;
}
@keyframes grow {
  from { width: 100px; height: 100px; }
  to { width: 200px; height: 200px; }
}

/* Use */
.element {
  width: 100px;
  height: 100px;
  animation: grow 1s ease;
}
@keyframes grow {
  from { transform: scale(1); }
  to { transform: scale(2); }
}

Animation Accessibility

Animations can enhance user experience but may cause issues for some users, particularly those with vestibular disorders or motion sensitivity.

Respect User Preferences

The prefers-reduced-motion media query allows you to provide alternative animations or disable them based on user system preferences.
.element {
  animation: bounce 1s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .element {
    /* Disable the animation */
    animation: none;
    
    /* Or provide a subtle alternative */
    transition: opacity 0.5s ease;
  }
}

Avoid Flashing Content

Rapid flashing or strobing effects can trigger seizures in people with photosensitive epilepsy. Avoid animations with:
  • Flashing more than 3 times per second
  • Large areas of flashing content
  • High contrast flashing

Provide Controls

For significant animations, provide users with controls to pause, stop, or disable animations.
<button id="toggle-animations">Pause Animations</button>
const toggleButton = document.getElementById('toggle-animations');
let animationsEnabled = true;

toggleButton.addEventListener('click', () => {
  animationsEnabled = !animationsEnabled;
  document.body.classList.toggle('animations-disabled', !animationsEnabled);
  toggleButton.textContent = animationsEnabled ? 'Pause Animations' : 'Enable Animations';
});
.animations-disabled * {
  animation: none !important;
  transition: none !important;
}

Browser Compatibility

Modern browsers have good support for CSS animations and transitions, but there are some considerations for older browsers.

Vendor Prefixes

For older browsers, you might need vendor prefixes. However, most modern browsers no longer require them for animations.
.element {
  -webkit-animation: fade 1s ease;
  animation: fade 1s ease;
}

@-webkit-keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
Consider using a tool like Autoprefixer to automatically add necessary vendor prefixes based on your browser support requirements.

Feature Detection

You can use feature detection to provide fallbacks for browsers that don’t support certain animation features.
if ('animation' in document.documentElement.style) {
  // Animations are supported
  document.body.classList.add('animations-enabled');
} else {
  // Animations are not supported
  document.body.classList.add('animations-disabled');
}

Common Animation Patterns

Here are some reusable animation patterns for common UI elements:

Fade In

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.fade-in {
  animation: fade-in 0.5s ease forwards;
}

Slide In

@keyframes slide-in-left {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

@keyframes slide-in-right {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

@keyframes slide-in-up {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

@keyframes slide-in-down {
  from { transform: translateY(-100%); }
  to { transform: translateY(0); }
}

.slide-in-left {
  animation: slide-in-left 0.5s ease forwards;
}

.slide-in-right {
  animation: slide-in-right 0.5s ease forwards;
}

.slide-in-up {
  animation: slide-in-up 0.5s ease forwards;
}

.slide-in-down {
  animation: slide-in-down 0.5s ease forwards;
}

Bounce

@keyframes bounce {
  0%, 20%, 50%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-30px);
  }
  60% {
    transform: translateY(-15px);
  }
}

.bounce {
  animation: bounce 1s ease;
}

Pulse

@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}

.pulse {
  animation: pulse 1.5s ease infinite;
}

Shake

@keyframes shake {
  0%, 100% {
    transform: translateX(0);
  }
  10%, 30%, 50%, 70%, 90% {
    transform: translateX(-10px);
  }
  20%, 40%, 60%, 80% {
    transform: translateX(10px);
  }
}

.shake {
  animation: shake 0.8s ease;
}

Rotate

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

.rotate {
  animation: rotate 2s linear infinite;
}

Flip

@keyframes flip {
  0% {
    transform: perspective(400px) rotateY(0);
  }
  100% {
    transform: perspective(400px) rotateY(360deg);
  }
}

.flip {
  animation: flip 1s ease;
  backface-visibility: visible;
}

Animation Libraries

While CSS animations are powerful, animation libraries can provide additional features and simplify complex animations.

Animate.css

A library of ready-to-use, cross-browser animations for use in your web projects.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
<div class="animate__animated animate__bounce">Bouncing element</div>
Animate.css Documentation

Magic Animations

CSS3 animations with special effects.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/magic/1.1.0/magic.min.css">
<div class="magictime vanishIn">Appearing element</div>
Magic Animations on GitHub

Hover.css

A collection of CSS3 powered hover effects.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/hover.css/2.3.1/css/hover-min.css">
<button class="hvr-grow">Grow on Hover</button>
Hover.css Documentation

CSShake

CSS classes to move your DOM with shake effects.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/csshake/1.5.3/csshake.min.css">
<div class="shake">Shake me</div>
CSShake Documentation

JavaScript Animation Libraries

For more complex animations or when you need programmatic control, JavaScript libraries can be helpful:

GSAP (GreenSock Animation Platform)

Professional-grade animation for the modern web.
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script>
  gsap.to(".element", {
    duration: 1,
    x: 100,
    y: 50,
    rotation: 360,
    ease: "elastic"
  });
</script>
GSAP Documentation

Anime.js

A lightweight JavaScript animation library.
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
<script>
  anime({
    targets: '.element',
    translateX: 250,
    rotate: '1turn',
    duration: 800,
    easing: 'easeInOutQuad'
  });
</script>
Anime.js Documentation

Motion One

A new animation library, built on the Web Animations API for high performance.
<script src="https://cdn.jsdelivr.net/npm/motion@10.15.5/dist/motion.min.js"></script>
<script>
  import { animate } from "motion";
  animate(".element", { x: 100, opacity: 0 }, { duration: 1 });
</script>
Motion One Documentation

Lottie

Render After Effects animations natively in the browser.
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.9.4/lottie.min.js"></script>
<div id="animation-container"></div>
<script>
  lottie.loadAnimation({
    container: document.getElementById('animation-container'),
    renderer: 'svg',
    loop: true,
    autoplay: true,
    path: 'animation.json'
  });
</script>
Lottie Documentation

Conclusion

CSS animations and transitions provide powerful tools for creating engaging, interactive web experiences. By understanding the fundamentals and following best practices, you can create smooth, performant animations that enhance your user interface without sacrificing accessibility or performance. Key takeaways:
  1. Use transitions for simple state changes and keyframe animations for more complex sequences
  2. Prefer animating transform and opacity for better performance
  3. Consider accessibility with prefers-reduced-motion and animation controls
  4. Optimize animations for performance by minimizing repaints and reflows
  5. Use animation libraries when you need more complex effects or better browser support

Resources

Documentation

Tools

Further Learning