Understanding Cubic Bezier Transitions
Why transition-timing-function matters so much and how to design your own easing curves.
It's easy to type transition: all 0.3s ease; and move on. But the difference between a site that feels "premium" and one that feels "cheap" is often hiding exactly in that one line.
Why the curve matters
The browser's default ease doesn't really mimic real-world motion. Real objects don't stop abruptly — they decelerate gradually. That's exactly what cubic-bezier() lets you control.
.card {
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.card:hover {
transform: translateY(-4px);
}
Try tweaking different easing curves in this live interactive sandbox below to see and feel the difference:
transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);What do those four numbers mean?
cubic-bezier(x1, y1, x2, y2) defines the motion's speed curve using two control points:
- x1, y1: how the curve leaves its starting point
- x2, y2: how the curve approaches its end point
Y values can exceed 1 — meaning the motion slightly overshoots its target before settling. That gives buttons a lively, tactile feel.
/* A button press with a subtle overshoot */
.button {
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.button:active {
transform: scale(0.96);
}
A practical rule of thumb
You don't need a custom curve for every animation. Picking 2-3 standard curves for a project (say, one for "entrance," one for "hover," one for "emphasis") and defining them as CSS custom properties keeps things consistent and easy to maintain:
:root {
--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
--ease-out-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
}
It might look like a tiny detail, but a well-chosen easing curve is one of the cheapest ways to make an interface feel expensive.