Level up CSS transitions with cubic-bezier

If you’ve ever noticed that your CSS transitions feel a bit… flat, you’re not alone (I seem them a lot! 😭). The default ease timing function works fine, but it’s generic. Real-world motion has character, it bounces, overshoots and feels natural. That is what cubic-bezier is for.

What is cubic-bezier?

In CSS, cubic-bezier is used in the transition-timing-function or animation-timing-function property. It defines how intermediate values are calculated during a transition using a cubic Bézier curve (orly?! 🦉).

The syntax looks like this:

cubic-bezier(x1, y1, x2, y2)

Those four numbers define two control points on a curve. The curve starts at (0,0) and ends at (1,1), representing the beginning and end of the transition. The control points shape how the animation progresses between those states.

For a detailed explanation, check out the MDN Web Docs.

Examples that make a difference

Let’s look at some examples that use default easing functions and then show a more interesting timing functions. Hover to see the effects:

Before:

After taken from Perron’s docs; see logo:

This is how it is done with Tailwind CSS:

<div class="transition duration-500 ease-[cubic-bezier(0.68,-0.6,0.32,1.6)] hover:scale-80 hover:rotate-359"></div>

And how you write it with CSS:

.icon {
  transition: transform 0.5s cubic-bezier(.68 -.6 .32 1.6);
}
.icon:hover {
  transform: scale(0.8) rotate(359deg);
}

Before:

After:

This is how it is done with Tailwind CSS:

<div class="transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:scale-150"></div>

And how you write it with CSS:

.button {
  transition: transform 0.3s cubic-bezier(.34 1.56 .64 1);
}
.button:hover {
  transform: scale(1.5);
}

Before:

After:

This is how it is done with Tailwind CSS:

<div class="transition-transform duration-500 ease-[cubic-bezier(0.87,0,0.13,1)] hover:translate-x-8"></div>

And how you write it with CSS:

.pill {
  transition: transform 0.5s cubic-bezier(.87 0 .13 1);
}
.pill:hover {
  transform: translateX(2rem);
}

Notice the negative values in the cubic-bezier functions? Those create the “bounce” effect that overshoots its target before settling into place and what gives that “premium” feel.

Tailwind CSS provides better defaults with ease-{in,out,in-out} classes

Finding the perfect curve

Rather than guessing values, use Easing Wizard. It’s a beautiful, interactive tool (that I shared in my notes some weeks ago; only accessible via atom feed 🤫) where you can drag control points and see the resulting animation in real-time. Copy the generated cubic-bezier values directly into your CSS.

The difference between ease and a well-crafted cubic-bezier curve is immediately noticeable. Your interfaces will feel more responsive, more alive and more professional.

Try replacing your next transition: all 300s ease with transition: all 300s cubic-bezier(.68 -.6 .32 1.6) and see the difference for yourself.

Product-minded Rails notes

Once a month: straightforward notes on improving UX in Rails—what to simplify, what to measure, and UI/frontend changes that move real usage.

Over to you…

What did you like about this article? Learned something knew? Found something is missing or even broken? 🫣 Let me (and others) know!

Comments are powered by Chirp Form

Want to read me more?