A cubic-bezier(x1, y1, x2, y2) easing curve runs from 0,0 to 1,1 and is bent by two control points. Both x values must stay between 0 and 1 or the browser drops the whole declaration, while y values may go below 0 or above 1 to overshoot. Drag the handles below and watch the dots race.
| Keyword | cubic-bezier() value | Feels like |
|---|---|---|
| linear | cubic-bezier(0, 0, 1, 1) | Constant speed, mechanical |
| ease (the default) | cubic-bezier(0.25, 0.1, 0.25, 1) | Quick start, long gentle stop |
| ease-in | cubic-bezier(0.42, 0, 1, 1) | Slow start, abrupt stop |
| ease-out | cubic-bezier(0, 0, 0.58, 1) | Fast start, soft landing |
| ease-in-out | cubic-bezier(0.42, 0, 0.58, 1) | Soft at both ends |
Updated
Typing four numbers and guessing how they will feel rarely works. Here you shape the curve by hand, see the numbers update, and compare the motion side by side with a straight line and the browser default before you paste anything into your stylesheet.
Drag the handles or type values, press Play to race your curve against linear and ease, then copy the CSS or Tailwind class.
The first five come from the CSS Easing Functions specification. The Tailwind rows are that framework's default theme. The overshoot rows are starting points we tuned by eye.
| Name | Source | Value |
|---|---|---|
| linear | CSS keyword | cubic-bezier(0, 0, 1, 1) |
| ease | CSS keyword | cubic-bezier(0.25, 0.1, 0.25, 1) |
| ease-in | CSS keyword | cubic-bezier(0.42, 0, 1, 1) |
| ease-out | CSS keyword | cubic-bezier(0, 0, 0.58, 1) |
| ease-in-out | CSS keyword | cubic-bezier(0.42, 0, 0.58, 1) |
| ease-in (Tailwind) | Tailwind default | cubic-bezier(0.4, 0, 1, 1) |
| ease-out (Tailwind) | Tailwind default | cubic-bezier(0, 0, 0.2, 1) |
| ease-in-out (Tailwind) | Tailwind default | cubic-bezier(0.4, 0, 0.2, 1) |
| springy out | Overshoot | cubic-bezier(0.34, 1.56, 0.64, 1) |
| anticipate | Overshoot | cubic-bezier(0.6, -0.4, 0.4, 1.4) |
| snappy | Overshoot | cubic-bezier(0.2, 0.9, 0.3, 1.1) |
Hover the card. It lifts slightly past its resting point and settles, all from the timing function.
<style>
.card { width: 180px; padding: 18px; border-radius: 14px; background: #eff6ff; border: 1px solid #bfdbfe;
transition: transform 450ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.card:hover { transform: translateY(-12px); }
@media (prefers-reduced-motion: reduce) { .card { transition: none; } }
</style>
<div style="padding-top:20px"><div class="card">Hover me</div></div>The bracket form needs no spaces. The @theme variable turns the curve into a named class.
<!-- one-off -->
<button class="transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:scale-105">Save</button>
/* app.css: reusable */
@import "tailwindcss";
@theme {
--ease-springy: cubic-bezier(0.34, 1.56, 0.64, 1);
}
<!-- then: class="ease-springy" -->
Time runs left to right and progress runs bottom to top. A steep part of the curve means fast movement, a flat part means the element is barely moving. The line from corner to corner is linear motion.
Handles above the top edge or below the bottom edge push progress past 100 percent or below 0 percent for a moment. That is how you get a gentle overshoot without writing keyframes.
The same value works in transition-timing-function, animation-timing-function and the transition and animation shorthands. Inside @keyframes, a timing function set on a keyframe applies only to the stretch until the next keyframe.
Some properties cannot go below zero, such as opacity or a width. If a curve overshoots below 0, those values are clamped, so the dip is simply not visible.
They are the coordinates of the two control points: x1 and y1 for the first, x2 and y2 for the second. The x axis is the share of the duration that has passed and the y axis is how far the animated value has progressed.
If either x value is lower than 0 or higher than 1, the function is invalid and the browser throws away the declaration, falling back to whatever value applied before. Check that x1 and x2 sit between 0 and 1.
No. Tailwind's default theme defines ease-in as cubic-bezier(0.4, 0, 1, 1), ease-out as cubic-bezier(0, 0, 0.2, 1) and ease-in-out as cubic-bezier(0.4, 0, 0.2, 1), which differ from the CSS keywords of the same name. Both sets are presets in the editor so you can compare them.
Write the value in square brackets without spaces, for example ease-[cubic-bezier(0.34,1.56,0.64,1)]. To reuse it, declare a theme variable such as --ease-springy in @theme and Tailwind creates an ease-springy class.
No. One cubic curve can overshoot once and come back, but it cannot bounce several times. For a multi-hop bounce, use @keyframes with several steps or the newer linear() function, which accepts a list of points.
Related: CSS transitions playground, CSS animation generator, Tailwind animations guide, CSS transforms guide, Blob generator.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.