Skip to main content

Two Types of Animations

  1. Transitions - Animate property changes (simple, common)
  2. Keyframe Animations - Complex, multi-step animations

CSS Transitions

Transitions animate changes from one state to another.

Basic Syntax

transition: property duration timing-function delay;

Simple Example

.button {
  background-color: var(--color-secondary);
  transition: background-color 300ms ease;
}

.button:hover {
  background-color: var(--color-secondary-dark);
  /* Smoothly animates to dark blue */
}

Transition Properties

.element {
  transition-property: background-color;
  transition-property: all;  /* Animate all changing properties */
  transition-property: transform, opacity;  /* Multiple */
}

Shorthand Syntax

.element {
  transition: background-color 300ms ease;
}

Real Examples from the Project

Button Hover

.header__search-button {
  background-color: var(--color-secondary);
  transition: background-color var(--transition-fast);
}

.header__search-button:hover {
  background-color: var(--color-secondary-dark);
}

/* Variables defined in :root */
:root {
  --transition-fast: 150ms ease;
}
.header__nav-link {
  color: var(--color-gray-600);
  border-radius: var(--border-radius-sm);
  transition: background-color var(--transition-fast);
}

.header__nav-link:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

Logo Scale on Hover

.header__logo-link {
  display: flex;
  align-items: baseline;
  transition: transform var(--transition-fast);
}

.header__logo-link:hover {
  transform: scale(1.02);  /* Slightly larger */
}

Call-to-Action Button

.hero__cta {
  background-color: var(--color-secondary);
  transition: background-color var(--transition-fast),
              transform var(--transition-fast);
}

.hero__cta:hover {
  background-color: var(--color-secondary-dark);
  transform: translateY(-2px);  /* Lift up slightly */
}

.hero__cta:active {
  transform: translateY(0);  /* Push back down when clicked */
}

Product Card

.product-card {
  background-color: var(--color-white);
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition-base),
              box-shadow var(--transition-base);
}

.product-card:hover {
  transform: translateY(-8px);  /* Float up */
  box-shadow: var(--shadow-lg); /* Stronger shadow */
}

Image Zoom on Card Hover

.product-card__image {
  transition: transform var(--transition-base);
}

/* When card is hovered, scale the image inside */
.product-card:hover .product-card__image {
  transform: scale(1.05);
}

Benefits Item

.benefits__item {
  background-color: var(--color-gray-100);
  transition: transform var(--transition-fast),
              box-shadow var(--transition-fast);
}

.benefits__item:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.footer__link {
  color: var(--color-gray-500);
  transition: color var(--transition-fast);
}

.footer__link:hover {
  color: var(--color-secondary);
}
.footer__social-link {
  transition: transform var(--transition-fast);
}

.footer__social-link:hover {
  transform: scale(1.2);  /* 20% larger */
}

Timing Functions Explained

Visual Comparison

.element {
  transition: transform 500ms ease;
  /* Slow start, fast middle, slow end */
  /* Most natural-feeling */
}

Custom Cubic Bezier

.element {
  /* Custom timing curve */
  transition: transform 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
  /* Creates a bounce effect */
}
Use cubic-bezier.com to create custom timing functions visually.

Transform Property

Common transforms used in transitions:
.element {
  transform: translateX(20px);   /* Move right 20px */
  transform: translateY(-10px);  /* Move up 10px */
  transform: translate(20px, -10px);  /* Both */
}

Keyframe Animations

For complex, multi-step animations.

Basic Syntax

/* 1. Define the animation */
@keyframes animation-name {
  from {
    /* Starting state */
  }
  to {
    /* Ending state */
  }
}

/* 2. Apply to element */
.element {
  animation: animation-name duration timing-function iteration-count;
}

Percentage Keyframes

@keyframes slide-in {
  0% {
    transform: translateX(-100%);
    opacity: 0;
  }
  50% {
    opacity: 0.5;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

Real Animation: Loading Spinner

.products__spinner {
  width: 40px;
  height: 40px;
  border: 4px solid var(--color-gray-200);
  border-top-color: var(--color-secondary);
  border-radius: var(--border-radius-full);
  
  /* Apply animation */
  animation: spin 1s linear infinite;
}

/* Define animation */
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
Use linear timing for spinners so rotation speed is constant.

Animation Properties

.element {
  animation-name: slide-in;
}

Shorthand

.element {
  animation: name duration timing-function delay iteration-count direction fill-mode;
  
  /* Example */
  animation: slide-in 1s ease-out 0s 1 normal forwards;
  
  /* Common shorthand */
  animation: spin 1s linear infinite;
}

Common Animation Patterns

Fade In

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

.element {
  animation: fade-in 500ms ease-out;
}

Slide In from Left

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

.element {
  animation: slide-in-left 600ms ease-out;
}

Bounce

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

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

Pulse (Scale)

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

.element {
  animation: pulse 2s ease-in-out infinite;
}

Shake

@keyframes shake {
  0%, 100% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-10px);
  }
  75% {
    transform: translateX(10px);
  }
}

.element {
  animation: shake 400ms ease-in-out;
}

Performance Tips

Animate transform and opacity - they’re GPU-accelerated
Use will-change sparingly for complex animations
Avoid animating width, height, top, left - they cause reflows
Don’t animate too many elements at once

GPU-Accelerated Properties

/* FAST - GPU accelerated */
.element {
  transform: translateX(100px);
  opacity: 0.5;
}

/* SLOW - Causes reflow/repaint */
.element {
  left: 100px;      /* Use transform instead */
  width: 500px;     /* Avoid animating */
}

will-change

.element {
  /* Tell browser this will animate */
  will-change: transform, opacity;
}

.element:hover {
  transform: scale(1.1);
}
Don’t overuse will-change - it uses extra memory. Only use for complex animations.

Accessibility: Reduced Motion

Respect users who prefer reduced motion:
/* Normal animations */
.element {
  transition: transform 300ms ease;
}

/* Disable for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
Always include prefers-reduced-motion for better accessibility. Some users get motion sickness from animations.

Animation Best Practices

Keep animations subtle and purposeful
Use appropriate durations (150-300ms for most UI)
Match timing to the action (entering vs exiting)
Use easing for natural feel
Test on slower devices
Don’t animate on page load unless necessary
Avoid animations that distract from content

Duration Guidelines

  • Very fast (100-150ms) - Small changes, icons
  • Fast (150-250ms) - Buttons, links, hovers
  • Medium (250-400ms) - Modals, dropdowns, cards
  • Slow (400-600ms) - Page transitions, large movements
  • Very slow (600ms+) - Special effects, attention-grabbing

Variables for Animations

From the project:
:root {
  --transition-fast: 150ms ease;
  --transition-base: 300ms ease;
  --transition-slow: 500ms ease;
}

/* Use throughout */
.button {
  transition: background-color var(--transition-fast);
}

.card {
  transition: transform var(--transition-base),
              box-shadow var(--transition-base);
}

Complete Example: Animated Card

.card {
  background-color: var(--color-white);
  border-radius: var(--border-radius-md);
  box-shadow: var(--shadow-sm);
  
  /* Multiple transitions */
  transition: transform var(--transition-base),
              box-shadow var(--transition-base);
}

.card:hover {
  transform: translateY(-8px);
  box-shadow: var(--shadow-lg);
}

.card__image {
  border-radius: var(--border-radius-md);
  transition: transform var(--transition-base);
}

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

.card__button {
  background-color: var(--color-secondary);
  transition: background-color var(--transition-fast),
              transform var(--transition-fast);
}

.card__button:hover {
  background-color: var(--color-secondary-dark);
  transform: translateY(-2px);
}

.card__button:active {
  transform: translateY(0);
}

Next Steps

Responsive Design

Make animations responsive

Variables

Use variables in animations

Build docs developers (and LLMs) love