Tools · CSS · Cubic-Bezier-Easing-Generator · Anleitungen · An Overshoot Menu Animation With ease-out-back, and Its Two Bugs

An Overshoot Menu Animation With ease-out-back, and Its Two Bugs

3 Min. Lesezeit

A dropdown menu that slides down, goes a little past its resting place, and settles back feels lively in a way a plain ease-out does not. The curve for that is a "back" curve, and Cubic-bezier easing generator has one as a preset: ease out back, cubic-bezier(0.34, 1.56, 0.64, 1). Dropping it into a transition takes one line. Making it behave properly takes three more decisions, because an overshoot curve causes problems an ordinary one never does. Here is the whole job.

An ease-out-back curve rises past full progress, then settles back to it A chart with time across and progress up. A dashed line marks full progress at 1. The curve starts at 0,0, climbs steeply, rises above the dashed line, then comes back down to finish exactly at 1. The part above the line is labelled overshoot. 1 0 overshoot time transform: moves past, then settles opacity: capped at 1, overshoot not visible
The part of the curve above 1 is the overshoot. It shows on a property that can go past its target, and disappears on one that cannot.

The first version

The menu is hidden by moving it up a little and fading it out, and shown by adding a class:

.menu {
  opacity: 0;
  transform: translateY(-8px);
  transition: transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1),
              opacity 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.menu.is-open {
  opacity: 1;
  transform: translateY(0);
}

Opening looks right: the menu drops slightly past its place and settles. Closing does not.

Bug 1: it overshoots on the way closed too

A transition uses the timing function of the style it is moving to. When the class is removed, the destination is the plain .menu rule, and its curve is the same ease-out-back, played forward from open to closed. It is not reversed. So on close, the menu shoots up past its hidden position and springs back, which looks like a glitch rather than a flourish.

The fix is to give each direction its own curve. Put the overshoot on the open state, and a calm ease-in on the base rule, so closing simply accelerates away:

.menu {
  transition: transform 200ms ease-in, opacity 200ms ease-in;
}
.menu.is-open {
  transition: transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1),
              opacity 300ms ease-out;
}

Closing is also shorter than opening here. Nobody waits on a menu that is going away.

Bug 2: the overshoot does nothing on opacity

Notice the opacity transition no longer uses the back curve. Opacity cannot go above 1, so the part of the curve past full progress is clipped, and opacity just reaches full a little early and sits there. The overshoot only shows on a property that can actually go past its target, like transform. Using a back curve on opacity is not harmful, it is just pointless. Give opacity an ordinary curve and let the transform carry the motion.

Bug 3: the overshoot is cut off

If the menu sits inside a container with overflow: hidden, the extra few pixels of travel can go outside the container's edge and be cut off at the peak of the overshoot. It shows up as a flicker of a clipped edge. Either let the menu render outside the clipped container, or give it a little room to travel.

Tune it before you ship it

Paste the preset into the generator, set the duration to 300 ms, and press Replay animation to watch it beside ease. If the bounce feels too strong, drag the first handle lower: the plain-words line stops saying the curve goes past the end once the handle no longer pulls it above 1. And wrap the motion for people who have asked for less of it:

@media (prefers-reduced-motion: reduce) {
  .menu, .menu.is-open { transition: none; }
}

For why a curve changes how fast motion feels, see the background. For dragging and comparing curves step by step, see the how-to guide. Or open the ease out back preset.

Cubic-Bezier-Easing-Generator kostenlos ausprobieren Cubic-Bezier-Easing-Generator