In CSS, what do the four numbers in `cubic-bezier(0.68, -0.55, 0.27, 1.55)` represent, and why can a transition using it overshoot its final value?
answer
- two control points, endpoints fixed
- one axis is time, the other progress
- x is constrained, y is not
- clamped properties cannot bounce
- a separate function makes discrete jumps
basics
~20 sThey are the x and y coordinates of the two control points of a cubic Bezier curve whose endpoints are fixed at (0,0) and (1,1), mapping elapsed time to progress. The x values must stay within 0 to 1, but y may go below 0 or above 1, which produces anticipation and overshoot.
solid answer
~50 s`cubic-bezier(x1, y1, x2, y2)` defines the two control points P1 and P2 of a curve that always starts at (0,0) and ends at (1,1). The horizontal axis is the fraction of the duration elapsed and the vertical axis is the fraction of the value change applied, so the curve's shape is the easing. The x coordinates are clamped to the range 0 to 1 — a value outside it makes the function invalid, because time must move forward monotonically — but the y coordinates are unrestricted. A negative y1 means the value first moves backwards (anticipation) and a y2 above 1 means it shoots past the target and settles back, which is how bouncy easings are written. The named keywords are just aliases: `ease` is `cubic-bezier(0.25, 0.1, 0.25, 1)` and `linear` is `cubic-bezier(0, 0, 1, 1)`. Overshoot is only visible where the property accepts out-of-range values; `opacity` clamps to 0–1, so it cannot bounce.
code
css · 14 lines.back-ease {
/* y1 below 0 = anticipation, y2 above 1 = overshoot */
transition: translate 400ms cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
.typewriter {
/* eight discrete jumps instead of a smooth curve */
transition: background-position 800ms steps(8, jump-end);
}
.spring-ish {
/* piecewise-linear easing for shapes a single bezier cannot express */
transition: translate 600ms linear(0, 0.6 30%, 1.05 55%, 0.98 70%, 1);
}go deeper
Know the named keywords — linear, ease, ease-in, ease-out, ease-in-out — and that ease is the default when you omit a timing function.
Explain the curve as a time-to-progress mapping with fixed endpoints, say which coordinates are constrained, and connect a y value outside 0–1 to anticipation and overshoot.
Show taste as well as mechanics: ease-out for entering elements, restraint with overshoot on large surfaces, and awareness that clamped properties cannot express a bounce.
Own the motion vocabulary — a short named set of curves and durations shared across the product, so easing is a design decision made once rather than a number invented per component.
## What a timing function is A timing function is a pure mapping from *input progress* (how much of the duration has elapsed, 0 to 1) to *output progress* (how much of the value change has been applied). The browser samples it each frame and uses the result to interpolate between the start and end values. Nothing about the property is involved — the same curve drives a colour, a length, or a transform. ## Reading `cubic-bezier()` ```css .card { transition: transform 400ms cubic-bezier(0.68, -0.55, 0.27, 1.55); } ``` The curve is a cubic Bezier with four control points. The first and last are fixed by the specification at P0 = (0,0) and P3 = (1,1) — at time zero no progress has been made, at the end all of it has. The four numbers you write are P1 = (x1, y1) and P2 = (x2, y2), the two intermediate handles that bend the curve. - **x is time.** x1 and x2 must lie in the closed range 0 to 1. A value outside it makes the whole declaration invalid and it is dropped, because a curve that doubles back horizontally would mean time running backwards. - **y is progress.** y1 and y2 have no restriction. Values between 0 and 1 keep the motion inside its endpoints; values outside push it past them. In the example, y1 = -0.55 pulls the early part of the curve below zero, so the element first eases slightly *away* from its destination — the anticipation you see in cartoon motion — and y2 = 1.55 pushes the late part above one, so it flies past the target and settles back. That single declaration is the whole "back ease" effect. ## The keywords are aliases Every named function has an exact cubic-bezier equivalent: ```text linear = cubic-bezier(0, 0, 1, 1) ease = cubic-bezier(0.25, 0.1, 0.25, 1) ease-in = cubic-bezier(0.42, 0, 1, 1) ease-out = cubic-bezier(0, 0, 0.58, 1) ease-in-out = cubic-bezier(0.42, 0, 0.58, 1) ``` Note that `ease` — the initial value of `transition-timing-function` — is not symmetric: it accelerates fast and decelerates gently. For UI that enters the screen, `ease-out` usually reads better, because motion arriving at rest is what the eye expects; for something leaving, `ease-in`. ## Where overshoot is visible and where it is not Overshoot means the interpolated value temporarily leaves the range between start and end. Whether you see it depends on the property: - `transform` translation, `margin`, `top` and similar accept values beyond the endpoints, so the element visibly passes the target and returns. - `opacity` is clamped to the 0–1 range when used, so an overshooting curve produces no visible bounce at the ends — it just saturates. - Colour channels are similarly clamped to the valid gamut. So "can I get a bouncy fade?" is a fair follow-up, and the answer is no: pick a property whose range is open. ## `steps()` — the discontinuous family ```css .sprite { transition: background-position 1s steps(8, jump-end); } ``` `steps(n, position)` divides progress into `n` equal jumps instead of a smooth curve, which is how sprite-sheet and typewriter effects are built. The position keyword decides where the jumps land: `jump-start` jumps at the beginning of each interval, `jump-end` (the default) at the end, `jump-none` uses `n` values with no jump at either end, and `jump-both` jumps at both. The older keywords `start` and `end` are aliases for `jump-start` and `jump-end`, and `step-start` / `step-end` are shorthands for `steps(1, jump-start)` and `steps(1, jump-end)`. ## `linear()` for arbitrary curves A cubic Bezier cannot express a genuine multi-bounce or spring, because it is a single curve segment. The `linear()` function takes a list of progress stops — `linear(0, 0.5 25%, 0.9 50%, 1)` — and joins them with straight segments, so a spring simulation can be sampled into it. It is widely supported in current browsers and is the honest answer to "how do I do a real bounce in pure CSS". ## What interviewers are checking They want to hear that the curve is time-to-progress, not a path through space; that x is constrained and y is not; that the keywords are aliases rather than special engine behaviour; and that you would reach for `ease-out` over the default `ease` for entering UI. Being able to say "the negative y1 is anticipation" without hand-waving is the mark of someone who has actually tuned motion.
- Why does an overshooting easing produce no visible bounce when applied to `opacity`?Because `opacity` is clamped to the range 0 to 1. The timing function still computes progress above 1, but the resulting value saturates at fully opaque, so the excess is invisible. Properties with open ranges — translation, margins, offsets — show the overshoot. If you want a bouncy fade, animate position or scale alongside the fade.
- What makes `cubic-bezier(1.2, 0, 0.5, 1)` invalid?The first x coordinate is 1.2, outside the permitted 0 to 1 range. Time must advance monotonically across the duration, so the specification constrains the control points' x values; y values are free precisely because progress may leave its range. An invalid timing function makes the declaration invalid, and the property falls back to whatever value was in effect.
- When would you choose `steps()` over a smooth curve?When the change is meant to read as discrete rather than continuous: advancing a sprite sheet's `background-position` frame by frame, a typewriter reveal, or a segmented progress indicator that should tick rather than glide. Anything where an intermediate half-state would look like a rendering glitch rather than motion.
saying these in an interview costs you the question
- Thinks the four numbers are a path the element travels
- Believes y values must stay between 0 and 1
- Says `ease` is symmetrical or the same as `ease-in-out`
- Expects an overshooting curve to bounce an opacity fade
- Treats the keyword easings as engine features rather than aliases