CSS

Lesson 8: Transitions & Animations

Duration: 60 minutes | Difficulty: Advanced | Prerequisites: Lessons 1-7 completed

πŸ“‹ Learning Objectives

By the end of this lesson, you will be able to:

🎯 CSS Transitions

Transitions provide smooth changes between CSS property values over a specified duration.

Basic Transition Syntax

.element {
    transition: property duration timing-function delay;
}

/* Examples */
.button {
    background: #3498db;
    transition: background 0.3s ease;
}

.button:hover {
    background: #2980b9;
}

/* Multiple properties */
.card {
    transform: scale(1);
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
    transform: scale(1.05);
    box-shadow: 0 8px 32px rgba(0,0,0,0.2);
}

Transition Properties

Individual Properties

.element {
    transition-property: opacity, transform;
    transition-duration: 0.3s, 0.5s;
    transition-timing-function: ease, ease-in-out;
    transition-delay: 0s, 0.1s;
}

/* All properties */
.all-properties {
    transition: all 0.3s ease;
}

Timing Functions

.linear { transition-timing-function: linear; }
.ease { transition-timing-function: ease; } /* Default */
.ease-in { transition-timing-function: ease-in; }
.ease-out { transition-timing-function: ease-out; }
.ease-in-out { transition-timing-function: ease-in-out; }

/* Custom cubic-bezier */
.custom {
    transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* Steps (for sprite animations) */
.steps {
    transition-timing-function: steps(4, end);
}

🎬 CSS Animations

Animations allow you to animate CSS properties using keyframes, providing more control than transitions.

Keyframe Syntax

@keyframes animationName {
    0% { /* from */
        opacity: 0;
        transform: translateY(20px);
    }
    
    50% {
        opacity: 0.5;
        transform: translateY(-10px);
    }
    
    100% { /* to */
        opacity: 1;
        transform: translateY(0);
    }
}

/* Alternative syntax */
@keyframes slideIn {
    from {
        transform: translateX(-100%);
    }
    to {
        transform: translateX(0);
    }
}

Animation Properties

.animated-element {
    animation-name: slideIn;
    animation-duration: 0.5s;
    animation-timing-function: ease-out;
    animation-delay: 0.2s;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: both;
    animation-play-state: running;
}

/* Shorthand */
.element {
    animation: slideIn 0.5s ease-out 0.2s 1 normal both;
}

Animation Direction

.normal { animation-direction: normal; }     /* Default: forward */
.reverse { animation-direction: reverse; }    /* Backward */
.alternate { animation-direction: alternate; } /* Forward, then backward */
.alternate-reverse { animation-direction: alternate-reverse; }

Fill Mode

.none { animation-fill-mode: none; }         /* Default */
.forwards { animation-fill-mode: forwards; }  /* Keep final state */
.backwards { animation-fill-mode: backwards; } /* Apply initial state during delay */
.both { animation-fill-mode: both; }         /* Both forwards and backwards */

πŸ› οΈ Hands-On Exercises

Exercise 1: Interactive Button Effects

<div class="button-showcase">
    <button class="btn btn-hover">Hover Effect</button>
    <button class="btn btn-press">Press Effect</button>
    <button class="btn btn-ripple">Ripple Effect</button>
    <button class="btn btn-loading">Loading</button>
</div>
.btn {
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    margin: 0.5rem;
    position: relative;
    overflow: hidden;
    transition: all 0.3s ease;
}

/* Hover effect */
.btn-hover {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    transform: translateY(0);
}

.btn-hover:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}

.btn-hover:active {
    transform: translateY(0);
    box-shadow: 0 5px 10px rgba(102, 126, 234, 0.4);
}

/* Press effect */
.btn-press {
    background: #e74c3c;
    color: white;
    transition: transform 0.1s ease;
}

.btn-press:active {
    transform: scale(0.95);
}

/* Ripple effect */
.btn-ripple {
    background: #2ecc71;
    color: white;
}

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

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

/* Loading animation */
.btn-loading {
    background: #3498db;
    color: white;
    min-width: 120px;
}

.btn-loading::after {
    content: '';
    width: 16px;
    height: 16px;
    border: 2px solid transparent;
    border-top: 2px solid white;
    border-radius: 50%;
    display: inline-block;
    margin-left: 8px;
    animation: spin 1s linear infinite;
}

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

Exercise 2: Card Animations

<div class="cards-container">
    <div class="card card-fade-in">
        <h3>Fade In</h3>
        <p>Gentle appearance animation</p>
    </div>
    
    <div class="card card-slide-up">
        <h3>Slide Up</h3>
        <p>Slides up from bottom</p>
    </div>
    
    <div class="card card-bounce">
        <h3>Bounce</h3>
        <p>Playful bounce effect</p>
    </div>
</div>
.cards-container {
    display: flex;
    gap: 2rem;
    padding: 2rem;
    flex-wrap: wrap;
    justify-content: center;
}

.card {
    background: white;
    padding: 2rem;
    border-radius: 12px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    width: 250px;
    text-align: center;
}

/* Fade in animation */
.card-fade-in {
    opacity: 0;
    animation: fadeIn 0.8s ease-out forwards;
}

@keyframes fadeIn {
    to {
        opacity: 1;
    }
}

/* Slide up animation */
.card-slide-up {
    opacity: 0;
    transform: translateY(30px);
    animation: slideUp 0.6s ease-out 0.2s forwards;
}

@keyframes slideUp {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Bounce animation */
.card-bounce {
    animation: bounce 0.8s ease-out 0.4s both;
}

@keyframes bounce {
    0%, 20%, 53%, 80%, 100% {
        animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
        transform: translateY(0);
    }
    40%, 43% {
        animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
        transform: translateY(-30px);
    }
    70% {
        animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
        transform: translateY(-15px);
    }
    90% {
        transform: translateY(-4px);
    }
}

Exercise 3: Loading Animations

<div class="loaders">
    <div class="loader loader-dots">
        <span></span>
        <span></span>
        <span></span>
    </div>
    
    <div class="loader loader-pulse"></div>
    
    <div class="loader loader-bars">
        <div></div>
        <div></div>
        <div></div>
        <div></div>
    </div>
</div>
.loaders {
    display: flex;
    gap: 4rem;
    justify-content: center;
    align-items: center;
    padding: 4rem;
}

.loader {
    display: inline-block;
}

/* Dots loader */
.loader-dots {
    display: flex;
    gap: 0.5rem;
}

.loader-dots span {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #3498db;
    animation: dotPulse 1.4s infinite ease-in-out both;
}

.loader-dots span:nth-child(1) { animation-delay: -0.32s; }
.loader-dots span:nth-child(2) { animation-delay: -0.16s; }

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

/* Pulse loader */
.loader-pulse {
    width: 40px;
    height: 40px;
    background: #e74c3c;
    border-radius: 50%;
    animation: pulse 2s infinite;
}

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

/* Bars loader */
.loader-bars {
    display: flex;
    gap: 4px;
    align-items: end;
}

.loader-bars div {
    width: 6px;
    height: 40px;
    background: #2ecc71;
    animation: bars 1.2s infinite ease-in-out;
}

.loader-bars div:nth-child(1) { animation-delay: -1.2s; }
.loader-bars div:nth-child(2) { animation-delay: -1.1s; }
.loader-bars div:nth-child(3) { animation-delay: -1.0s; }
.loader-bars div:nth-child(4) { animation-delay: -0.9s; }

@keyframes bars {
    0%, 40%, 100% {
        transform: scaleY(0.4);
    }
    20% {
        transform: scaleY(1);
    }
}

Exercise 4: Page Transitions

/* Page entrance animations */
.page-enter {
    animation: pageEnter 0.8s ease-out;
}

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

/* Scroll-triggered animations */
.reveal {
    opacity: 0;
    transform: translateY(50px);
    transition: all 0.6s ease-out;
}

.reveal.active {
    opacity: 1;
    transform: translateY(0);
}

/* Hover animations for interactive elements */
.interactive {
    transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.interactive:hover {
    transform: scale(1.05) rotate(-2deg);
}

🚨 Common Pitfalls

  1. Overusing animations: Less is more - use purposeful animations
  2. Poor performance: Animate transform and opacity for best performance
  3. Ignoring accessibility: Respect prefers-reduced-motion
  4. Too fast/slow: Test timing for natural feel
  5. Blocking interactions: Ensure animations don’t prevent user actions

πŸ” Best Practices

  1. Animate transform and opacity for performance
  2. Use appropriate timing: 200-500ms for micro-interactions
  3. Respect user preferences with prefers-reduced-motion
  4. Progressive enhancement: Ensure functionality without animations
  5. Test on different devices for performance

πŸ“ Performance Considerations

/* Good: GPU-accelerated properties */
.good {
    transform: translateX(100px);
    opacity: 0.5;
}

/* Avoid: Layout-triggering properties */
.avoid {
    left: 100px;
    width: 200px;
    height: 100px;
}

/* Force hardware acceleration */
.accelerated {
    will-change: transform;
    transform: translateZ(0); /* Create stacking context */
}

πŸ“ Assessment

Quick Quiz

  1. What’s the difference between transitions and animations?
  2. Which CSS properties are best for performance when animating?
  3. How do you make an animation repeat infinitely?
  4. What does animation-fill-mode: forwards do?

Practice Tasks

  1. Create a smooth navigation menu with hover effects
  2. Build a loading spinner using keyframe animations
  3. Design page entrance animations
  4. Create hover effects for a card gallery

πŸ”— What’s Next?

In Lesson 9, we’ll explore Forms & User Interface, learning how to style form controls, create custom inputs, and build beautiful, accessible user interface components.


Animations bring life to your designs. Use them thoughtfully to enhance user experience without overwhelming the interface.