Skip to content

CSS Animations and Keyframes: A Complete Guide for Developers

CSS animations bring interfaces to life. From subtle hover effects to complex multi-step transitions, the animation property and @keyframes rule give you fine-grained control over how elements move, fade and transform. This guide covers everything you need to build performant, accessible animations with pure CSS.

The @keyframes Rule

A keyframe block defines what an animation looks like at specific points during its timeline. You specify percentages (or from / to as aliases for 0% and 100%) and the browser interpolates the values between them.

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

For multi-step animations, use percentage keyframes to define intermediate states.

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

The Animation Property

The animation shorthand combines several sub-properties into a single declaration.

animation: name duration timing-function delay
iteration-count direction fill-mode;

Here is a practical example that fades in an element over 400 milliseconds.

.card {
animation: fadeIn 400ms ease-out forwards;
}

Each sub-property can also be set individually: animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction and animation-fill-mode.

Timing Functions

The timing function controls the acceleration curve of your animation. CSS provides several built-in options.

  • ease (default) starts slow, speeds up, then slows down at the end.
  • linear maintains constant speed throughout.
  • ease-in starts slow and accelerates.
  • ease-out starts fast and decelerates. Great for elements entering the viewport.
  • ease-in-out combines ease-in and ease-out for smooth start and finish.
  • cubic-bezier(x1, y1, x2, y2) lets you define a custom curve for precise control.

Custom cubic-bezier values are useful for bouncy or spring-like effects. For example, cubic-bezier(0.68, -0.55, 0.27, 1.55) creates a slight overshoot and snap-back effect.

Common Animation Patterns

Fade In

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

Slide In from Left

@keyframes slideInLeft {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}

Pulse / Scale

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

Rotate

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

Fill Mode and Direction

animation-fill-mode determines what styles apply before and after the animation runs. The most common value is forwards, which keeps the element in its final keyframe state after the animation ends. Without it, the element snaps back to its pre-animation state.

animation-direction controls whether the animation runs forwards, backwards or alternates. Setting alternate with infinite iteration count creates a smooth back-and-forth loop, perfect for breathing or pulsing effects.

Performance Tips

Not all CSS properties animate equally. For smooth 60fps animations, stick to properties that the browser can composite on the GPU.

  • Prefer transform and opacity. These properties are composited on a separate layer and do not trigger layout recalculations or repaints.
  • Avoid animating layout properties. Properties like width, height, margin and top/left force the browser to recalculate layout for the entire page on every frame.
  • Use will-change sparingly. Adding will-change: transform hints to the browser to promote the element to its own layer. But overusing it wastes GPU memory, so only apply it to elements that actually animate.
  • Keep durations reasonable. Animations between 150ms and 500ms feel responsive. Anything over 1 second can feel sluggish unless it is a background decorative effect.

Accessibility: prefers-reduced-motion

Some users experience motion sickness or discomfort from animations. The prefers-reduced-motion media query lets you respect their system preference.

@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}

This removes animations for users who opt out while preserving them for everyone else. It is a small addition that significantly improves inclusivity.

Multiple Animations

You can apply multiple animations to a single element by comma-separating them. Each animation can have its own duration, delay and timing function.

.element {
animation: fadeIn 400ms ease-out,
slideUp 600ms ease-out 200ms;
}

This technique is useful for staggered entrances where you want opacity and position to animate independently with different timings.

Try it yourself

Visually build CSS animations with a live preview. Choose from common presets or customize every property, then copy the generated code.

Open CSS Animation Generator →