CSS Animations and Transitions

CSS Animations and Transitions

CSS animations and transitions bring websites to life by adding smooth, professional movement and effects. This guide covers everything you need to create engaging, performant animations.

What are CSS Animations?

Animations are visual changes over time. They can make your website more engaging, guide user attention, and improve user experience when used thoughtfully.

Animation vs Transitions

  • Transitions: Smooth changes between element states
  • Animations: Multiple keyframes and more complex sequences
/* Transitions - animate from one state to another */
.button {
    background: blue;
    transition: background 0.3s ease;
}

.button:hover {
    background: red; /* Animate this change */
}

/* Animations - full animation sequences */
.spinner {
    animation: spin 1s linear infinite;
}

CSS Transitions

Basic Transition Properties

.element {
    /* What properties to animate */
    transition-property: background, color, transform;
    
    /* How long animation takes */
    transition-duration: 0.3s;
    
    /* Timing function (easing) */
    transition-timing-function: ease-in-out;
    
    /* Delay before starting */
    transition-delay: 0.1s;
    
    /* Shorthand - combines all */
    transition: all 0.3s ease-in-out 0.1s;
}

Transition Examples

/* Button hover effect */
.button {
    background: #007bff;
    color: white;
    border: none;
    padding: 12px 24px;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.3s ease;
    transform: translateY(0);
}

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

/* Form input focus */
.input {
    border: 2px solid #ddd;
    padding: 10px;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.input:focus {
    outline: none;
    border-color: #007bff;
    box-shadow: 0 0 0 3px rgba(0,123,255,0.25);
}

/* Card flip on hover */
.card {
    perspective: 1000px;
    transition: transform 0.6s;
}

.card:hover {
    transform: rotateY(10deg);
}

Timing Functions

/* Linear - constant speed */
.linear { transition-timing-function: linear; }

/* Ease - slow start, fast middle, slow end */
.ease { transition-timing-function: ease; }

/* Ease-in - slow start, fast end */
.ease-in { transition-timing-function: ease-in; }

/* Ease-out - fast start, slow end */
.ease-out { transition-timing-function: ease-out; }

/* Ease-in-out - slow start and end */
.ease-in-out { transition-timing-function: ease-in-out; }

/* Custom cubic-bezier */
.custom { 
    transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

CSS Animations

Animation Properties

.element {
    /* Animation name (matches @keyframes) */
    animation-name: slideIn;
    
    /* Duration */
    animation-duration: 1s;
    
    /* Timing function */
    animation-timing-function: ease-out;
    
    /* Delay before starting */
    animation-delay: 0.2s;
    
    /* Number of repetitions */
    animation-iteration-count: 3;
    
    /* Direction */
    animation-direction: alternate;
    
    /* Fill mode */
    animation-fill-mode: forwards;
    
    /* Play state */
    animation-play-state: running;
    
    /* Shorthand */
    animation: slideIn 1s ease-out 0.2s 3 alternate forwards;
}

Keyframes

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

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

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

@keyframes fadeInUp {
    0% {
        opacity: 0;
        transform: translateY(30px);
    }
    100% {
        opacity: 1;
        transform: translateY(0);
    }
}

Multiple Keyframes

@keyframes multiStep {
    0% {
        transform: scale(0.5) rotate(0deg);
        opacity: 0;
    }
    25% {
        transform: scale(0.75) rotate(90deg);
        opacity: 0.5;
    }
    50% {
        transform: scale(1) rotate(180deg);
        opacity: 0.75;
    }
    75% {
        transform: scale(1.25) rotate(270deg);
        opacity: 0.5;
    }
    100% {
        transform: scale(0.5) rotate(360deg);
        opacity: 0;
    }
}

Practical Animation Examples

Loading Spinners

/* Circle spinner */
.spinner-circle {
    width: 40px;
    height: 40px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid #007bff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

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

/* Dots spinner */
.spinner-dots {
    display: flex;
    gap: 8px;
}

.spinner-dots div {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #007bff;
    animation: dotPulse 1.4s ease-in-out infinite both;
}

.spinner-dots div:nth-child(1) { animation-delay: -0.32s; }
.spinner-dots div:nth-child(2) { animation-delay: -0.16s; }
.spinner-dots div:nth-child(3) { animation-delay: 0s; }

@keyframes dotPulse {
    0%, 80%, 100% {
        transform: scale(0);
    }
    40% {
        transform: scale(1);
    }
}

Button Animations

.btn-slide {
    position: relative;
    overflow: hidden;
    z-index: 1;
    transition: color 0.3s ease;
}

.btn-slide::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(255,255,255,0.2);
    transform: translateX(-100%);
    transition: transform 0.3s ease;
    z-index: -1;
}

.btn-slide:hover {
    color: white;
}

.btn-slide:hover::after {
    transform: translateX(0);
}

/* Ripple effect */
.btn-ripple {
    position: relative;
    overflow: hidden;
}

.btn-ripple::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 0;
    height: 0;
    border-radius: 50%;
    background: rgba(255,255,255,0.5);
    transform: translate(-50%, -50%);
    transition: width 0.6s, height 0.6s;
}

.btn-ripple:active::after {
    width: 300px;
    height: 300px;
}

Text Animations

/* Typewriter effect */
.typewriter {
    font-family: monospace;
    overflow: hidden;
    border-right: 3px solid #007bff;
    white-space: nowrap;
    animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite;
}

@keyframes typing {
    from { width: 0; }
    to { width: 100%; }
}

@keyframes blink-caret {
    from, to { border-color: transparent; }
    50% { border-color: #007bff; }
}

/* Glitch effect */
.glitch {
    position: relative;
    color: white;
    font-size: 2em;
    text-transform: uppercase;
    animation: glitch 2s infinite;
}

.glitch::before,
.glitch::after {
    content: attr(data-text);
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.glitch::before {
    animation: glitch-1 0.5s infinite;
    color: #00ffff;
    z-index: -1;
}

.glitch::after {
    animation: glitch-2 0.5s infinite;
    color: #ff00ff;
    z-index: -2;
}

@keyframes glitch-1 {
    0% {
        clip: rect(42px, 9999px, 44px, 0);
    }
    25% {
        clip: rect(12px, 9999px, 58px, 0);
    }
    50% {
        clip: rect(72px, 9999px, 30px, 0);
    }
    75% {
        clip: rect(42px, 9999px, 100px, 0);
    }
    100% {
        clip: rect(32px, 9999px, 58px, 0);
    }
}

@keyframes glitch-2 {
    0% {
        clip: rect(65px, 9999px, 119px, 0);
    }
    25% {
        clip: rect(25px, 9999px, 140px, 0);
    }
    50% {
        clip: rect(70px, 9999px, 85px, 0);
    }
    75% {
        clip: rect(65px, 9999px, 119px, 0);
    }
    100% {
        clip: rect(85px, 9999px, 60px, 0);
    }
}

Hover Effects

/* Image zoom on hover */
.zoom-container {
    overflow: hidden;
    border-radius: 8px;
}

.zoom-image {
    transition: transform 0.3s ease;
    transform-origin: center center;
}

.zoom-container:hover .zoom-image {
    transform: scale(1.2);
}

/* Card lift effect */
.lift-card {
    background: white;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    transform: translateY(0);
}

.lift-card:hover {
    transform: translateY(-8px);
    box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}

/* Gradient shift */
.gradient-shift {
    background: linear-gradient(45deg, #667eea, #764ba2);
    background-size: 200% 200%;
    transition: background-position 0.5s ease;
}

.gradient-shift:hover {
    background-position: 100% 100%;
}

Advanced Animation Techniques

CSS Variables with Animations

:root {
    --primary-color: #007bff;
    --animation-duration: 0.3s;
    --animation-easing: ease-in-out;
}

.animated-element {
    color: var(--primary-color);
    transition: transform var(--animation-duration) var(--animation-easing);
}

.animated-element:hover {
    transform: scale(1.1) rotate(5deg);
}

Performance Optimization

/* Use transform and opacity for better performance */
.optimized-animation {
    /* Good: Use transform */
    transform: translateX(100px) scale(1.2);
    transition: transform 0.3s ease;
}

.poor-performance {
    /* Avoid: Animating layout properties */
    left: 100px;
    top: 50px;
    width: 200px;
    height: 100px;
    transition: left 0.3s ease, top 0.3s ease, width 0.3s ease, height 0.3s ease;
}

/* Use will-change for optimization */
.will-change {
    will-change: transform;
    animation: complexAnimation 2s ease-in-out;
}

Hardware Acceleration

/* Force GPU acceleration */
.gpu-accelerated {
    /* Enable GPU acceleration */
    transform: translateZ(0);
    backface-visibility: hidden;
    perspective: 1000px;
}

.animated-3d {
    transform: translateZ(0) rotateX(45deg) rotateY(45deg);
    transform-style: preserve-3d;
}

JavaScript Animation Control

Animation Control Classes

class AnimationController {
    constructor(element) {
        this.element = element;
        this.isPlaying = false;
    }

    play() {
        this.element.style.animationPlayState = 'running';
        this.isPlaying = true;
    }

    pause() {
        this.element.style.animationPlayState = 'paused';
        this.isPlaying = false;
    }

    restart() {
        this.element.style.animation = 'none';
        // Force reflow
        this.element.offsetHeight;
        this.element.style.animation = null;
        this.isPlaying = true;
    }

    toggle() {
        if (this.isPlaying) {
            this.pause();
        } else {
            this.play();
        }
    }
}

// Usage
const animatedElement = document.querySelector('.my-animation');
const controller = new AnimationController(animatedElement);

document.querySelector('.play-btn').addEventListener('click', () => controller.play());
document.querySelector('.pause-btn').addEventListener('click', () => controller.pause());
document.querySelector('.toggle-btn').addEventListener('click', () => controller.toggle());

Scroll Animations

// Intersection Observer for scroll animations
class ScrollAnimator {
    constructor() {
        this.observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    entry.target.classList.add('animate-in');
                }
            });
        }, {
            threshold: 0.1,
            rootMargin: '0px 0px -50px 0px'
        });

        // Observe all elements with animate-on-scroll class
        document.querySelectorAll('.animate-on-scroll').forEach(el => {
            this.observer.observe(el);
        });
    }
}

// CSS for scroll animations
.animate-in {
    opacity: 0;
    transform: translateY(30px);
    transition: all 0.6s ease-out;
}

.animate-in.animated {
    opacity: 1;
    transform: translateY(0);
}

// Initialize
const scrollAnimator = new ScrollAnimator();

Responsive Animations

Mobile-First Approach

/* Default (mobile) - simpler animations */
.responsive-animation {
    animation: simpleSlide 0.5s ease-out;
}

/* Tablet - moderate animations */
@media (min-width: 768px) {
    .responsive-animation {
        animation: complexSlide 0.8s cubic-bezier(0.4, 0, 0.2, 1);
    }
}

/* Desktop - full animations */
@media (min-width: 1024px) {
    .responsive-animation {
        animation: fullSlide 1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
    }
}

/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
    .responsive-animation {
        animation: none !important;
        transition: none !important;
    }
}

Animation Breakpoints

/* Disable animations on slow connections */
@media (prefers-reduced-data: reduce) {
    .heavy-animation {
        animation: none;
        transform: none;
    }
}

/* Adjust duration for different devices */
.animation {
    --duration: 0.5s;
    animation: fadeIn var(--duration) ease-out;
}

@media (max-width: 768px) {
    .animation {
        --duration: 0.3s; /* Faster on mobile */
    }
}

Complete Examples

Animated Landing Page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Animated Landing Page</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            overflow-x: hidden;
        }

        .hero {
            height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
        }

        .hero-content {
            opacity: 0;
            transform: translateY(50px);
            animation: fadeInUp 1s ease-out 0.5s forwards;
        }

        .hero h1 {
            font-size: clamp(2rem, 5vw, 4rem);
            margin-bottom: 1rem;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }

        .hero p {
            font-size: 1.2rem;
            margin-bottom: 2rem;
            opacity: 0;
            transform: translateY(30px);
            animation: fadeInUp 1s ease-out 1s forwards;
        }

        .cta-button {
            display: inline-block;
            padding: 15px 30px;
            background: white;
            color: #667eea;
            text-decoration: none;
            border-radius: 50px;
            font-weight: bold;
            transform: scale(0);
            animation: scaleIn 0.5s ease-out 1.5s forwards;
            transition: all 0.3s ease;
        }

        .cta-button:hover {
            transform: scale(1.1);
            box-shadow: 0 10px 25px rgba(0,0,0,0.2);
        }

        .floating-shapes {
            position: absolute;
            width: 100%;
            height: 100%;
            overflow: hidden;
            z-index: -1;
        }

        .shape {
            position: absolute;
            border-radius: 50%;
            opacity: 0.1;
        }

        .shape:nth-child(1) {
            width: 80px;
            height: 80px;
            background: rgba(255,255,255,0.2);
            top: 20%;
            left: 10%;
            animation: float 20s infinite ease-in-out;
        }

        .shape:nth-child(2) {
            width: 60px;
            height: 60px;
            background: rgba(255,255,255,0.1);
            top: 60%;
            right: 15%;
            animation: float 15s infinite ease-in-out reverse;
        }

        .shape:nth-child(3) {
            width: 100px;
            height: 100px;
            background: rgba(255,255,255,0.15);
            bottom: 20%;
            left: 20%;
            animation: float 25s infinite ease-in-out 2s;
        }

        @keyframes fadeInUp {
            from {
                opacity: 0;
                transform: translateY(50px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        @keyframes scaleIn {
            from {
                transform: scale(0);
            }
            to {
                transform: scale(1);
            }
        }

        @keyframes float {
            0%, 100% {
                transform: translateY(0) rotate(0deg);
            }
            33% {
                transform: translateY(-30px) rotate(120deg);
            }
            66% {
                transform: translateY(-20px) rotate(240deg);
            }
        }

        /* Responsive animations */
        @media (max-width: 768px) {
            .hero h1 {
                font-size: 2rem;
            }
            
            .floating-shapes {
                display: none; /* Hide on mobile for performance */
            }
        }

        @media (prefers-reduced-motion: reduce) {
            .hero-content,
            .cta-button,
            .floating-shapes * {
                animation: none !important;
            }
        }
    </style>
</head>
<body>
    <div class="floating-shapes">
        <div class="shape"></div>
        <div class="shape"></div>
        <div class="shape"></div>
    </div>
    
    <div class="hero">
        <div class="hero-content">
            <h1>Welcome to Our Amazing Product</h1>
            <p>Transform your business with our innovative solution</p>
            <a href="#get-started" class="cta-button">Get Started</a>
        </div>
    </div>
</body>
</html>

Animated Card Component

.card {
    background: white;
    border-radius: 12px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    padding: 1.5rem;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    transform: translateY(0);
    position: relative;
    overflow: hidden;
}

.card:hover {
    transform: translateY(-8px);
    box-shadow: 0 12px 20px rgba(0,0,0,0.15);
}

.card::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
    transition: left 0.5s ease;
}

.card:hover::before {
    left: 100%;
}

.card-title {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
    color: #333;
    transition: color 0.3s ease;
}

.card:hover .card-title {
    color: #007bff;
}

.card-image {
    width: 100%;
    height: 200px;
    object-fit: cover;
    transition: transform 0.3s ease;
}

.card:hover .card-image {
    transform: scale(1.05);
}

Best Practices

Performance Guidelines

/* Animate transform, opacity, filter */
.performant {
    transform: translateX(100px) scale(1.2);
    opacity: 0.8;
    filter: blur(2px);
}

/* Avoid animating layout properties */
.non-performant {
    /* Avoid animating these properties */
    width: 200px;
    height: 100px;
    left: 50px;
    top: 20px;
    margin: 10px;
    padding: 15px;
}

/* Use will-change */
.optimized-element {
    will-change: transform;
}

/* Remove animations when not needed */
@media (prefers-reduced-motion: reduce) {
    .animated {
        animation: none !important;
        transition: none !important;
    }
}

Accessibility Considerations

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
    .animation {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* Provide fallbacks */
.animation {
    /* Modern animation */
    animation: slideIn 0.5s ease-out;
}

@supports not (animation: slideIn 0.5s ease-out) {
    .animation {
        /* Fallback for older browsers */
        transform: translateX(0);
        transition: transform 0.5s ease-out;
    }
}

/* Ensure animations don't cause issues */
.focus-able {
    animation-play-state: paused;
}

.focus-able:focus,
.focus-able:hover {
    animation-play-state: running;
}

External Resources:

Related Tutorials:

Last updated on