Springs, in three numbers
Stiffness, damping, mass. A short, practical map of what each one does to the feel of a component — and which two you should actually touch.
Spring parameters get tuned by vibes. Somebody drags the stiffness up, the thing feels snappier, they ship it. That works right up until the animation needs to match another one, at which point nobody can say what's wrong.
Here's the whole model, and it really is this small:
- Stiffness — how hard it pulls toward the target. Higher is faster and more eager.
- Damping — how much it resists. Higher stops the bounce; too high and it crawls.
- Mass — how much inertia it carries. Higher overshoots more and settles later.
export const springs = {
/** Tooltips, small reveals. Quick, no visible overshoot. */
snappy: { type: "spring", stiffness: 560, damping: 38, mass: 1 },
/** Sheets, drawers, anything with size. A hint of settle. */
smooth: { type: "spring", stiffness: 320, damping: 30, mass: 1 },
/** Playful, for things the user directly grabbed. */
bouncy: { type: "spring", stiffness: 420, damping: 18, mass: 1 },
} as const;
// The wrapper is the travel range; the spring runs on transform.
<motion.span
className="absolute left-0 w-[calc(100%-40px)]"
animate={{ transform: end ? "translateX(0%)" : "translateX(-100%)" }}
transition={springs.smooth} // carries velocity, so it interrupts cleanly
>
<span className="absolute -right-10 size-10 rounded-lg" />
</motion.span>;In practice: leave mass at 1 and forget it exists. Raising mass is almost always a slower way to get the effect you'd get by lowering stiffness, and it makes two springs much harder to match.
export const springs = {
/** Tooltips, small reveals. Quick, no visible overshoot. */
snappy: { type: "spring", stiffness: 560, damping: 38, mass: 1 },
/** Sheets, drawers, anything with size. A hint of settle. */
smooth: { type: "spring", stiffness: 320, damping: 30, mass: 1 },
/** Playful, for things the user directly grabbed. */
bouncy: { type: "spring", stiffness: 420, damping: 18, mass: 1 },
} as const;One rule worth keeping: if a spring visibly bounces on a surface that contains text, it's wrong. Overshoot on a body of text reads as instability. Save it for handles, toggles, and things the user is holding.