CSSCSS Animations Deep Dive

CSS Animations

CSS animations create complex motion sequences using @keyframes that define animation states at different points in time. Unlike transitions (which animate between two states), animations can have unlimited keyframes and repeat infinitely. Animations are ideal for looping effects, complex sequences, and visual storytelling.

Defining keyframes

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

/* Use the animation */
.element {
  animation: slide-in 1s ease-out;
}

/* Alternative: from and to */
@keyframes fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

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

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

.bouncing {
  animation: bounce 1s ease-in-out infinite;
}
Animation properties

Property

Purpose

Example

animation-name

Name of @keyframes

@keyframes my-animation

animation-duration

How long the animation lasts

1s, 500ms

animation-timing-function

Easing curve

ease, linear, ease-in-out

animation-delay

Delay before starting

0.5s, 200ms

animation-iteration-count

How many times to repeat

1, 5, infinite

animation-direction

Reverse or alternate

normal, reverse, alternate

animation-fill-mode

State after animation

forwards, backwards, both

animation-play-state

Running or paused

running, paused

CSS
/* Longhand */
.element {
  animation-name: slide-in;
  animation-duration: 1s;
  animation-timing-function: ease-out;
  animation-delay: 0.2s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
  animation-fill-mode: forwards;
}

/* Shorthand: name duration timing delay iteration direction fill */
.element {
  animation: slide-in 1s ease-out 0.2s infinite alternate forwards;
}

/* Multiple animations */
.element {
  animation:
    slide-in 1s ease-out,
    fade-in 0.5s ease-in 0.5s;
}

/* Control animation playback */
.element {
  animation-play-state: paused;
}

.element:hover {
  animation-play-state: running;
}
Animation direction and fill mode

CSS
/* animation-direction */
@keyframes move-right {
  0% { transform: translateX(0); }
  100% { transform: translateX(100px); }
}

.normal {
  animation: move-right 2s infinite;
  animation-direction: normal;  /* forward each time */
}

.reverse {
  animation: move-right 2s infinite;
  animation-direction: reverse;  /* backward each time */
}

.alternate {
  animation: move-right 2s infinite;
  animation-direction: alternate;  /* forward, backward, forward... */
}

.alternate-reverse {
  animation: move-right 2s infinite;
  animation-direction: alternate-reverse;  /* backward first */
}

/* animation-fill-mode: what happens after animation */
@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.forwards {
  animation: fade 1s forwards;
  /* Stays in final state (opacity: 1) after animation ends */
}

.backwards {
  animation: fade 1s backwards;
  /* Jumps back to start state (opacity: 0) after animation ends */
}

.both {
  animation: fade 1s both;
  /* Applies both directions */
}

.none {
  animation: fade 1s none;
  /* Returns to original state, no fill */
}
Practical animation examples

CSS
<!-- Infinite loading spinner -->
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

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

<!-- Pulse animation -->
@keyframes pulse {
  0%, 100% {
    opacity: 1;
    box-shadow: 0 0 0 0 rgba(0, 102, 204, 0.7);
  }
  50% {
    opacity: 0.8;
    box-shadow: 0 0 0 10px rgba(0, 102, 204, 0);
  }
}

.pulse {
  animation: pulse 2s infinite;
}

<!-- Bounce animation -->
@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  25% { transform: translateY(-20px); }
  50% { transform: translateY(0); }
  75% { transform: translateY(-10px); }
}

.bouncing-ball {
  width: 20px;
  height: 20px;
  background: blue;
  border-radius: 50%;
  animation: bounce 1s ease-in-out infinite;
}

<!-- Slide in animation -->
@keyframes slide-in {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

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

<!-- Gradient animation -->
@keyframes gradient-shift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.gradient-bg {
  background: linear-gradient(45deg, #667eea, #764ba2, #f093fb);
  background-size: 200% 200%;
  animation: gradient-shift 3s ease infinite;
}
Animation timing and delays

CSS
/* Stagger animations with delays */
.item {
  animation: fade-in 0.5s ease-out forwards;
}

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

<!-- Items fade in one after another -->

/* CSS custom property for delays */
.item {
  animation: fade-in 0.5s ease-out forwards;
  animation-delay: calc(var(--index) * 100ms);
}

/* Then in HTML: <div class="item" style="--index: 0;"> ... </div> */

/* Dynamic timing based on state */
.element {
  animation-duration: 0.3s;
  animation-timing-function: ease-out;
}

.element.fast {
  animation-duration: 0.1s;
}

.element.slow {
  animation-duration: 1s;
}
Animation performance

CSS
/* Optimize with transforms */
@keyframes move {
  from { transform: translateX(0); }    /* fast, GPU-accelerated */
  to { transform: translateX(100px); }
}

@keyframes bad-move {
  from { left: 0; }                      /* slow, triggers layout */
  to { left: 100px; }
}

/* Use opacity for fading (fast) */
@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Optimize by limiting affected elements */
.animated {
  will-change: transform;
  /* Tell browser to optimize for this */
}

/* Reduce animations on slow devices */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
Note
Animations bring websites to life. Use them to: - Guide user attention (pulse, bounce) - Provide feedback (spin loading indicator) - Transition between states (slide, fade) - Create visual interest (gradient shift, parallax) But use sparingly — too many animations distract from content.
animations can impact performance
Complex animations or animations on many elements can bog down older devices. Always: - Use transforms instead of position changes - Test on real devices - Respect `prefers-reduced-motion` preference - Consider using `will-change` for major animations
Next
Combining transforms, transitions, and animations for advanced effects.