You Might Also Like
Motion One Spring Cards — Free Spring-Physics Card Grid Snippet
Motion One Spring Cards · Cards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Motion One Spring Cards — Spring Physics Entrances, Hover Pops & Drag-Back with Motion One

Most CSS and JS animation relies on easing curves — bezier functions that approximate motion but never quite capture the springy overshoot-and-settle of a real physical object. This snippet uses Motion One, a small (under 5KB core) animation library built directly on the native Web Animations API, to drive card entrances, hover states, and drag interactions entirely with spring() easing instead of duration-based curves.
Why spring easing instead of bezier curves
A cubic-bezier easing curve is defined by four fixed control points and always takes the same shape regardless of how far an element travels. A spring, by contrast, is defined by physical parameters — stiffness and damping — and its motion emerges from simulating those forces frame by frame. This means a spring naturally travels faster over a longer distance and settles at a consistent, physical rate, which is why UI built with real device-like motion (iOS, native apps) tends to feel more responsive than web UI relying purely on timed curves. Motion One's spring() helper exposes this as a drop-in easing option for its animate() calls, so this snippet uses the exact same pattern as Scroll Reveal Grid's stagger, just with spring physics instead of a power curve.
Staggered spring entrance
On load, animate(cards, {...}, { delay: stagger(0.08), easing: spring({ stiffness: 220, damping: 18 }) }) animates every card's opacity, scale, and y-offset together, with each card's start delayed by 0.08s more than the previous one via Motion One's stagger() helper — the same staggered-wave idea as a GSAP-based reveal, but expressed with a two-line API and no separate plugin.
Hover pop and drag-back
Hovering a card triggers a snappier spring (stiffness: 300, damping: 14) that lifts and scales it slightly — high stiffness with low damping produces a small overshoot, giving the hover a lively "pop" rather than a flat linear lift. Dragging a card horizontally via pointer events, then releasing it, animates it back to x: 0, rotate: 0 using another spring — so letting go of a card doesn't just snap it back instantly, it bounces back the way a card on a real desk would.
Loading Motion One from a CDN
This snippet loads Motion One's UMD build directly via a <script> tag from jsDelivr, which exposes a global Motion object with animate, stagger, and spring — no bundler or import statement required, matching how this snippet library loads other CDN-based effects. If you're working in a module-based project instead, the same functions are available as named exports from the motion npm package or its ESM CDN build.
Customizing it
Tune stiffness/damping for snappier or looser motion (higher stiffness = faster, lower damping = more bounce), change the stagger delay, or swap the horizontal-only drag for a full 2D drag using Motion One's built-in drag utilities. Pair this with Feature Cards or a Product Card grid for a livelier catalog entrance.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't have to tune spring parameters by trial and error. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how stiffness and damping in spring({ stiffness: 220, damping: 18 }) combine to produce the entrance motion's settle time and how that differs from what a cubic-bezier easing curve of similar duration would look like. The same assistant can help you tune the feel — asking whether the hover spring's stiffness: 300, damping: 14 is too bouncy for a dense grid of many cards, or whether the drag-back spring should use different values than the entrance spring. It's also useful for extending the interaction: ask it to add full 2D drag instead of horizontal-only, constrain the drag to the grid's bounding box, or replace the shuffle button's instant fade-out with its own spring-based exit animation. Treat the code less like a finished artifact and more like a starting point for a conversation.
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:
Build a "spring cards" grid in plain HTML, CSS, and JavaScript using the Motion One library (loaded as a global via its CDN UMD build), where every animation — entrance, hover, and drag release — uses spring() easing instead of duration-based bezier curves.
Requirements:
- A responsive grid of card elements (name, role, avatar), initially hidden (opacity 0, scaled down) via CSS, that animate into place on page load using Motion One's animate() targeting all cards at once.
- The entrance animation must use Motion One's stagger() helper to delay each card's start by a small fixed amount relative to the previous card, and must use spring() (not a named easing string or bezier curve) as the easing option, combining opacity, scale, and a vertical offset.
- On mouseenter, each card should independently animate to a slightly larger scale and a small upward y-offset using a snappier spring configuration (higher stiffness, lower damping than the entrance), and animate back to its resting state with a different spring on mouseleave.
- Implement horizontal pointer-driven dragging on each card: track pointerdown/pointermove to move the card and apply a subtle rotation proportional to the drag distance directly via a CSS transform, then on pointerup use Motion One's animate() with spring() easing to animate the card's position and rotation back to zero — it must visibly bounce back rather than snap instantly.
- Add a "shuffle" button that quickly fades and scales all cards out, waits for that animation to finish (using the returned animation's .finished promise), then re-triggers the original staggered spring entrance.
- Load Motion One only via a CDN script tag (no bundler, no import statement) and access its animate, stagger, and spring functions from the resulting global object.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.
Step by step
How to Use
- 1Add the Motion One CDNInclude the motion.js UMD build from the CDN panel — it exposes a global Motion object.
- 2Paste HTML, CSS, and JSA team card grid and a shuffle button render.
- 3Watch the entranceCards spring in with a staggered 0.08s delay between each.
- 4Hover a cardIt springs up and scales slightly with a snappy pop.
- 5Drag a card sidewaysRelease it — it springs back to center with a slight bounce.
- 6Click Shuffle inCards fade out quickly, then replay the staggered spring entrance.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
stiffness controls how strongly the spring pulls toward its target — higher values move faster and more urgently. damping controls how much the motion is resisted as it approaches the target — lower damping allows more oscillation and overshoot before settling, higher damping approaches smoothly with little or no bounce. Motion One simulates these forces frame by frame rather than interpolating along a fixed curve, which is why the resulting motion looks different depending on how far the element travels.
Motion One is a much smaller library (a few KB versus GSAP's larger core plus plugins) built directly on the browser's native Web Animations API, which makes it a good fit when spring easing and simple entrance/hover/drag animations are all you need. GSAP remains the stronger choice for complex timelines, scroll-triggered sequencing, or SVG morphing — see Scroll Reveal Grid for a GSAP-based example doing exactly that.
Pointer events (pointerdown/pointermove/pointerup) track horizontal movement and directly set a translateX/rotate transform on the card while dragging, giving instant 1:1 feedback. On pointerup, Motion One's animate() call takes over, animating x and rotate back to 0 using spring() easing — so the release feels like a physical snap-back rather than an abrupt jump.
Yes. This snippet loads the UMD build via a <script> tag so animate, stagger, and spring are available as the global Motion object with no bundler. In a module-based project, install the motion npm package and import { animate, stagger, spring } from 'motion' directly, or import from an ESM CDN like esm.sh/motion if you're not using a bundler but want ES module syntax.
Import animate, stagger, and spring from the motion package (or reference the CDN global if not bundling), then call animate() inside a mount effect (useEffect, onMounted, or ngAfterViewInit) targeting refs to your card elements rather than querySelectorAll. Attach the pointer event handlers as React/Vue/Angular event bindings instead of addEventListener, keeping the same spring() easing values.