Animations in CSS

Animations in CSS let an element move through multiple visual states over time. They are used for loading indicators, attention effects, decorative motion, onboarding screens, notification feedback, skeleton loaders, and small interface details. Unlike transitions, animations do not need a state change such as hover. They can start automatically, repeat, pause, reverse, or run with several keyframe steps.

CSS animations are powerful, but they should be used with purpose. Good animation explains state, gives feedback, or adds subtle polish. Bad animation distracts from content, slows the interface, or makes the page uncomfortable to use. The technical skill is writing keyframes, but the design skill is knowing when motion actually helps.

Animation vs Transition

A transition animates between two values when something changes. An animation uses @keyframes and can define many steps. Use transitions for simple hover or open states. Use animations for looping loaders, multi-step motion, and effects that should run without a direct state change.

NeedBetter Tool
Button hover colortransition
Dropdown open statetransition
Loading spinneranimation
Pulsing notification dotanimation
Multi-step entrance sequenceanimation

Basic @keyframes Syntax

Animations start with a @keyframes rule. The keyframes describe what the element looks like at different points in the animation.

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

  to {
    opacity: 1;
  }
}

.panel {
  animation: fade-in 240ms ease;
}

The element starts transparent and becomes fully visible. The animation shorthand connects the keyframes to the element.

Percentage Keyframes

Keyframes can use percentages for more than two states. This is useful when an animation needs a middle step.

@keyframes pop {
  0% {
    scale: 0.95;
    opacity: 0;
  }

  60% {
    scale: 1.03;
    opacity: 1;
  }

  100% {
    scale: 1;
    opacity: 1;
  }
}

This creates a small pop effect. The element grows slightly past normal size, then settles back.

animation-name and animation-duration

The animation-name selects the keyframes. The animation-duration controls how long one cycle takes.

.toast {
  animation-name: pop;
  animation-duration: 220ms;
}

Short UI animations often feel best between 150ms and 400ms. Large decorative motion can be longer, but interface feedback should not feel slow.

animation-timing-function

The timing function controls acceleration during the animation. Common values include linear, ease, ease-in, ease-out, ease-in-out, steps(), and cubic-bezier().

.toast {
  animation: pop 220ms cubic-bezier(0.22, 1, 0.36, 1);
}

Custom easing can make motion feel smoother and more natural. Use consistent easing values across similar components.

animation-delay

The animation-delay property waits before starting the animation. It can also use a negative delay to start as if the animation is already partway through.

.item:nth-child(2) {
  animation-delay: 80ms;
}

.item:nth-child(3) {
  animation-delay: 160ms;
}

Small delays can create a staggered entrance. Keep delays short so content does not feel like it is waiting for decoration.

animation-iteration-count

The iteration count controls how many times the animation runs. It can be a number or infinite.

.loader {
  animation: spin 800ms linear infinite;
}

Infinite animation is common for loaders and subtle status indicators. Avoid infinite decorative motion near long reading content because it can distract users.

animation-direction

The direction controls whether the animation plays forward, backward, alternates, or alternates in reverse.

.pulse {
  animation: pulse 1.4s ease-in-out infinite alternate;
}

Alternate direction is useful for pulsing, breathing, floating, and back-and-forth decorative effects.

animation-fill-mode

The fill mode controls what styles apply before and after the animation runs. The value forwards keeps the final keyframe style after the animation ends.

.notice {
  animation: slide-in 260ms ease forwards;
}

Without fill mode, the element may return to its original style after the animation completes. This can surprise beginners.

animation-play-state

The play state can pause or run an animation. This is useful for hover pauses, user controls, or reducing distraction.

.ticker:hover {
  animation-play-state: paused;
}

If content moves continuously, users should have a way to pause it. This is especially important for accessibility.

Performance Friendly Animation

The safest animation properties are usually transform and opacity. Animating width, height, top, left, or layout-heavy shadows can trigger more browser work.

@keyframes slide-up {
  from {
    opacity: 0;
    transform: translateY(12px);
  }

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

This animation changes opacity and transform, which is a common performance-friendly pattern for UI entrance effects.

Reduced Motion

Some users prefer reduced motion. Respect that preference with prefers-reduced-motion.

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    scroll-behavior: auto;
  }
}

This does not mean every animation is forbidden. It means motion-heavy effects should have a safer alternative.

Multiple Animations on One Element

An element can run multiple animations at the same time by separating animation values with commas. This is useful when one animation controls opacity and another controls movement or rotation.

.badge {
  animation:
    fade-in 240ms ease forwards,
    float-y 2s ease-in-out infinite alternate;
}

Multiple animations should stay understandable. If the combined effect is hard to debug, split the visual parts into child elements or simplify the motion.

Animation Shorthand Order

The animation shorthand can include name, duration, timing function, delay, iteration count, direction, fill mode, and play state. The order is flexible for many values, but duration and delay can be confusing because both use time units.

.box {
  animation: slide-in 300ms ease-out 120ms both;
}

In this example, 300ms is the duration and 120ms is the delay. If two time values are present, the first is duration and the second is delay.

Entrance Animation Example

Entrance animations are common for cards, modals, and notifications. They should be short and should not block the user from reading content.

@keyframes enter-card {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.98);
  }

  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.card {
  animation: enter-card 220ms ease-out both;
}

The both fill mode applies the first keyframe before the animation starts and keeps the final keyframe after it ends. This prevents visual snapping.

Loading Spinner Example

A spinner is a classic CSS animation because it repeats continuously while work is happening.

@keyframes spin {
  to {
    transform: rotate(1turn);
  }
}

.spinner {
  width: 2rem;
  height: 2rem;
  border: 3px solid #e5e7eb;
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 750ms linear infinite;
}

Use a spinner only when the wait is real. For page skeletons or content placeholders, a subtle shimmer may explain loading better than a spinning icon.

Animation Events and JavaScript

JavaScript can listen for animation events such as animationstart, animationiteration, and animationend. This is useful when a class should be removed after an exit animation completes.

Keep the boundary clear: CSS should describe the motion, while JavaScript should coordinate state only when needed. Avoid putting timing logic in two places if one source can control it.

Debugging CSS Animations

If an animation does not run, check whether the animation name matches the keyframes name, whether the duration is greater than zero, whether the element exists in the expected state, and whether reduced motion rules are overriding it.

  • Check the keyframes name spelling.
  • Check animation-duration.
  • Check animation-fill-mode when styles snap back.
  • Check whether another animation shorthand resets longhand values.
  • Check prefers-reduced-motion rules.
  • Inspect the animation panel in browser developer tools.

Skeleton Loading Animation

A skeleton loader can suggest that content is loading without showing a spinner. It usually uses a moving gradient across placeholder blocks.

@keyframes shimmer {
  from {
    background-position: 200% 0;
  }

  to {
    background-position: -200% 0;
  }
}

.skeleton {
  background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
  background-size: 200% 100%;
  animation: shimmer 1.2s linear infinite;
}

Skeleton effects should be subtle. If the content loads quickly, heavy animation is unnecessary. If loading takes time, the skeleton should match the approximate shape of the real content.

Scroll and View Animation Note

Modern CSS is gaining scroll-linked and view-based animation features. These can connect animation progress to scroll position or element visibility. They are powerful, but they should be used carefully because scroll-based motion can easily become distracting.

For most beginner and intermediate projects, standard keyframe animations, transitions, and small JavaScript-triggered state classes are enough. Master those first before building complex scroll motion.

Animation Timing System

On a real website, animation values should be consistent. Define common durations and easing curves so buttons, cards, modals, and notifications feel like they belong to the same interface.

:root {
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --duration-fast: 160ms;
  --duration-normal: 240ms;
}

.toast {
  animation: slide-up var(--duration-normal) var(--ease-out) both;
}

This makes motion easier to maintain. If the design needs to feel faster or calmer, the shared values can be adjusted without editing every component.

When Not to Animate

Do not animate just because CSS makes it possible. Avoid animation on long paragraphs, essential reading paths, important form validation text, or anything that repeats endlessly near the main content. Motion should support the user, not compete with the content.

Animation Checklist

  • Use keyframes when motion needs multiple steps.
  • Use transitions for simple state changes.
  • Animate transform and opacity when possible.
  • Keep repeated motion away from long reading areas.
  • Respect reduced motion preferences.
  • Keep timing values consistent across the site.

The best animation is usually the one users understand without thinking about it. If motion explains what changed, it helps. If motion only shows off, remove it or reduce it.

Keep motion useful.

Common Animation Mistakes

  • Animating layout-heavy properties when transform would work.
  • Using infinite motion near reading content.
  • Forgetting animation-fill-mode and seeing elements snap back.
  • Making UI feedback animations too slow.
  • Ignoring prefers-reduced-motion.
  • Using animation when a simple transition would be clearer.

Animations in CSS FAQ

What is animation in CSS?

A CSS animation uses keyframes to change styles over time, with control over duration, timing, delay, direction, and repetition.

What is the difference between transition and animation?

A transition animates between two states, while an animation can run through multiple keyframes and does not always need a state change.

Can CSS animations run forever?

Yes. Use animation-iteration-count: infinite, but avoid distracting infinite motion.

Which properties are best for CSS animations?

Transform and opacity are usually the safest properties for smooth UI animation.


Continue learning CSS in order
Follow the topic sequence with the previous and next lesson.