Intermediate10 min read

CSS Transition Timing Functions: Easing and Cubic Bezier Curves

10 min read
348 words
19 sections15 code blocks

What Are Timing Functions?

A timing function controls the speed curve of a CSS transition or animation. Instead of transitioning at a constant speed, timing functions let you create natural-feeling motion with acceleration, deceleration, and bounce effects.

CSS
.box {
  transition: transform 0.3s ease-in-out;
  /*                          ^ timing function */
}

Built-in Timing Functions

ease (Default)

Starts slow, speeds up in the middle, and slows down at the end. This is the default and works well for most transitions:

CSS
.element { transition: all 0.3s ease; }

linear

Constant speed from start to finish. No acceleration or deceleration:

CSS
.element { transition: all 0.3s linear; }

Best for: progress bars, loading animations, continuous rotations.

ease-in

Starts slow and accelerates. The element appears to gain momentum:

CSS
.element { transition: all 0.3s ease-in; }

Best for: elements leaving the screen (exits).

ease-out

Starts fast and decelerates. The element appears to slow down naturally:

CSS
.element { transition: all 0.3s ease-out; }

Best for: elements entering the screen (entrances).

ease-in-out

Starts slow, speeds up, then slows down. Symmetrical acceleration:

CSS
.element { transition: all 0.3s ease-in-out; }

Best for: elements that move and stop within the viewport.

Comparing Timing Functions

CSS
.demo-ease { transition: transform 1s ease; }
.demo-linear { transition: transform 1s linear; }
.demo-ease-in { transition: transform 1s ease-in; }
.demo-ease-out { transition: transform 1s ease-out; }
.demo-ease-in-out { transition: transform 1s ease-in-out; }

Custom Cubic Bezier Curves

All CSS timing functions are based on cubic bezier curves. The cubic-bezier() function gives you full control with four values:

CSS
/* cubic-bezier(x1, y1, x2, y2) */
transition: all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);

The four values define two control points that shape the curve:

  • (x1, y1): First control point (affects the start)
  • (x2, y2): Second control point (affects the end)
  • x values must be between 0 and 1
  • y values can exceed 0-1 for overshoot effects

Built-in Functions as Cubic Bezier

CSS
ease:        cubic-bezier(0.25, 0.1, 0.25, 1.0)
linear:      cubic-bezier(0.0, 0.0, 1.0, 1.0)
ease-in:     cubic-bezier(0.42, 0, 1.0, 1.0)
ease-out:    cubic-bezier(0, 0, 0.58, 1.0)
ease-in-out: cubic-bezier(0.42, 0, 0.58, 1.0)
CSS
/* Snappy - fast start, quick stop */
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);

/* Smooth deceleration */
transition: all 0.3s cubic-bezier(0, 0, 0.2, 1);

/* Overshoot (bounce past target) */
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);

/* Anticipation (pulls back before moving) */
transition: all 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);

/* Elastic feel */
transition: all 0.5s cubic-bezier(0.68, -0.6, 0.32, 1.6);

The steps() Function

steps() creates a stepping animation that jumps between states instead of transitioning smoothly:

CSS
/* 4 discrete steps */
transition: all 1s steps(4);

/* With direction */
transition: all 1s steps(4, jump-start);
transition: all 1s steps(4, jump-end);    /* Default */
transition: all 1s steps(4, jump-both);
transition: all 1s steps(4, jump-none);

Practical steps() Examples

CSS
/* Typewriter effect */
.typewriter {
  overflow: hidden;
  white-space: nowrap;
  animation: typing 3s steps(30) forwards;
}

@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}

/* Sprite animation */
.sprite {
  width: 64px;
  height: 64px;
  background: url('spritesheet.png');
  animation: walk 0.8s steps(8) infinite;
}

/* Clock second hand */
.second-hand {
  animation: tick 60s steps(60) infinite;
}

Choosing the Right Timing Function

For UI Interactions

CSS
/* Hover effects: quick and snappy */
.button {
  transition: all 0.15s ease-out;
}

/* Dropdown menus: smooth entrance */
.dropdown {
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}

/* Modal appearance: slight overshoot feels natural */
.modal {
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

For Page Transitions

CSS
/* Content sliding in */
.slide-in {
  transition: transform 0.4s ease-out;
}

/* Fade in */
.fade-in {
  transition: opacity 0.3s ease;
}

General Guidelines

  • Entrances: Use ease-out (fast start, slow end)
  • Exits: Use ease-in (slow start, fast end)
  • State changes: Use ease or ease-in-out
  • Continuous motion: Use linear
  • Playful UI: Use cubic-bezier with overshoot

Duration Guidelines

CSS
/* Micro-interactions (hover, focus): 100-200ms */
.button { transition: all 0.15s ease; }

/* Small movements: 200-300ms */
.dropdown { transition: all 0.25s ease-out; }

/* Medium movements: 300-500ms */
.modal { transition: all 0.35s ease; }

/* Large movements: 400-700ms */
.page-transition { transition: all 0.5s ease-in-out; }

Summary

Timing functions control the speed curve of CSS transitions. Use ease-out for entrances, ease-in for exits, and ease-in-out for state changes. Create custom curves with cubic-bezier() for overshoot, bounce, and elastic effects. Use steps() for discrete frame-by-frame animations. Match your timing function and duration to the type and size of the movement for natural, polished interactions.