Buttons✨ Premium

Bezier Elastic

On hover the button grows to 1.08 and rises 3 px along a curve that overshoots its target by 9.8% before settling (peak at 1.088 around 230 ms); on click it squashes to 0.95 in 100 ms. Three states, one transition, no JavaScript.

CSSBezierElastic

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Buttons has 27 effects, including 2 free. Explore the category →

3 usage examples

Add to cart — product card — Bezier Elastic example 1

① Add to cart — product card

WhenItem card: visual, name, price, a full-width buy button.
WhyThe rebound on click release physically confirms the add without waiting for the cart; on hover, the lift singles out the hovered card in a grid. The use case was already listed on the previous page.
SettingsGradient #0e7490 → #155e75 (AA), scale(1.05), shadow rgba(14,116,144,.45).
Download the app — mobile app landing page — Bezier Elastic example 2

② Download the app — mobile app landing page

WhenApp landing: phone visual, one-line promise, a download button.
WhyAn isolated, generous button lets the curve express itself: the 10% overshoot is visible at that size and gives the download a “ready to leap” feel. The case was on the previous page.
Settingspadding: 18px 40px, .35s, curve cubic-bezier(.34, 1.8, .64, 1) (17.7% overshoot).
Spin the wheel — reward game card — Bezier Elastic example 3

③ Spin the wheel — reward game card

WhenMini-game card: a wheel, a ticket balance, the button that starts the round.
WhyA playful universe takes exaggeration well: stronger growth, deeper squash, colored shadow — the button becomes an object you press.
Settingsscale(1.12) translateY(-4px), :active at scale(.9), curve cubic-bezier(.34, 2, .64, 1) (25% overshoot).

How it works

Three states and no @keyframes: rest (cyan gradient #06b6d4 → #0891b2, white text), :hover (transform: scale(1.08) translateY(-3px) and box-shadow: 0 15px 35px rgba(6,182,212,.4)) and :active (transform: scale(.95)). The motion is only the transition between them: transition: .4s cubic-bezier(.34, 1.56, .64, 1) — all properties, 400 ms.

The curve is a “back-out”: its second control point has a y of 1.56, above 1. The value reaches the target at 37% of the time (≈ 150 ms), overshoots it by 9.8% of the distance at 57% (≈ 230 ms), then comes back to settle. Measured in Chromium: the scale goes from 1 to a 1.088 peak at 226 ms, then 1.08 at 400 ms; the translation peaks at −3.58 px before −3.24 px. Two details: translateY is written after scale, so it gets multiplied by 1.08 — the button rises 3.24 px, not 3; and the shadow follows the same curve, its offset and blur overshoot too.

On click, :active sets transition: .1s: the shorthand replaces the whole transition — 100 ms, default ease curve, no overshoot. The press is a snap, not a bounce. The rule omits translateY: the button also drops back 3 px. It doesn't touch box-shadow: the hover shadow stays under a button that is nonetheless squashed. Declared after :hover, :active wins for as long as the mouse button is down.

On release, the :active rule goes away and the hovered state's transition plays the way back: 400 elastic ms from 0.95 to 1.08. Since the overshoot is proportional to the distance (0.13 instead of 0.08), it's more visible: measured peak at 1.0927 around 245 ms. The release rebound, more than the hover, is what makes the effect. When the cursor leaves the button, back to 1 along the same curve, with a fleeting dip to about 0.992.

Accessibility

  • Insufficient contrast: white text on #06b6d4 = 2.43:1 and on #0891b2 = 3.68:1 — below the 4.5:1 AA threshold across the whole gradient, and below 3:1 on its light half. Keep the hue and darken: #0e7490 → #155e75 gives 5.4:1 and 7.3:1. Dark text (#083344) wouldn't be enough: 5.5:1 on the light end, 3.6:1 on the dark end. The growth changes nothing about contrast.
  • prefers-reduced-motion not handled: @media (prefers-reduced-motion: reduce) { .btn-bezier-elastic { transition: none } } — the states apply instantly. To remove the motion itself as well, add .btn-bezier-elastic:hover { transform: none } in the same rule: the shadow alone signals the hover.
  • Keyboard: :active applies while Space is held in most browsers — the squash exists on keyboard — but there is no :hover on focus. Add .btn-bezier-elastic:focus-visible { transform: scale(1.08) translateY(-3px); box-shadow: 0 15px 35px rgba(6,182,212,.4) }. The native outline is kept and follows the transform.
  • Footprint: at the peak, a 160 px button is 174 px wide and 55 px tall, and its shadow extends 35 px of blur below. Leave at least 8 px between two buttons, and a z-index on hover if the button overlaps a neighbor. Target ≈ 51 px tall at rest, above the 44 px minimum.
  • Touch: :active fires on touch — the squash is good mobile feedback; on iOS the hovered state sticks after a tap (@media (hover: hover)). Screen readers: nothing generated, accessible name = label of the native <button>.

Browser compatibility

Requires CSS transitions on transform and box-shadow with a cubic-bezier() whose y values leave [0, 1] — allowed by the specification and supported everywhere since 2012. No JavaScript, zero dependencies.

Chrome 26+✓ Full
Firefox 16+✓ Full
Safari 9+✓ Full
Edge 12+✓ Full
Mobile iOS✓ Full (:hover sticks after a tap)
Android Chrome✓ Full

Without transitions, the three states apply instantly: the button grows, squashes and returns at once — usable, without the bounce.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<button class="demo-btn btn-bezier-elastic">Elastic</button>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Bezier Elastic, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
cubic-bezier(.34, 1.56, .64, 1) (CSS — transition) 9.8% overshoot The 2nd parameter sets the bounce: 1.2 ≈ 1%, 1.4 ≈ 5%, 1.56 ≈ 10%, 1.8 ≈ 18%, 2 = 25%. Beyond that the overshoot turns cartoonish and the shadow “jumps”.
.4s (CSS — transition-duration) .4s Total length, bounce included. .25s: snappy, the overshoot is barely felt; .6s: the button floats.
scale(1.08) translateY(-3px) (CSS — :hover) 1.08 / −3 px Growth and lift. Order matters: translateY after scale is multiplied by the scale (3 × 1.08 = 3.24 px). Write translateY(-3px) scale(1.08) for exactly 3 px.
scale(.95) + transition: .1s (CSS — :active) .95 in 100 ms Depth and speed of the press. scale(.92): deeper; .15s: softer. Add box-shadow: 0 4px 12px rgba(6,182,212,.3) so the shadow closes in with the button instead of staying 15 px away.
box-shadow: 0 15px 35px rgba(6,182,212,.4) (CSS — :hover) cyan shadow, 15 px below the button Colored shadow in the gradient's hue — more a glow than a shadow. rgba(0,0,0,.3): neutral drop shadow; 0 8px 20px: closer, subtler.
linear-gradient(135deg, #06b6d4, #0891b2) (CSS — background) light cyan → cyan For AA contrast with white text: #0e7490 → #155e75. Change the shadow color at the same time.
transition-property (CSS — implicit) all Restrict to transform, box-shadow: a color or background change (theme, disabled state) won't bounce along the same curve.

FAQ

Because the curve's overshoot is proportional to the distance traveled — 9.8% in both cases. On hover, the scale goes from 1 to 1.08 (distance 0.08): peak at 1.088. On release, from 0.95 to 1.08 (distance 0.13): peak at 1.093. The same mechanism makes the return to rest almost invisible (dip to 0.992).
Because transition: .1s is a shorthand: rewriting it resets every component to its default, hence transition-timing-function: ease. The press happens in 100 ms with no overshoot — intended, a click should feel crisp. For an elastic press, write transition-duration: .1s alone: the hover curve is preserved.
At the peak it spills 7 px on each side and its shadow carries 35 px of blur downward. Three precautions: at least 8 px of margin around it, z-index: 1 on :hover (the button is already position: relative) so it rises above following elements, and a container without overflow: hidden, which would clip the overshoot and the shadow.