Skip to content
Motion & micro-interactionsLesson 4 of 8Beginner8 min

Jelly button: squash & stretch on press

Make a control feel physical: it squashes when pressed and springs back with a wobble.

Kept on this device only.

In one line: a button that briefly squashes when you press it and springs back with a little overshoot reads as physical, responsive, and alive — at the cost of about twenty lines of code.

What it is

"Squash and stretch" is a borrowed animation principle: an object compresses along one axis and expands along the other when force hits it, then settles. On a button, the "force" is your press. The control scales down and slightly wider on press, then springs back past its resting size before settling — a tiny wobble that signals I felt that.

Why it matters

A flat state change ("pressed → not pressed") tells the eye nothing about cause. A spring tells a cause-and-effect story: you pushed, it gave, it recovered. That micro-story is what makes an interface feel responsive rather than merely functional, and it costs almost nothing. The trick is restraint — the wobble should be felt more than seen. Overdo the overshoot and the control feels gelatinous and slow; underdo it and you've just added latency.

See it

Live demo

Hold to squash, release to feel the spring settle.

Tweak it3
0.06
600
12
Press and hold, then release. Try it with a keyboard too (Tab, then Space).

How it works

Three moves do all the work:

  1. On press, scale the element down and wide: scaleX up a touch, scaleY down. That's the squash.
  2. On release, animate back to scale: 1 with a spring, not a fixed-duration ease. A spring naturally overshoots and oscillates, which is the wobble — you don't keyframe it by hand.
  3. Tune the spring so it settles in roughly 300–450 ms. Higher stiffness = snappier; lower damping = more wobble.
Spring · stiffness 600, damping 12, mass 0.6
The same spring used below: a quick overshoot, then settle.

The settle time and overshoot you see above are the same numbers used in the code below. Reach for a spring (not ease-out) whenever the motion should feel like a physical recovery rather than a scripted move.

Build it

Vanilla CSS + a sprinkle of JS
.jelly-btn {
  /* resting state */
  transform: scale(1);
  transition: transform 120ms ease;
  transform-origin: center;
}
 
/* held down: squash (wider + shorter) */
.jelly-btn:active {
  transform: scale(1.06, 0.92);
  transition-duration: 80ms;
}
 
/* released: play the springy recovery */
.jelly-btn.is-released {
  animation: jelly-settle 420ms cubic-bezier(0.22, 1, 0.36, 1);
}
 
@keyframes jelly-settle {
  0%   { transform: scale(1.06, 0.92); }
  40%  { transform: scale(0.98, 1.03); } /* overshoot the other way */
  70%  { transform: scale(1.01, 0.99); }
  100% { transform: scale(1, 1); }
}
 
/* respect users who don't want motion */
@media (prefers-reduced-motion: reduce) {
  .jelly-btn,
  .jelly-btn:active,
  .jelly-btn.is-released {
    transition: none;
    animation: none;
    transform: none;
  }
}

Make it yours

Use the controls beside the demo above to change squash amount, spring stiffness, and damping — each change updates the example live.

Experiment in the playground

Reproduce it with an LLM

Reproduce it with an LLM

You are a senior front-end engineer. Build a reusable button component in React using the Motion library that has a 'jelly' squash-and-stretch micro-interaction: on press it scales slightly wider and shorter, and on release it springs back to its resting size with a small overshoot/wobble that settles in about 400 ms. Requirements: it must be a real <button> with keyboard activation and a visible focus-visible ring; it must fully disable the animation when prefers-reduced-motion: reduce is set; animate only transforms (no layout properties); no external CSS framework. Return only the component code with imports — no explanation.

Pitfalls & accessibility

Further reading

  • The spring-vs-easing distinction, in your animation library's official motion docs.
  • The original twelve principles of animation, for where "squash & stretch" comes from.