Magnetic Snap Carousel — HTML CSS JS Snippet

Magnetic Snap Carousel · Carousels · Plain HTML, CSS & JS · Live preview

About this UI Snippet

Magnetic Snap Carousel — Distance-Based Strength, Not an On/Off Hover

Screenshot of the Magnetic Snap Carousel snippet rendered live

A macOS Dock icon doesn't just pop up when the cursor touches it — it grows *gradually* as the cursor approaches, peaks directly under it, and shrinks back as the cursor moves away. This carousel rebuilds that exact falloff for a row of cards: one mousemove listener on the wrapper computes, for *every* card on every frame, how close the cursor is to that card's center — and scales its lift and size continuously from that single distance value.

One distance, two derived effects

For each card, dist is the horizontal pixel distance between the cursor and that card's center. That gets converted into a strength between 0 (at the edge of a fixed RADIUS) and 1 (dead-center under the cursor) with one line: 1 - dist / RADIUS. Both the vertical lift (translateY) and the scale-up are then just strength multiplied by a maximum constant — so a card barely inside the radius lifts and grows barely at all, while the card directly under the cursor gets the full effect, and everything in between is a smooth gradient rather than a binary hover state.

Why every card is recalculated on every mousemove

Rather than attaching a separate hover listener per card (which only knows about *itself*), one listener on the wrapper loops through *all* cards on every cursor move. That's what makes the neighboring-card falloff possible at all — a per-card :hover can never know how close the cursor is to the card next to it, only whether it's directly over its own boundary.

What's included

Features

Continuous distance-based strength — not a binary hover state, cards ease in and out smoothly
One mousemove listener recalculates every card every frame, enabling true neighbor falloff
Lift and scale both derive from the same single distance-to-strength calculation, staying in sync
z-index rises with proximity so the emphasized card visually sits above its neighbors
Configurable radius and pull strength via two constants at the top of the JS
Touch fallback gives a tapped card the same full emphasis briefly, since hover doesn't exist on touch

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSSix flat cards appear in a row, at rest.
  2. 2
    Move your mouse across themCards near the cursor lift and grow, with strength fading smoothly the further they are from it.
  3. 3
    Move the cursor away entirelyAll cards settle back to their resting size and position.
  4. 4
    Tap on mobileTapping a card gives it the same full-strength lift briefly, since there's no hover on touch.
  5. 5
    Tune the feelAdjust MAX_PULL and RADIUS in the JS to make the effect stronger, weaker, tighter, or wider.

Real-world uses

Common Use Cases

Icon or app launcher rows
The exact macOS Dock magnification feel, reusable for any row of icons or shortcuts.
Product or feature highlight strips
A playful, tactile way to draw attention to whichever item a visitor is currently browsing near.
Navigation bars with rich hover feedback
Give a nav row a premium, responsive feel beyond a flat color-change hover.
Learning distance-based interaction design
A clean, minimal example of converting cursor proximity into a continuous visual response.

Got questions?

Frequently Asked Questions

Increase MAX_PULL (pixels of lift/scale at full strength) for a stronger pull, or increase RADIUS (pixels) so cards start responding from further away. Both are plain constants at the top of the JS.

The cards sit in a single horizontal row, so horizontal distance to each card's center is sufficient. For a grid layout, compute the full 2D distance using both clientX and clientY against each card's center instead.

No — the listener only ever writes to transform and z-index, both of which are compositor-only properties that don't trigger layout, so it stays smooth even across many cards.

Swap the horizontal distance calculation for a vertical one (comparing clientY against each card's vertical center) and change translateY to translateX in the transform, mirroring the effect onto the other axis.

The magnetic hover is a pure enhancement — cards remain in normal document flow with no content hidden behind the effect, and the touch fallback ensures the visual feedback isn't exclusively mouse-only. For full keyboard support, add a :focus-visible style that applies the same full-strength lift.

Source Code

<div class="msc-wrap" id="mscWrap">
  <div class="msc-card" style="background:linear-gradient(160deg,#6366f1,#4338ca)">🎧</div>
  <div class="msc-card" style="background:linear-gradient(160deg,#ec4899,#9d174d)">📷</div>
  <div class="msc-card" style="background:linear-gradient(160deg,#0ea5e9,#0369a1)">⌚</div>
  <div class="msc-card" style="background:linear-gradient(160deg,#10b981,#047857)">🎮</div>
  <div class="msc-card" style="background:linear-gradient(160deg,#f59e0b,#b45309)">🔊</div>
  <div class="msc-card" style="background:linear-gradient(160deg,#8b5cf6,#5b21b6)">💻</div>
</div>
<p class="msc-hint">Move your mouse across the row — nearby cards lift and pull toward the cursor.</p>

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the strength = 1 - dist / RADIUS formula produces a smooth falloff rather than an abrupt on/off effect, and why recalculating every card on every mousemove (instead of per-card hover listeners) is what makes neighboring cards react to a cursor that isn't directly over them. It's also worth asking the assistant to extend the distance calculation to two dimensions for a grid layout instead of a single row, or to add a subtle magnetic "pull toward center" horizontal shift alongside the existing vertical lift.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a magnetic hover effect for a row of cards in plain HTML, CSS, and vanilla JavaScript, where nearby cards lift and grow proportionally to cursor proximity — no library, inspired by the macOS Dock magnification effect.

Requirements:
- A horizontal row of card elements inside a wrapper container.
- A single mousemove listener attached to the wrapper (not one listener per card) that, on every cursor movement, loops through every card and computes the horizontal pixel distance between the cursor's current position and that specific card's horizontal center point.
- For each card, convert that distance into a continuous "strength" value between 0 (at or beyond a fixed maximum radius) and 1 (cursor exactly at the card's center), using a linear falloff formula, not a stepped or binary one.
- Apply both a vertical lift (translateY, negative to move the card upward) and a scale increase to each card, where both values are computed as that card's strength multiplied by separate fixed maximum constants — so a card just inside the radius barely moves while a card directly under the cursor reaches near-maximum lift and scale, and everything between interpolates smoothly.
- Cards beyond the maximum radius from the cursor must return to a fully neutral resting transform (no lift, scale 1).
- When the cursor leaves the wrapper entirely, all cards must reset to their neutral resting state.
- Since touch devices have no hover, provide a touch fallback: tapping a card applies that same full-strength lift and scale transform to it directly (and resets any other card), so the effect isn't exclusively mouse-only.
- All animated properties must be limited to transform (and optionally z-index) so the effect stays smooth and never triggers a layout reflow.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.