A CSS cubic-bezier() declaration looks wrong in the browser for one of three reasons: x1 or x2 slipped outside the 0–1 range and the browser silently fell back to the default `ease`, the y control points produced anticipation or overshoot you didn't notice in the preview, or the curve is fine but the surrounding component changed duration, property, or distance. CSS defines a cubic-bezier value as invalid when either x control coordinate lies outside the closed zero-to-one range, and per the W3C CSS Easing Functions Level 1 specification the engine treats the whole declaration as if it were never written. That is the single most common cause of "my custom easing didn't apply" complaints. The second most common cause is treating the y axis as a progress clamp — it is not, and a y value above one or below zero is allowed and can produce legal but unexpected motion. The third cause is testing the curve in the wrong context: a 900 ms preview across a short distance will never feel the same as a 250 ms button hover across a long property change. The fix starts by isolating which of the three buckets your problem sits in.

Why the Generated Cubic Bezier Looks Wrong in the Browser
When a CSS cubic-bezier curve "doesn't work", CSS does not throw an error — it discards the timing-function and falls back to `ease`. That silent failure is the most frequent reason a freshly generated result looks wrong, and it almost always traces back to one of the four control numbers, not the rest of the transition declaration. If the browser reports `ease` as the active timing-function in DevTools' Computed pane, your curve was rejected and the generator output is not what the page is using.
A separate class of "wrong-looking" results happens when the curve applies, but the y values produced motion you didn't expect. A y1 above 1.0 launches the value past its target before settling back; a y2 below 0 pulls it backwards before continuing forward. These are legal CSS values per the specification, but they read as glitches in interfaces that are supposed to feel predictable.
A third class is the deceptive match: the curve is correct, but the surrounding context changed. A longer duration flattens the perceived acceleration; a shorter duration exaggerates it. Distance matters because cubic-bezier shapes time, not distance — a 50-pixel translate and a 500-pixel translate produce very different perceived velocities from the same curve.
Check the X Coordinates Before Anything Else
CSS requires x1 and x2 to remain inside the closed 0–1 interval. The CSS Cubic Bezier Generator clamps those inputs to that range, but the value you paste into your stylesheet has no such protection — a stray keystroke, a misread decimal, or a copy-paste error can push one x outside the range and the declaration stops working entirely. Before changing the y values, confirm x1 and x2 are between 0.000 and 1.000 inclusive.
A quick diagnostic is to read the sample table at input 0.0 and input 1.0. Both must read 0.000 and 1.000 respectively; if they don't, the solver was given a degenerate curve and the whole declaration is suspect. The five numeric progress samples are the fastest way to verify the curve is what you think it is, far faster than visually scanning the graph.
X Coordinate Reference
| Input progress | Required output | Why it matters |
|---|---|---|
| 0.00 | 0.000 | Curve must start at origin; otherwise the animation begins mid-motion. |
| 0.25 | varies | First sample of curve shape; checks the early ease. |
| 0.50 | varies | Halfway in input time, not halfway along the curve. |
| 0.75 | varies | Second-half shape; checks the late ease. |
| 1.00 | 1.000 | Curve must end at (1, 1); otherwise the animation ends before the value reaches the target. |
Standard Keyword Coordinates
| Keyword | x1 | y1 | x2 | y2 |
|---|---|---|---|---|
| linear | 0.000 | 0.000 | 1.000 | 1.000 |
| ease | 0.250 | 0.100 | 0.250 | 1.000 |
| ease-in | 0.420 | 0.000 | 1.000 | 1.000 |
| ease-out | 0.000 | 0.000 | 0.580 | 1.000 |
| ease-in-out | 0.420 | 0.000 | 0.580 | 1.000 |
Read the Sample Table, Not the Chart Shape
The sample table is the answer to a question the control-point diagram alone cannot answer: how far has the animated value progressed when a given fraction of time has passed? For each input at 0.0, 0.25, 0.5, 0.75, and 1.0, the generator solves the curve parameter that produces the requested x coordinate and then evaluates y. It uses bounded Newton iterations and falls back to bisection when needed. That avoids the common mistake of treating the curve parameter as elapsed time.
A concrete consequence: if you set y1 to 1.6 expecting a quick launch, the sample at input 0.25 will read well above 0.25, not 0.40 — the curve is steep, but the x axis is still elapsed time. The chart can show you the overshoot, but only the table tells you at what point in time the value actually crossed the target. Treat the table as ground truth and the chart as a visual aid.
Fix the Y Coordinates When Overshoot Is the Problem
The y control values describe output progress, not a clamped percentage. Per the CSS specification, y1 and y2 may legally fall anywhere in the finite range, and the generator accepts values from minus ten through ten. Most overshoot problems are not validation problems; they are taste problems. A y2 of 1.4 means the element arrives at its target, slides 40% past, then settles back. That is technically valid and visually intentional in some interfaces, and a bug in others.
If the overshoot is unintentional, the cleanest fix is to halve the y coordinate that produces the bounce and re-read the sample table. If the sample at input 0.75 is well above 1.0, the element is past its destination three-quarters of the way through the animation; pull that y value back until the sample sits at or near 1.0. If the chart appears to extend beyond its square frame, that is by design — the chart keeps a stable zero-to-one window so ordinary timing curves stay comparable. The samples and the Run preview still use the actual computed easing, so use those to decide, not the visible chart bounds.
How to Repair a Bad Cubic Bezier Step by Step
- Open the CSS Cubic Bezier Generator and select the keyword that most closely matched your original intent — `ease`, `ease-in`, `ease-out`, or `ease-in-out`. The generator exposes the four standardized coordinates so you can audit what the keyword actually is.
- Read the sample table at inputs 0.0 and 1.0 and confirm both read 0.000 and 1.000. If they don't, the curve is degenerate and nothing downstream will behave.
- Adjust x1 first, watching the early samples. CSS requires x1 to stay in [0, 1]; the input field will refuse values outside that range.
- Adjust y1 until the sample at input 0.25 matches the early feel you want. If you overshoot above 1.0 here, the element will appear to launch before it should.
- Adjust x2 and y2 in the same way, this time watching the sample at input 0.75. The last quarter is where most "feels wrong" complaints live.
- Click Run preview to watch the circle travel across its 900 ms distance with the exact generated easing. Repeated clicks toggle forward and reverse motion.
- Copy the complete declaration, including the semicolon, and test it in the real component at the real distance, property, and duration.
Test the Declaration in the Real Component, Not the Preview
The preview demonstrates timing, not production performance under every workload. A real component can feel different when distance, duration, property, rendering cost, input device, or surrounding motion changes. Paste the declaration into the actual stylesheet, on the actual property you are animating, and watch the motion at the actual duration. If the curve felt perfect in the preview but sloppy in production, the curve is probably right and the context is wrong.
- Animate the same property the preview is showing, not a substitute.
- Match the real duration; halving or doubling duration changes perceived acceleration.
- Match the real distance; curves shape time, so a 5 px shift looks different from a 500 px shift.
- Respect reduced-motion preferences where animation is not essential.
- Verify focus and pointer behavior while the element is transitioning; an oscillating curve can move focus rings unexpectedly.
When the Curve Is Correct but the Motion Still Feels Off
A valid curve is not automatically accessible, performant, or appropriate. Large overshoots can make controls appear to reverse, leave their container, or reveal content that was expected to remain clipped. Rapid oscillation between two close y values produces vibration that reads as a bug. And focus rings that follow an overshooting element can land in the wrong place for keyboard users.
If the curve passes every check above and still looks wrong, the issue is almost certainly outside the timing-function: a property that should not be transitioned, a duration that is too short for the distance, or a missing `will-change` declaration that forces a paint on every frame. The generator does not produce keyframes, spring physics, linear() stop lists, steps(), duration, delay, iteration count, or a full transition shorthand — only the timing-function itself. For deeper reading on the same tool, see how to use cubic-bezier CSS for custom easing and avoid mistakes when you generate a CSS cubic bezier.
When everything is aligned, the browser renders the exact easing you designed; per MDN's cubic-bezier reference, the four-number declaration is the most portable way to ship a custom easing curve.