Motion With Intent: Animation That Doesn't Wreck Performance

Motion is the fastest way to make a site feel expensive. It's also the fastest way to make one feel broken, and the distance between those two outcomes is smaller than most people expect.
The difference isn't taste, or budget, or which library you picked. It's whether the motion was planned with the build or bolted on near the end. Animation added late fights the layout it was added to. It drops frames on the phones your actual customers use, and it tends to arrive in the same sprint as the launch date, which is when nobody has time to test it on anything.
Animate the cheap properties, avoid the expensive ones
This is the one piece of technical knowledge that does most of the work, and it's not complicated. Browsers handle some changes almost for free and others at real cost.
Transform and opacity are the cheap pair. The compositor can handle them on its own, without asking the rest of the rendering pipeline for anything, which is why a transform-based animation holds 60fps on hardware where the alternative stutters. Everything else is where the trouble is. Animate width, height, top, left, margin or padding and you force the browser to recalculate layout on every frame, for that element and often for a good deal of the page around it.
So: move things with translate, scale them with scale, fade them with opacity. If you find yourself animating a layout property, the usual fix is to reach for transform instead, and the second most usual fix is to accept that the effect isn't worth what it costs.
One caveat that catches people, because the advice gets repeated without it: will-change is not a performance setting you sprinkle on things. Each one promotes an element to its own layer, and layers cost memory. Put it on forty elements and you'll make the page slower than it was before you optimised it.
The good news is you don't have to take any of this on faith, and this is the habit worth building. Chrome DevTools will show you the layers the page has created, and it will flash the regions being repainted as you scroll. If a scroll is causing large areas of the page to repaint, you have found the problem, and you usually find it in about thirty seconds. Turn on the frame rate meter while you're there. Watching the number drop on a specific interaction is far more useful than a Lighthouse score after the fact, because it tells you which interaction rather than that something, somewhere, is slow.
Treat scroll as one timeline, not forty listeners
Scroll is the primary interaction on almost every marketing site, so it's worth being deliberate about. The pattern that goes wrong is a pile of independent scroll listeners, each added by whoever built that section, all firing on the same frame and all measuring the DOM at slightly different moments.
The symptom is jank that only appears when two effects overlap, which makes it miserable to reproduce and very easy to ship. We run one loop instead. Pinned sections, scrubbed reveals and velocity-reactive effects all read from a single source of truth, so nothing is measuring layout while something else is changing it.
- One requestAnimationFrame loop for scroll and animation, not one per component.
- Read layout in a batch, write in a batch. Interleaving them is what causes forced reflow.
- Use IntersectionObserver to decide what's on screen. Don't compute it from scroll position yourself.
- Lazy-load anything heavy — WebGL especially — behind the fold, and make sure the page is complete without it.
If the animation can't survive a three-year-old Android on hotel wifi, it isn't finished. It's a demo.
Reduced motion is a designed state, not a fallback
Every motion we ship has a prefers-reduced-motion branch, and we design it rather than generate it. There's a lazy version of this where you disable all the transitions and call it accessibility, and it usually produces a page that jumps around oddly because half the layout assumed something would animate into place.
Doing it properly is better for the people who need it, obviously. But it's also a genuinely useful forcing function for the team, and this is the part worth internalising: if the page stops making sense when you remove the motion, the content underneath was too thin. The animation was doing the work the copy should have been doing. That's a content problem wearing an engineering costume, and it's better to find it in week three than after launch.
Motion belongs in the design system
Here's where most of this goes wrong at scale, and it's not really a performance issue at all. Durations and easing curves get chosen per component, by whoever built that component, on the day they built it. Nobody writes them down. Six months later the site has fourteen different easing curves and four ideas about how long a transition should last, and it feels subtly incoherent in a way that's hard to name in a review.
Motion should be tokenised exactly like colour and spacing. A small set of durations. A smaller set of curves. Named by role, so people pick the right one without needing to have an opinion about cubic-bezier values.
- 01Three durations is usually enough. Something quick for state changes on things you're touching, something medium for elements entering and leaving, something slower for large deliberate transitions. Fast, medium, slow — and if you need a fourth, ask why first.
- 02Two or three easing curves, maximum. One that starts fast and settles for things arriving, one symmetrical one for things that move under direct manipulation. Most sites need fewer than they have.
- 03Write down when not to animate. Data tables, form validation errors, anything that appears because something went wrong. Motion on an error message reads as decoration at exactly the moment someone wants information.
This is a large part of what a design system agency is actually being paid for, and it's the part that tends to get cut from the scope first because it's invisible in a presentation. It's also the part that determines whether the system still holds together in a year, once the people who built it have moved on to something else.
Hold a performance budget, and hold it early
We set Lighthouse and Core Web Vitals targets at the start of a build and treat them as acceptance criteria, the same as any functional requirement. It matters that this happens at the start. A budget agreed in week one is a design constraint that shapes what gets built; the same budget introduced in week ten is just bad news, and by then the expensive decisions are already load-bearing.
Two of the three Core Web Vitals are directly exposed to motion, which is worth knowing before you start. Cumulative Layout Shift punishes exactly the animations that move layout around, so the compositor-only rule pays off twice. And Interaction to Next Paint, which replaced First Input Delay in 2024, measures how quickly the page responds when someone actually does something — so a main thread busy running your hero animation is a main thread that isn't responding to a tap.
The practical version: measure on an ordinary Android handset with the CPU throttled, not on the machine you built it on. Every animation looks fine on a developer laptop. That's not a useful test and it never has been.
What this buys you
Sites that feel alive and still load fast, which people talk about as a trade-off and mostly isn't one. The two goals only conflict when motion is an afterthought competing for resources that were already committed. Plan it in from the first commit and there's nothing to trade away, because the budget was part of the design rather than something the animation had to be squeezed into afterwards.
Motion is also doing commercial work when it's done well, not just aesthetic work. It shows state changes so people know something happened. It draws attention to the one thing on the page that matters. It covers loading gracefully instead of leaving a blank rectangle. There's more on how that connects to enquiries in conversion-focused web design, which covers the same ground from the other end.
If you're building or repairing a system where this needs to be consistent across a lot of surfaces, that's the work we do. And the one habit worth adopting today: pick your three durations before you build the first component, not after you've built forty.

