Bouncy, spring-like motion used to be a reliable tell that JavaScript was involved. The easing curves CSS shipped with — ease, ease-in-out, and hand-tuned cubic-bezier() values — can't overshoot. A cubic Bézier has two control points, which is enough for a smooth acceleration but not enough for a curve that shoots past its target and settles back. That limitation is gone, and the thing that removed it is one unassuming function: linear().
What does linear() actually do?
It defines an easing curve by listing points along it, and interpolating straight lines between them. On its own that sounds like a downgrade — straight lines instead of smooth curves. The insight is that with enough points, a series of short straight segments approximates any curve you like, including ones that overshoot and oscillate. A spring is just a curve that passes 1.0, climbs a little higher, dips back under, and wobbles to rest. Express that as forty or fifty points and linear() reproduces it convincingly.
.card {
transition: transform 0.6s linear(
0, 0.006, 0.025, 0.101, 0.539, 0.826,
0.960, 1.052, 1.076, 1.058, 1.026,
1.003, 0.997, 1.001, 1
);
}
Read the numbers and you can see the bounce: the value sails up past 1.0 to around 1.076, then eases back down through 0.997 before settling at 1. That overshoot-and-settle is the spring.
Where do those numbers come from?
You do not calculate them by hand, and you shouldn't try. The workflow that has emerged is to use a spring or easing generator — you pick the physical feel you want using intuitive controls like stiffness, mass, and damping, and it emits the linear() string for you to paste in. Think of the generator as a compiler: springs are described in physics terms, and linear() is the target format the browser understands. You tune the feel; the tool handles the arithmetic.
If a library already did this, why switch?
Three reasons, and they compound.
First, it runs where CSS transitions and animations run, which means the browser can hand qualifying properties like transform and opacity to the compositor. A JavaScript spring updates a value every frame on the main thread; if that thread is busy, the spring stutters. The CSS version is far more resistant to that.
Second, it composes with everything CSS already offers. You can attach a spring curve to a transition, to an @keyframes animation, or — combined with scroll-driven timelines — to motion that a spring drives as the user scrolls. No bridge code between your animation library and the rest of your styles.
Third, it is less to ship and less to maintain. A generated string of numbers sitting in a stylesheet has no dependency, no version, and no bundle cost.
A note on restraint
Because springs are now this easy, the temptation is to put a bounce on everything. Resist it. A subtle overshoot on a modal appearing, or a toggle snapping into place, reads as polish. The same effect on every hover, every menu, every list item reads as noise and slows the interface down in the places people use most. The best use of spring easing is sparing: reserve it for the one or two moments where a little physical life genuinely helps the user understand that something arrived or changed, and keep the workhorse transitions calm.
The headline is simple. The one curve type CSS was missing is here, delivered through a function that looks almost too plain to matter. Reach for a generator, paste the result, and the bouncy motion that used to justify an animation library is now fourteen numbers in a transition.

