A CSS cubic-bezier() value with x1 or x2 outside the closed 0–1 range is invalid and causes the browser to silently fall back to the keyword ease, which is why a curve that "looks right" in a viewer can render as plain linear motion in production. Several other failure modes appear during generation: y1 and y2 values that leave the 0–1 range can pull animated content outside its container, a midpoint reading in the curve viewer often reports the wrong y value because the elapsed-time axis is not the same as the curve parameter, and a copied declaration still needs the right property, distance, duration, and reduced-motion fallback to behave correctly. The CSS Cubic Bezier Generator builds every one of these constraints into the interface, constraining x to the valid CSS range and validating each entry before output, but understanding what goes wrong behind the numbers is what keeps you from re-debugging the same curve tomorrow.

What invalid CSS actually does in the browser

A cubic-bezier() declaration takes exactly four numbers: x1, y1, x2, y2. The first and third describe input-time positions along the horizontal axis, so the W3C CSS Easing Functions Level 1 specification requires both to sit in the closed 0–1 range. When either x slips outside that range, the entire declaration becomes invalid at parse time. Browsers do not throw a warning, do not highlight the offending property in DevTools, and do not log anything to the console; they substitute the keyword ease and continue. That is the single most common silent failure when you generate a CSS cubic bezier: you spend ten minutes dialing in a curve, paste it into your stylesheet, and watch the element glide in with the same flat acceleration as every other ease on the page.

You can confirm this by inspecting the computed timing-function on the element. Chromium, Firefox, and Safari all report ease in the Computed panel when the custom declaration was rejected. The takeaway is that invalid cubic-bezier values do not produce broken CSS or a parser error; they produce a working stylesheet that simply does not contain the curve you wrote. The CSS Cubic Bezier Generator constrains x1 and x2 to the valid CSS range at the input level, so a curve that looks coherent in the preview is also a curve the browser will accept.

When y values escape the chart and what that does to motion

The vertical coordinates y1 and y2 describe output progress rather than input time, and CSS permits them to step outside the 0–1 range. Negative y produces anticipation, where the animated value moves slightly backward before continuing toward its destination, and y values greater than one create overshoot, where the value passes the end position before settling. Both effects are commonly used for spring-like buttons, toast notifications, and bounce accents, and the W3C CSS Easing Functions Level 1 specification explicitly permits them.

What goes wrong is when the overshoot exceeds what the surrounding layout can absorb. A button that translates 24 pixels with a y2 of 1.8 may end up 4 pixels past its target and slide back, which looks intentional. A button that scales from 1 to 1 with a y2 of 1.8 will visibly grow to 1.8x before shrinking, which often overlaps adjacent elements or escapes a clipping parent that was expected to contain the hit area. The curve preview in a chart that clips the y axis to 0–1 hides this problem: the line appears to leave the visible square, but you cannot tell from the chart alone how far the actual computed value traveled. The sample table and the moving preview in the CSS Cubic Bezier Generator reflect the true computed easing rather than a clipped graph, so the only way to know whether an overshoot is reasonable is to read the y outputs at t = 0.25, 0.5, 0.75, and 1 and watch the preview motion at the real distance.

Why the midpoint sample is not the curve value at t = 0.5

Another quiet failure mode appears in the sample table that most generators show next to the curve. A naive implementation reads y at the curve parameter t = 0.5 and reports it as the "50 percent progress." That reading is wrong whenever the x control coordinates are not symmetric around 0.5, which is the case for every non-linear easing keyword and for most custom curves. CSS easing uses elapsed input progress on the horizontal axis, so the value the browser actually applies at the halfway time mark is the y coordinate of whatever curve parameter produces x = 0.5, not whatever curve parameter happens to equal 0.5.

The CSS Cubic Bezier Generator solves this correctly. For each requested input at 0, 0.25, 0.5, 0.75, and 1, the engine finds the curve parameter whose x coordinate matches the requested input and then evaluates y at that parameter. The implementation uses bounded Newton iterations first and falls back to bisection when Newton would step outside the valid range. The practical effect: when you read 0.500 in the sample column, you are looking at the value your animation will report at the literal halfway point of its duration. If you switch to a custom curve and the sample at 50 percent stays close to 0.5, the easing is roughly linear through the middle. If it jumps to 0.9, the animation is already nearly finished by the time the duration is half over, which is a useful tell for over-eager ease-out designs.

Generate a cubic-bezier that survives validation

To produce a CSS cubic-bezier() declaration that does not silently fall back to ease or misbehave at runtime, follow these steps with the CSS Cubic Bezier Generator:

  1. Start with a standard easing preset if you do not already have four numbers in mind. Clicking ease, ease-in, ease-out, ease-in-out, or linear loads the exact control points defined by the CSS Easing Functions Level 1 specification, so you begin from a curve that is known to be valid.
  2. Adjust x1, y1, x2, and y2 one at a time. The x inputs are constrained to the closed 0–1 range, so any value you can enter is already syntactically valid. y inputs accept finite values from minus ten through ten, which covers every reasonable anticipation or overshoot without exposing the parser to NaN or infinity.
  3. Read the graph and the sample table together. The graph shows the parametric shape from the fixed start at (0,0) to the fixed end at (1,1); the table shows the actual y values at the time marks 0, 0.25, 0.5, 0.75, and 1. If the two disagree about what 50 percent looks like, trust the table.
  4. Use Run preview to play the curve on a 900 ms transform transition between two horizontal positions. Watch the motion at your intended distance and on your intended property before committing.
  5. Copy the declaration with the Copy CSS button. The output is the complete transition-timing-function declaration, including the semicolon, rounded to three decimals. If clipboard access is denied, the declaration remains visible and selectable so you can still paste it manually without losing work.

If the curve still misbehaves after you paste it, run through the cubic-bezier troubleshooting checklist for layered causes such as a conflicting shorthand transition or an inherited timing function.

Five standard keywords and the curves they actually load

Below is what each built-in CSS easing keyword resolves to in cubic-bezier() form. The values come from the keyword mappings in the W3C CSS Easing Functions Level 1 specification and are the same numbers the CSS Cubic Bezier Generator loads when you press each keyword button.

Keywordx1y1x2y2
linear0011
ease0.250.10.251
ease-in0.42011
ease-out000.581
ease-in-out0.4200.581

Two things go wrong if you skip this table. First, you may assume the keyword itself is the safest output, but every keyword here is a shorthand for four numbers, and the generator deliberately outputs all four numbers rather than the keyword so the declaration can be recorded, compared, and modified without hidden defaults. Second, you may assume ease and ease-in-out behave similarly because both contain the word "ease." In practice, the table shows that ease has its first control point pulled forward to (0.25, 0.1) and its second held at (0.25, 1), while ease-in-out is symmetric and never accelerates that aggressively at the start. The numeric outputs make the difference obvious; the keywords alone hide it. For additional reference, MDN documents the same cubic-bezier() function with browser-oriented examples and cross-browser history.

After you copy: motion behaviors that still go wrong

A valid transition-timing-function declaration is only one ingredient in a working animation. Several runtime behaviors can still feel wrong once the curve is correct.

The first is property mismatch. The Run preview in the CSS Cubic Bezier Generator demonstrates the curve on a transform transition over 900 ms. The same curve applied to width, height, or top will not feel identical because each property has a different rendering cost and a different relationship between pixels and perceived motion. If the curve feels too aggressive in production, the cause is usually the property, not the easing; the same numbers behave differently on layout-triggering properties than on transform or opacity.

The second is overshoot against reduced-motion preferences. CSS permits curves that are technically valid but unpleasant for users with vestibular sensitivity. A y2 of 2.0 produces a fast zoom past the end position; if the animation is decorative rather than essential, respect the prefers-reduced-motion media query and either remove the transition or swap in a flatter curve for affected users. MDN calls this out as a usability concern, not just a polish step.

The third is focus and pointer behavior. While an element is transitioning, its bounding box may briefly extend beyond its resting size due to overshoot. If the element is interactive, the overshoot can reveal or obscure adjacent hit targets, change the visible hover region, or pull the element outside a clipping parent that was expected to contain it. Always run the curve against the real component, not against an isolated test box.

The fourth is the shorthand trap. transition: all 300ms cubic-bezier(...) looks tempting, but transition: all also animates properties you did not intend, including some that produce visible reflow. Specifying the property explicitly avoids that surprise and makes the curve's effect on a single attribute predictable.

The fifth is silent clipping in the chart. A custom curve with y2 well above 1 will leave the visible square in the generator's plot area. The chart keeps a stable 0–1 frame so ordinary timing curves are easy to compare, but the curve itself is computed at full precision regardless of where the line sits on screen. If overshoot matters to your design, inspect the moving preview and the numeric samples; they reflect the actual computed easing, not a clamped display.

Most cubic-bezier failures are quiet. Invalid x values fade to ease without a warning, y overshoots hide inside a clipped chart, and a midpoint reading can quietly report the wrong y for the requested time. Building the validation into the input layer, by constraining x to [0,1], accepting bounded y values, solving for x before reading y, and serializing the result as a complete four-number declaration, removes the silent-fallback class of failure entirely. Once the timing function is verified, the remaining work is choosing a property, distance, and duration that match the curve and respecting reduced-motion preferences where animation is not essential.

For a deeper look, see How to Get Started With the CSS Triangle Generator.