CSS Reference Guide

All TopicsPlayground

Transitions & Animations

@keyframes, transition shorthand, timing functions, will-change, and animation performance — bring your UI to life.

Transitions @keyframes Timing Functions Performance will-change

What Are CSS Transitions

A CSS transition smoothly interpolates a property's value from one state to another over a given duration. Instead of changes happening instantly (e.g., a button changing color on hover), the browser animates between the two values.

Transitions require a trigger — a pseudo-class (:hover, :focus), a class change via JavaScript, or any state change that alters a property value.

CSS
.button {
  background: #6c8cff;
  transition: background 0.3s ease;
}
.button:hover {
  background: #4a6cf7;  /* smoothly transitions to this */
}

Which Properties Can Be Transitioned?

Not every CSS property is animatable. The property must have identifiable intermediate values. Here are common transitionable properties:

CategoryProperties
Colorcolor, background-color, border-color, outline-color, box-shadow color
Layoutwidth, height, max-width, max-height, padding, margin
Transformtransform (translate, rotate, scale, skew)
Opacityopacity
Typographyfont-size, letter-spacing, word-spacing, line-height
Borderborder-width, border-radius, border-color
Positiontop, right, bottom, left
Not Animatabledisplay, font-family, position, float, content

transition-property

Specifies which CSS properties should be transitioned. You can target specific properties or use all.

CSS
/* Single property */
transition-property: background-color;

/* Multiple properties */
transition-property: background-color, transform, opacity;

/* All transitionable properties */
transition-property: all;

/* None (disable transitions) */
transition-property: none;
Live Example — Specific vs All
transform only
(color snaps)
transition: all
(both smooth)
Pro Tip
Using transition-property: all is convenient but can cause unexpected transitions on properties you didn't intend. Prefer listing specific properties for production code.

transition-duration

How long the transition takes. Values in seconds (s) or milliseconds (ms). A duration of 0s means no transition (instant).

CSS
transition-duration: 0.3s;    /* 300 milliseconds */
transition-duration: 300ms;   /* same thing */
transition-duration: 1s;      /* one second */

/* Different durations per property */
transition-property: background, transform;
transition-duration: 0.3s, 0.6s;
Live Example — Duration Comparison (Hover)
0.1s
0.3s
0.6s
1.0s
2.0s

Hover the area above to see all bars expand at different speeds.

transition-timing-function

Controls the acceleration curve of the transition — how the intermediate values are distributed over time.

Built-in Keywords

ValueBehaviorcubic-bezier Equivalent
easeSlow start, fast middle, slow end (default)cubic-bezier(0.25, 0.1, 0.25, 1)
linearConstant speed throughoutcubic-bezier(0, 0, 1, 1)
ease-inSlow start, acceleratescubic-bezier(0.42, 0, 1, 1)
ease-outFast start, deceleratescubic-bezier(0, 0, 0.58, 1)
ease-in-outSlow start and endcubic-bezier(0.42, 0, 0.58, 1)
Live Example — All Timing Functions (Hover)
ease
linear
ease-in
ease-out
ease-in-out
bouncy

Hover to see all dots race. The purple "bouncy" dot uses cubic-bezier(0.68, -0.55, 0.27, 1.55).

steps()

The steps() function divides the transition into equal jumps — useful for sprite sheet animations or typewriter effects.

CSS
/* 5 discrete steps */
transition-timing-function: steps(5, jump-end);

/* Common sprite animation */
.sprite {
  width: 64px;
  background: url('sheet.png');
  animation: walk 0.6s steps(8) infinite;
}

transition-delay

Waits a specified time before the transition starts. Can be positive or negative.

CSS
transition-delay: 0.2s;    /* wait 200ms then start */
transition-delay: -0.3s;   /* start 300ms into the transition */

/* Staggered transitions on multiple properties */
transition-property: opacity, transform;
transition-duration: 0.4s, 0.4s;
transition-delay: 0s, 0.15s;  /* transform starts later */
Live Example — Staggered Delays (Hover)

Hover to see the wave effect created by staggered delays.

Transition Shorthand

The transition shorthand combines all four sub-properties in one declaration:

CSS
/* transition: property duration timing-function delay */
transition: background-color 0.3s ease 0s;

/* Multiple transitions */
transition: background 0.3s ease,
            transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),
            box-shadow 0.3s ease 0.1s;

/* Shortcut for all properties */
transition: all 0.3s ease;

Practical Transition Examples

Live Example — Button Hover Effects
Live Example — Underline Nav Links
Live Example — Card Hover

Hover Card

This card lifts up, gets a stronger shadow, and highlights its border on hover using CSS transitions.

What Are CSS Animations

CSS animations differ from transitions in key ways:

FeatureTransitionsAnimations
TriggerRequires state change (:hover, class toggle)Runs automatically (or on class add)
KeyframesOnly 2 states (from → to)Multiple keyframes (0%, 25%, 50%…)
LoopingCannot loopCan loop infinitely
DirectionForward onlyNormal, reverse, alternate
ComplexitySimple state transitionsComplex multi-step sequences

@keyframes

The @keyframes rule defines the stages of an animation. You give it a name and specify what should happen at each point in time.

CSS
/* Simple from/to */
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Percentage keyframes */
@keyframes bounce {
  0%   { transform: translateY(0); }
  40%  { transform: translateY(-30px); }
  60%  { transform: translateY(-15px); }
  80%  { transform: translateY(-5px); }
  100% { transform: translateY(0); }
}

/* Multiple properties */
@keyframes slideInFade {
  0%   { opacity: 0; transform: translateX(-40px); }
  100% { opacity: 1; transform: translateX(0); }
}

Animation Properties

Apply a keyframe animation to an element with these properties:

CSS
.element {
  animation-name: bounce;             /* name of @keyframes */
  animation-duration: 0.8s;           /* how long */
  animation-timing-function: ease;    /* acceleration curve */
  animation-delay: 0.2s;              /* wait before starting */
  animation-iteration-count: 3;       /* how many times */
  animation-direction: alternate;     /* forward then backward */
  animation-fill-mode: forwards;      /* keep end state */
  animation-play-state: running;      /* running or paused */
}

animation-iteration-count

CSS
animation-iteration-count: 1;          /* play once (default) */
animation-iteration-count: 3;          /* play 3 times */
animation-iteration-count: infinite;   /* loop forever */
animation-iteration-count: 2.5;        /* play 2.5 times, stop mid-animation */

animation-direction

Live Example — All Four Directions
normal
reverse
alternate
alternate-reverse

animation-fill-mode

Controls what styles apply to the element before the animation starts and after it ends.

ValueBefore AnimationAfter Animation
noneElement's own stylesElement's own styles (default)
forwardsElement's own stylesKeeps last keyframe's styles
backwardsApplies first keyframe (during delay)Element's own styles
bothApplies first keyframeKeeps last keyframe's styles
Live Example — forwards vs none
none
forwards
both

After the animation ends: "none" snaps back, "forwards" stays at the end position.

animation-play-state

Pauses or resumes an animation. Commonly used with :hover to let users control animations.

CSS
.spinner {
  animation: spin 2s linear infinite;
}
.spinner:hover {
  animation-play-state: paused;
}
Live Example — Hover to Pause

Hover to pause the rotation.

Animation Shorthand

CSS
/* animation: name duration timing delay count direction fill-mode play-state */
animation: bounce 0.8s ease-in-out 0.2s infinite alternate forwards running;

/* In practice, most values are optional */
animation: fadeIn 0.5s ease;
animation: spin 1s linear infinite;
animation: slideUp 0.4s ease forwards;

/* Multiple animations on one element */
animation: fadeIn 0.5s ease,
            slideUp 0.6s ease 0.1s;

Practical Animation Examples

Loading Spinners

Live Example — Three Spinner Styles

Ring

Dots

Pulse

Attention Animations

Live Example — Shake, Bounce, Pulse
Shake
Bounce
Pulse

Hover each box to trigger the animation.

Gradient Animation

Live Example — Animated Gradient Background

Typing Effect

Live Example — Typewriter
Hello, CSS World!

Performance

Not all CSS properties are equal in animation performance. The browser rendering pipeline has three stages: Layout → Paint → Composite. Animating properties that only trigger compositing is the cheapest.

Property Cost Table

TierPropertiesCost
Composite Onlytransform, opacityCheapest — GPU accelerated, no repaint
Paintcolor, background, box-shadow, border-radiusMedium — repaint but no layout
Layoutwidth, height, padding, margin, top, left, font-sizeExpensive — triggers full layout recalculation
The Golden Rule
For 60fps animations, only animate transform and opacity. These are the only properties that can be handled entirely by the GPU compositor without triggering layout or paint.

will-change

CSS
/* Hint the browser to prepare for animation */
.animated-element {
  will-change: transform, opacity;
}

/* Apply on hover intent, remove when done */
.card {
  transition: transform 0.3s ease;
}
.card:hover {
  will-change: transform;
  transform: translateY(-4px);
}
Don't Overuse will-change
Applying will-change to too many elements wastes GPU memory. Only use it on elements that actually animate, and consider removing it after the animation completes.

prefers-reduced-motion

Some users experience motion sickness or have vestibular disorders. The prefers-reduced-motion media query lets you respect their system preference.

CSS
/* Approach 1: Remove animations 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;
  }
}

/* Approach 2: Only add animations for users who are OK with motion */
.element {
  opacity: 1;  /* default: no animation */
}

@media (prefers-reduced-motion: no-preference) {
  .element {
    animation: fadeIn 0.5s ease;
  }
}
Accessibility Matters
Approach 2 (opt-in motion) is preferred because it treats motion as a progressive enhancement. Elements work without animation, and animation is added only when the user hasn't indicated a preference.

Gotchas

1. display: none Cannot Be Transitioned
Changing from display: none to display: block happens instantly — no transition. Workaround: use opacity and visibility together, or use @starting-style (modern CSS).
2. Transitioning to auto
height: auto cannot be transitioned. The browser doesn't know what numeric value "auto" resolves to ahead of time. Workaround: transition max-height from 0 to a large value, or use CSS Grid's grid-template-rows: 0fr → 1fr trick.
3. Transitions on Page Load
Transitions defined on elements fire on initial page load, causing a flash. Fix: add a .no-transition class to <body> and remove it after load, or defer the class that triggers transitions.
4. Animation Name Conflicts
@keyframes names are global. If two stylesheets define @keyframes fadeIn, the last one wins. Use unique, prefixed names (e.g., myApp-fadeIn) or CSS @layer to avoid conflicts.
5. Transition on the Element, Not the State
Always put the transition on the base element, not on :hover. If you put it on :hover, the return transition (mouse leave) won't be smooth.
CSS
/* WRONG — transition only applies during hover */
.btn:hover {
  background: red;
  transition: background 0.3s;
}

/* RIGHT — transition applies in both directions */
.btn {
  transition: background 0.3s;
}
.btn:hover {
  background: red;
}

Pro Tips

1. Use transform Instead of Position Properties
Instead of animating top/left (triggers layout), use transform: translate() (composite only). Same visual effect, dramatically better performance.
2. cubic-bezier for Personality
Custom curves add character to your UI. Try these popular curves:
  • Snappy: cubic-bezier(0.4, 0, 0.2, 1) — Material Design standard
  • Bouncy: cubic-bezier(0.68, -0.55, 0.27, 1.55)
  • Swift out: cubic-bezier(0.55, 0, 0.1, 1)
3. Stagger Animations with CSS
Use animation-delay with nth-child() for pure-CSS staggering without JavaScript:
CSS
.item {
  animation: fadeInUp 0.5s ease both;
}
.item:nth-child(1) { animation-delay: 0.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; }

/* Or use custom properties for dynamic staggering */
.item {
  animation-delay: calc(var(--index) * 0.1s);
}
/* <div class="item" style="--index:0"> */
/* <div class="item" style="--index:1"> */
4. Debug Animations in DevTools
Chrome DevTools has an Animations tab (More tools → Animations) that lets you inspect, slow down (25%/10% speed), replay, and scrub through CSS animations. Firefox has a similar tool in the Inspector panel.
5. The max-height Trick for Accordion
Since height: auto can't be transitioned, use max-height with a large value:
CSS
.accordion-body {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s ease;
}
.accordion.open .accordion-body {
  max-height: 500px; /* larger than content */
}

/* Modern alternative with Grid: */
.accordion-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.4s ease;
}
.accordion.open .accordion-body {
  grid-template-rows: 1fr;
}
6. Transition Durations Guide
  • Micro-interactions (hover, focus): 100–200ms
  • Small movements (tooltips, dropdowns): 200–300ms
  • Medium movements (modals, panels): 300–500ms
  • Large movements (page transitions): 500–800ms
  • Anything over 1s feels sluggish for UI interactions
Previous CSS Variables