webdevpuneet.com

Jelly Icon Dock Hover — Spring Physics Icon Snippet

Jelly Icon Dock Hover · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

<div class="jd-stage">
  <p class="jd-hint">Hover each icon — it squashes down then stretches up with real spring physics, like soft rubber</p>
  <div class="jd-dock" id="jdDock">
    <button class="jd-icon" style="background:#6366f1">🏠</button>
    <button class="jd-icon" style="background:#22c55e">💬</button>
    <button class="jd-icon" style="background:#f59e0b">🎵</button>
    <button class="jd-icon" style="background:#ec4899">📷</button>
    <button class="jd-icon" style="background:#0ea5e9">📁</button>
    <button class="jd-icon" style="background:#8b5cf6">⚙️</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three-stage compress-rebound-settle sequence per icon, not a single squash-and-return
transform-origin: bottom center anchors the squash so icons read as sitting on a surface
Hand-written spring-damper physics loop drives every stage transition, not CSS keyframes
Each icon has fully independent physics state via a per-call closure — no shared globals
Interrupting a hover mid-sequence smoothly redirects the spring rather than restarting a timeline
Self-terminating requestAnimationFrame loop stops once each icon settles, avoiding wasted frames
Configurable stiffness, damping, and per-stage timing constants
Works on any button-like element via a single attachJellySpring(el) call
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Jelly Icon Dock Hover — Bottom-Anchored Spring Squash-Rebound-Settle Sequence

Screenshot of the Jelly Icon Dock Hover snippet rendered live

This dock treats each icon like a soft rubber tile sitting on a surface: hovering one compresses it downward, then it rebounds into a taller stretch before finally settling back to normal size — a three-stage physical sequence, not a single squash-and-return like jelly press button, and not a rigid scale-up like a typical macOS-style dock magnification effect. Each icon runs its own independent spring simulation, so hovering across the row shows several icons at different points in their own compress-rebound-settle cycle at once.

Bottom-anchored transform origin

Every .jd-icon has transform-origin: bottom center. This single CSS property is what makes the squash read as "sitting on a surface" rather than floating in space — when scaleY shrinks, the icon's bottom edge stays anchored and only its top compresses downward, exactly like pressing down on a rubber tile fixed to a shelf, rather than shrinking symmetrically from its own center.

The three-stage sequence

On mouseenter, the target scale first becomes { sx: 1.22, sy: 0.74 } — wide and flat, the compression. A setTimeout of 90ms later, the target flips to { sx: 0.92, sy: 1.28 } — narrow and tall, the rebound stretch, simulating the stored energy from the compression releasing upward. A further 140ms later, the target settles at { sx: 1, sy: 1 }, natural size. Each stage change calls wake() to restart the physics loop if it had gone idle, and because the spring's own velocity carries over between target changes, the transitions between stages blend smoothly rather than snapping.

The spring-damper loop itself

Identical in structure to jelly press button's physics: each frame computes a force toward the current target ((target - current) * stiffness), accumulates it into velocity, damps that velocity, and integrates it into the current scale. The loop self-terminates once velocity and distance-to-target both fall under a small epsilon, avoiding wasted requestAnimationFrame calls once an icon is visually at rest.

Per-icon independence

attachJellySpring(el) creates a fresh closure over its own sx, sy, vx, vy, and running flag for every icon it's called on. There's no shared state between icons — hovering one doesn't affect any other icon's spring, and rapidly hovering several icons in sequence lets each one run its full three-stage sequence independently and concurrently.

Interrupted hovers

If the cursor leaves an icon mid-sequence, mouseleave immediately overrides the target back to { 1, 1 } — because the spring loop always chases whatever the current target is, this cleanly interrupts an in-progress compress or rebound stage rather than needing to cancel and restart a separate timeline.

Step by step

How to Use

  1. 1
    Hover any dock iconWatch the three-stage motion: a quick downward compression, a rebound into a taller stretch, then a settle back to normal size.
  2. 2
    Hover multiple icons quicklySweep across the dock — each icon runs its own independent sequence, so several can be mid-motion simultaneously.
  3. 3
    Move away mid-sequenceLeave an icon partway through its rebound and watch the spring smoothly redirect back toward normal size instead of finishing the stretch first.
  4. 4
    Tune the timingIn the JS panel, adjust the 90ms and 140ms setTimeout delays to change how quickly each stage of the sequence transitions.
  5. 5
    Tune the physics feelChange stiffness and damping at the top of attachJellySpring for a snappier or bouncier overall feel.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

macOS-style application docks
A launcher dock with tactile, physical feedback on hover feels more premium than a flat scale(1.1) hover state.
CARD
Toolbar and quick-action icon rows
Apply to a floating toolbar's icon buttons so each action feels responsive and alive the moment the cursor arrives, before any click.
Learn multi-stage spring sequencing
Study how chaining setTimeout target changes on top of one continuous spring loop produces a believable multi-beat physical motion from simple physics.
Creative and entertainment app interfaces
Media players, chat apps, and creative tools with a playful brand voice benefit from icon-level physicality across the whole dock.
Mobile-style bottom navigation bars
Bring the same squash-rebound feel to a bottom nav bar's icon buttons for a native-app-like tactile quality on the web.
Related: Jelly Press Button
See the Jelly Press Button for the click-triggered single-stage version of the same underlying spring-damper technique.

Got questions?

Frequently Asked Questions

The sequence models a physical compression-and-release: the icon first compresses under an implied "poke" (wide and flat), then the stored energy rebounds it into a taller stretch, before settling to rest. This three-stage motion reads as far more physical and tactile than an instant scale-up.

Without it, scaling shrinks and grows symmetrically from the icon's own center, which looks like it is floating and expanding in place. Anchoring at the bottom edge keeps the icon's base fixed, so only the top visibly compresses and stretches — matching how a real tile pressed on a surface would deform.

Each call to attachJellySpring(el) creates its own private sx, sy, vx, vy, and running variables inside a closure. There is no shared state between icons, so hovering several icons in quick succession lets each run its own full sequence independently and concurrently.

The mouseleave listener immediately sets the target back to {1, 1}. Because the spring loop always chases whatever the current target is (using its existing velocity), this smoothly redirects the icon back to rest from wherever it currently is in its sequence, rather than snapping or requiring the rebound stage to finish first.

Adjust the target values inside the mouseenter handler: {sx: 1.22, sy: 0.74} for the compression stage and {sx: 0.92, sy: 1.28} for the rebound stage. Values closer to 1 on both axes produce a subtler effect; further from 1 produces a more exaggerated cartoon-like jelly motion.

Yes. Click "JSX" for a React component. Keep the spring state (sx, sy, vx, vy, running, and any pending timeout ids) in a ref per icon rather than component state, since it updates on every animation frame, and clear any pending setTimeout calls on unmount to avoid memory leaks.