CSS Transition Timing Functions: Easing and Cubic Bezier Curves
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.
.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:
.element { transition: all 0.3s ease; }linear
Constant speed from start to finish. No acceleration or deceleration:
.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:
.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:
.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:
.element { transition: all 0.3s ease-in-out; }Best for: elements that move and stop within the viewport.
Comparing Timing Functions
.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:
/* 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
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)Popular Custom Curves
/* 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:
/* 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
/* 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
/* 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
/* 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
/* 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.