Skip to content

CSS Cubic Bezier Editor

Drag the control points, watch the real animation next to ease or linear, and copy the timing function as CSS or a Tailwind class.

How it works

A timing function decides how an animation's progress maps onto time. cubic-bezier() describes that mapping as a curve:

css
transition: transform 600ms cubic-bezier(0.22, 1, 0.36, 1);

The curve always starts at (0, 0) and ends at (1, 1). The four numbers are the two control points in between, x1, y1, x2, y2. The horizontal axis is time and the vertical axis is progress, so a steep part of the curve is fast and a flat part is slow.

The rules for the numbers

  • x has to stay between 0 and 1. Time can't run backwards, and a value outside that range makes the whole declaration invalid.
  • y can go past 0 and 1. That is what makes an animation overshoot its target or pull back before it starts.

The built-in keywords are curves too

KeywordSame as
linearcubic-bezier(0, 0, 1, 1)
easecubic-bezier(0.25, 0.1, 0.25, 1)
ease-incubic-bezier(0.42, 0, 1, 1)
ease-outcubic-bezier(0, 0, 0.58, 1)
ease-in-outcubic-bezier(0.42, 0, 0.58, 1)

Which curve to use

  • Things appearing: ease-out. It starts fast and settles, so the interface answers right away.
  • Things leaving: ease-in. It starts slowly and accelerates off the screen.
  • Things moving within the screen: ease-in-out. It speeds up and slows down, like a physical object.
  • Constant motion: linear. Spinners and marquees should not pulse.

Most interface transitions feel right somewhere around 150 to 300ms. Larger movements can take longer.

Different curves for opening and closing

A transition uses the timing function of the state it's going to. Set one on the base rule and another on the open state, and each direction gets its own. Mirror gives you the matching curve for the opposite direction:

css
.menu { transition: transform 200ms cubic-bezier(0.4, 0, 1, 1); /* closing */ } .menu.open { transition: transform 300ms cubic-bezier(0, 0, 0.2, 1); /* opening */ }

Overshoot has limits

A y above 1 pushes the animated value past its end. That works for transform, but opacity stops at 0 and 1, and a property like padding can't go below zero, so part of the curve has no visible effect.

One cubic Bézier can't bounce several times. For springs and bounces, use the linear() timing function, which accepts a long list of points.

Keep it smooth and optional

Animate transform and opacity when you can, since browsers can run them without repainting the page. And respect people who ask for less motion:

css
@media (prefers-reduced-motion: reduce) { .menu { transition: none; } }