20 CSS Animated Buttons09 / 20

Pure CSSMIT licensed

CSS Jelly Bounce Button Animation

Playful elastic micro-interaction buttons that wobble with a jelly-like spring on hover and snap back with an overshoot settle using CSS scale and skew keyframes.

Published

Live Demo
Try it

The code

<div class="ab-09">
  <p class="ab-09__label">Hover for jelly effect</p>
  <div class="ab-09__row">
    <button class="ab-09__btn ab-09__btn--pink">Subscribe!</button>
    <button class="ab-09__btn ab-09__btn--lime">Let's Go!</button>
    <button class="ab-09__btn ab-09__btn--sky">Join Now!</button>
  </div>
</div>
.ab-09,
.ab-09 *,
.ab-09 *::before,
.ab-09 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ab-09 ::selection {
  background: #ec4899;
  color: #fff;
}

.ab-09 {
  font-family: system-ui,sans-serif;
  background: #fdf2ff;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2rem;
  padding: 2rem;
}

.ab-09__label {
  font-size: .78rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #a855f7;
  opacity: .6;
}

.ab-09__row {
  display: flex;
  gap: 1.25rem;
  flex-wrap: wrap;
  justify-content: center;
}

.ab-09__btn {
  --c: #ec4899;
  padding: .85rem 2.2rem;
  font-size: 1.05rem;
  font-weight: 800;
  color: #fff;
  background: var(--c);
  border: none;
  border-radius: 50px;
  cursor: pointer;
  outline: none;
  letter-spacing: .03em;
  transform-origin: center;
  box-shadow: 0 4px 16px rgba(0,0,0,.15);
  will-change: transform;
}

.ab-09__btn--pink {
  --c: #ec4899;
}

.ab-09__btn--lime {
  --c: #16a34a;
}

.ab-09__btn--sky {
  --c: #0284c7;
}

.ab-09__btn:hover {
  animation: ab-09-jelly .7s cubic-bezier(.4,0,.2,1) both;
}

@keyframes ab-09-jelly {
  0% {
    transform: scale(1);
  }

  10% {
    transform: scaleX(1.2) scaleY(.85);
  }

  25% {
    transform: scaleX(.85) scaleY(1.15);
  }

  40% {
    transform: scaleX(1.1) scaleY(.92);
  }

  55% {
    transform: scaleX(.95) scaleY(1.05);
  }

  70% {
    transform: scaleX(1.03) scaleY(.98);
  }

  85% {
    transform: scaleX(.99) scaleY(1.01);
  }

  100% {
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ab-09__btn:hover {
    animation: none;
    transform: scale(1.04);
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Animated Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Jelly Bounce Button Animation
Source: https://codefronts.com/motion/css-animated-buttons/css-jelly-bounce-button-animation/

Playful elastic micro-interaction buttons that wobble with a jelly-like spring on hover and snap back with an overshoot settle using CSS scale and skew keyframes.
## HTML
```html
<div class="ab-09">
  <p class="ab-09__label">Hover for jelly effect</p>
  <div class="ab-09__row">
    <button class="ab-09__btn ab-09__btn--pink">Subscribe!</button>
    <button class="ab-09__btn ab-09__btn--lime">Let's Go!</button>
    <button class="ab-09__btn ab-09__btn--sky">Join Now!</button>
  </div>
</div>
```
## CSS
```css
.ab-09,
.ab-09 *,
.ab-09 *::before,
.ab-09 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ab-09 ::selection {
  background: #ec4899;
  color: #fff;
}

.ab-09 {
  font-family: system-ui,sans-serif;
  background: #fdf2ff;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2rem;
  padding: 2rem;
}

.ab-09__label {
  font-size: .78rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #a855f7;
  opacity: .6;
}

.ab-09__row {
  display: flex;
  gap: 1.25rem;
  flex-wrap: wrap;
  justify-content: center;
}

.ab-09__btn {
  --c: #ec4899;
  padding: .85rem 2.2rem;
  font-size: 1.05rem;
  font-weight: 800;
  color: #fff;
  background: var(--c);
  border: none;
  border-radius: 50px;
  cursor: pointer;
  outline: none;
  letter-spacing: .03em;
  transform-origin: center;
  box-shadow: 0 4px 16px rgba(0,0,0,.15);
  will-change: transform;
}

.ab-09__btn--pink {
  --c: #ec4899;
}

.ab-09__btn--lime {
  --c: #16a34a;
}

.ab-09__btn--sky {
  --c: #0284c7;
}

.ab-09__btn:hover {
  animation: ab-09-jelly .7s cubic-bezier(.4,0,.2,1) both;
}

@keyframes ab-09-jelly {
  0% {
    transform: scale(1);
  }

  10% {
    transform: scaleX(1.2) scaleY(.85);
  }

  25% {
    transform: scaleX(.85) scaleY(1.15);
  }

  40% {
    transform: scaleX(1.1) scaleY(.92);
  }

  55% {
    transform: scaleX(.95) scaleY(1.05);
  }

  70% {
    transform: scaleX(1.03) scaleY(.98);
  }

  85% {
    transform: scaleX(.99) scaleY(1.01);
  }

  100% {
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ab-09__btn:hover {
    animation: none;
    transform: scale(1.04);
  }
}
```

How this works

The jelly effect chains scaleX and scaleY transforms through a multi-step keyframe (ab-09-jelly): the button first squashes horizontally (scaleX(1.2) scaleY(.9)), then stretches vertically (scaleX(.85) scaleY(1.1)), and finally oscillates back through diminishing overshoot values before settling at scale(1). This mimics the physics of a jelly object being compressed and released. All transforms operate from the button's natural centre (transform-origin: center), so the effect stays spatially stable.

The animation is triggered by the :hover state, and because it ends at the identity transform, the button naturally returns to normal when the cursor leaves mid-animation. animation-fill-mode: both ensures the first frame is applied immediately on hover without a single frame at the resting state. Combining with a slight filter: brightness pulse makes the jelly feel more alive, as if it's reacting to light during deformation.

Make it yours

  • Intensify the jelly effect by increasing the extreme scale values (e.g. scaleX(1.35) for squash and scaleY(1.2) for stretch).
  • Add a colour flash during the peak squash by transitioning filter: hue-rotate(30deg) at the 20% keyframe step.
  • Create an idle bounce by adding a secondary animation: ab-09-idle 2s ease-in-out infinite with gentle scaleY(1.02) pulses between hover interactions.
  • Delay the jelly on staggered rows by giving each button an increasing animation-delay — e.g. 0s, .1s, .2s — for a wave effect across a button group.
  • Reduce the settle wobble for a tighter feel by cutting the oscillation keyframe count from 7 steps to 5, eliminating the final two minor overshoot steps.

Gotchas — read before shipping

  • The scaleX/scaleY combination can cause text to appear slightly blurry during the squash step on low-DPI screens — this is a sub-pixel rendering artefact and is usually imperceptible at normal viewing distances.
  • If the button is inside a card with overflow: hidden, the scale-up phase may be clipped — ensure the container has enough padding or use a wrapper with overflow: visible.
  • On Safari, combined scale transforms occasionally trigger a repaint of sibling elements — isolate the button in its own stacking context with isolation: isolate if you see neighbouring content flashing.

Browser support

ChromeSafariFirefoxEdge
36+9+16+36+

CSS transform animations are universally supported; the multi-step keyframe works in all modern engines.

Techniques used in this demo

Search CodeFronts

Loading…