CSS cubic-bezier() Function
The CSS cubic-bezier() function defines a custom easing curve that controls how values change during animations and transitions.
It uses four numeric values to control acceleration, deceleration, and effects such as overshooting the final position. The function is commonly used with transition-timing-function and animation-timing-function to create smoother, more natural movement.
Syntax
cubic-bezier(x1, y1, x2, y2)
The function requires four comma-separated numbers representing two control points on a cubic Bézier curve.
For example:
.box {
transition: transform 0.5s cubic-bezier(0.25, 0.1, 0.25, 1);
}
This creates a transition using the same timing curve as the CSS ease keyword.
Additional examples:
cubic-bezier(0, 0, 1, 1)
cubic-bezier(0.42, 0, 1, 1)
cubic-bezier(0, 0, 0.58, 1)
cubic-bezier(0.42, 0, 0.58, 1)
cubic-bezier(0.34, 1.56, 0.64, 1)
The first four examples correspond to common easing patterns. The final example allows the animated value to overshoot its destination before settling.
The function returns an easing function, not a length, angle, color, or directly usable numeric value.
Parameters
The cubic-bezier() function requires exactly four numeric parameters.
| Parameter | Description | Allowed Values |
|---|---|---|
x1 |
Horizontal coordinate of the first control point. | 0 to 1 |
y1 |
Vertical coordinate of the first control point. | Any finite number |
x2 |
Horizontal coordinate of the second control point. | 0 to 1 |
y2 |
Vertical coordinate of the second control point. | Any finite number |
The horizontal coordinates must remain between 0 and 1, inclusive. The vertical coordinates can be negative or greater than 1.
For example, this is valid:
cubic-bezier(0.3, -0.5, 0.7, 1.5)
However, this is invalid because the first horizontal coordinate exceeds 1:
cubic-bezier(1.5, 0, 0.5, 1)
The four values must be numbers without units such as px, s, or %.
How It Works
An easing function determines how quickly an animated value progresses from its starting state to its ending state.
The horizontal axis represents elapsed progress through the animation, while the vertical axis represents the progress of the animated value.
For example, consider an element moving 200 pixels over one second.
With linear timing, the element travels at a constant rate:
.box {
transition: transform 1s linear;
}
With a custom Bézier curve, the element can start slowly, accelerate, and slow down before reaching its destination:
.box {
transition: transform 1s cubic-bezier(0.42, 0, 0.58, 1);
}
Both transitions take one second. The difference is how the movement is distributed throughout that second.
The cubic-bezier() function changes the rate of interpolation, not the transition duration.
Control Points
A cubic Bézier easing curve is defined by four points. Two are fixed, and two are controlled by the function's arguments.
| Point | Coordinates | Purpose |
|---|---|---|
| P0 | (0, 0) |
Fixed starting point. |
| P1 | (x1, y1) |
First control point, influencing the beginning of the curve. |
| P2 | (x2, y2) |
Second control point, influencing the end of the curve. |
| P3 | (1, 1) |
Fixed ending point. |
For example:
cubic-bezier(0.25, 0.1, 0.25, 1)
The first control point is (0.25, 0.1), and the second is (0.25, 1).
Moving these control points changes the curve and therefore changes the timing of the animation.
The curve always begins at (0, 0) and ends at (1, 1).
Easing Presets
Several CSS timing keywords correspond to specific cubic Bézier curves.
| Keyword | Equivalent Function | Behavior |
|---|---|---|
linear |
cubic-bezier(0, 0, 1, 1) |
Constant rate of progress. |
ease |
cubic-bezier(0.25, 0.1, 0.25, 1) |
Starts slowly, accelerates, then slows down. |
ease-in |
cubic-bezier(0.42, 0, 1, 1) |
Starts slowly and accelerates. |
ease-out |
cubic-bezier(0, 0, 0.58, 1) |
Starts quickly and decelerates. |
ease-in-out |
cubic-bezier(0.42, 0, 0.58, 1) |
Starts and ends slowly. |
For example, these declarations produce equivalent timing:
transition-timing-function: ease-in-out;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
Use a predefined keyword when it produces the desired effect. Use cubic-bezier() when more precise timing control is needed.
Custom Easing
Custom easing curves allow movement to behave differently from the built-in timing keywords.
For example, a transition that accelerates strongly near the beginning can use:
.box {
transition: transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}
A curve with a slower beginning can use:
.box {
transition: transform 0.6s cubic-bezier(0.7, 0, 0.9, 0.3);
}
Custom easing is particularly useful for menus, dialogs, cards, buttons, and other interactive components.
The best curve depends on the distance traveled, the duration, and the intended visual effect.
Short interface transitions often benefit from easing that responds quickly and settles smoothly.
Overshoot Effects
The vertical control point values can extend outside the normal range of 0 to 1.
For example:
cubic-bezier(0.34, 1.56, 0.64, 1)
The second parameter is greater than 1, allowing the interpolated value to move beyond its final destination before returning.
Another example:
cubic-bezier(0.5, -0.5, 0.5, 1.5)
This curve can initially move backward and later overshoot the final value.
For a horizontal translation, overshoot may cause the element to travel farther than the specified distance before settling.
For example:
.box {
transition: transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.box:hover {
transform: translateX(100px);
}
The element can briefly move beyond 100 pixels before returning to its final position.
Overshoot depends on the selected curve and the property being animated. Properties with restricted value ranges may clamp values that exceed their allowed limits.
Using with Transitions
The cubic-bezier() function can control the timing of CSS transitions.
For example:
.button {
transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.button:hover {
transform: scale(1.08);
}
The button smoothly increases in size when hovered.
The same timing function can be applied using the individual transition properties:
.button {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
Both approaches produce the same timing behavior.
Different properties can use different easing curves:
.card {
transition:
transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1),
opacity 0.3s ease-out;
}
The transform and opacity transitions are controlled independently.
Using with Animations
The function can also control the timing of CSS keyframe animations.
For example:
.box {
animation: slide-in 1s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes slide-in {
from {
transform: translateX(-100px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
The animation uses the custom curve to determine how quickly the element moves and fades into view.
It can also be specified separately:
.box {
animation-name: slide-in;
animation-duration: 1s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
For animations with multiple keyframes, timing functions can be assigned to individual keyframe intervals.
For example:
@keyframes move-box {
0% {
transform: translateX(0);
animation-timing-function: cubic-bezier(0.42, 0, 1, 1);
}
50% {
transform: translateX(100px);
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
}
100% {
transform: translateX(200px);
}
}
The first timing function applies from 0% to 50%, while the second applies from 50% to 100%.
Using Custom Properties
CSS custom properties make easing curves reusable across multiple components.
For example:
:root {
--ease-smooth: cubic-bezier(0.2, 0.8, 0.2, 1);
--ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1);
}
.button {
transition: transform 0.3s var(--ease-smooth);
}
.card {
transition: transform 0.5s var(--ease-overshoot);
}
Changing a custom property updates every component that uses it.
Individual parameters can also be stored:
.box {
--x1: 0.25;
--y1: 0.1;
--x2: 0.25;
--y2: 1;
transition-timing-function: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2));
}
This approach is useful when several animations share a consistent motion style.
Example
The following example compares linear timing with three different cubic Bézier curves. Hover over the example area to move all four bars.
HTML:
<div class="bezier-demo" tabindex="0">
<p>Hover or focus here to compare the movement.</p>
<div class="bezier-row">
<span>Linear</span>
<div class="bezier-track"><div class="bezier-box bezier-linear"></div></div>
</div>
<div class="bezier-row">
<span>Ease In</span>
<div class="bezier-track"><div class="bezier-box bezier-in"></div></div>
</div>
<div class="bezier-row">
<span>Ease Out</span>
<div class="bezier-track"><div class="bezier-box bezier-out"></div></div>
</div>
<div class="bezier-row">
<span>Ease In Out</span>
<div class="bezier-track"><div class="bezier-box bezier-in-out"></div></div>
</div>
</div>
CSS:
.bezier-demo {
max-width: 400px;
padding: 15px;
border: 1px solid #ddd;
}
.bezier-demo p {
margin-bottom: 15px;
}
.bezier-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.bezier-row span {
width: 85px;
flex-shrink: 0;
font-size: 13px;
}
.bezier-track {
flex: 1;
min-width: 0;
height: 24px;
background-color: #e9ecef;
}
.bezier-box {
width: 24px;
height: 24px;
background-color: #0066cc;
transform: translateX(0);
}
.bezier-linear {
transition: transform 1.5s linear;
}
.bezier-in {
transition: transform 1.5s cubic-bezier(0.42, 0, 1, 1);
}
.bezier-out {
transition: transform 1.5s cubic-bezier(0, 0, 0.58, 1);
}
.bezier-in-out {
transition: transform 1.5s cubic-bezier(0.42, 0, 0.58, 1);
}
.bezier-demo:is(:hover, :focus-visible) .bezier-box {
transform: translateX(120px);
}
@media (prefers-reduced-motion: reduce) {
.bezier-box {
transition-duration: 0.01ms;
}
}
In the following Browser Result, hover over the area or focus it using the keyboard.
Hover or focus here to compare the movement.
All four bars use the same duration and destination. Their different timing functions control how quickly they move at each stage.
Overshoot Example
This example demonstrates a cubic Bézier curve that moves beyond its final position before returning.
HTML:
<div class="bezier-overshoot-demo" tabindex="0">
<p>Hover or focus to see the overshoot effect.</p>
<div class="bezier-overshoot-track">
<div class="bezier-overshoot-box"></div>
</div>
</div>
CSS:
.bezier-overshoot-demo {
max-width: 400px;
padding: 15px;
border: 1px solid #ddd;
}
.bezier-overshoot-demo p {
margin-bottom: 15px;
}
.bezier-overshoot-track {
height: 36px;
padding: 6px;
background-color: #e9ecef;
overflow: visible;
}
.bezier-overshoot-box {
width: 24px;
height: 24px;
background-color: #0066cc;
transform: translateX(0);
transition: transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.bezier-overshoot-demo:is(:hover, :focus-visible) .bezier-overshoot-box {
transform: translateX(200px);
}
@media (prefers-reduced-motion: reduce) {
.bezier-overshoot-box {
transition-duration: 0.01ms;
}
}
Hover or focus to see the overshoot effect.
The box moves slightly beyond the specified 200-pixel destination before settling back into place.
This effect is created by the timing curve rather than additional keyframes.
Comparison with Other Easing Functions
CSS provides several ways to control animation and transition timing.
| Function or Keyword | Behavior | Typical Use |
|---|---|---|
linear |
Changes values at a constant rate. | Continuous, uniform movement. |
cubic-bezier() |
Uses a custom smooth curve. | Custom acceleration and deceleration. |
steps() |
Changes values in discrete jumps. | Frame-based or stepped animations. |
linear() |
Defines a piecewise linear easing function using multiple stops. | Custom multi-stage easing. |
For example:
transition-timing-function: linear;
transition-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
transition-timing-function: steps(5, end);
transition-timing-function: linear(0, 0.4 30%, 1);
Use cubic-bezier() for smooth custom timing, steps() for discrete changes, and linear() when multiple timing segments are needed.
Accessibility
Motion effects can make interfaces easier to understand, but excessive or unexpected movement may be uncomfortable for some users.
Use prefers-reduced-motion to respect the user's operating system motion preference.
For example:
.card {
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
.card {
transition-duration: 0.01ms;
}
}
This reduces the duration of the transition for users who prefer less motion.
Avoid unnecessarily large movement, rapid repeated animations, and excessive overshoot effects.
Important information and controls should remain available regardless of whether an animation runs.
Common Uses
The cubic-bezier() function is useful wherever CSS animations or transitions need customized timing.
| Use | Description |
|---|---|
| Button interactions | Control hover and focus transitions. |
| Menus and navigation | Customize opening and closing movement. |
| Dialogs and panels | Create smooth entrance and exit effects. |
| Cards and images | Control scaling, translation, and opacity transitions. |
| Keyframe animations | Customize the progression between animation states. |
| Overshoot effects | Allow animated values to briefly move beyond their destinations. |
For example:
.menu-panel {
opacity: 0;
transform: translateY(-15px);
transition: opacity 0.3s ease-out, transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.menu-panel.is-open {
opacity: 1;
transform: translateY(0);
}
The panel fades and moves into place using separate timing functions.
Limitations
The cubic-bezier() function has several important rules and limitations.
- It requires exactly four numeric parameters.
- The first and third parameters must be between 0 and 1, inclusive.
- The second and fourth parameters can extend outside the 0 to 1 range.
- The function changes timing behavior but does not change animation duration.
- It does not create an animation or transition by itself.
- It cannot directly control properties that do not support interpolation.
- Some properties restrict or clamp values produced by overshooting curves.
- One cubic Bézier curve cannot reproduce every possible multi-stage or oscillating motion pattern.
For example, this is valid:
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
The following is invalid because it has only two parameters:
transition-timing-function: cubic-bezier(0.25, 0.75);
The following is invalid because the first horizontal coordinate exceeds 1:
transition-timing-function: cubic-bezier(1.5, 0, 0.5, 1);
Invalid easing functions cause the containing CSS declaration to be ignored.
Browser Support
The CSS cubic-bezier() function is Baseline Widely available. It has been supported across major browser engines since July 2015.
It works in current versions of Chrome, Edge, Firefox, Safari, and other modern browsers.
For a fallback, provide a standard easing keyword before the custom function:
.box {
transition: transform 0.4s ease;
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
Browsers that do not recognize the custom function can retain the earlier transition declaration.
For current compatibility information, visit Can I Use? .
Specifications
The cubic-bezier() function is defined in the CSS Easing Functions Level 1 .
The specification defines cubic Bézier control points, valid coordinate ranges, and how easing functions map input progress to output progress.
Related CSS features include linear(), steps(), transition-timing-function, animation-timing-function, and @keyframes.
