Motion with meaning
What Material Design 3 actually says about animation — springs, emphasized easing, and the discipline of doing less.
Animation is the most abused layer of frontend craft. We reach for it to feel modern and ship pages that feel slow. Material Design 3's motion system is the best writing on doing it responsibly, and its core insight fits in a sentence: motion should explain, not decorate.
The two curves that matter
MD3 collapses easing down to a small vocabulary. Two curves carry nearly all of it:
:root {
--motion-emphasized: cubic-bezier(0.2, 0, 0, 1); /* things the user did */
--motion-standard: cubic-bezier(0.3, 0, 0.8, 0.15); /* things leaving */
--motion-medium: 350ms;
--motion-long: 500ms;
}The emphasized curve front-loads almost all of its movement — by 40% of the duration it has covered ~90% of the distance, then spends the rest settling. The eye reads that as responsiveness (it moved instantly!) and grace (it landed softly). Linear easing, by contrast, reads as mechanical; ease-in reads as sluggish. One cubic-bezier swap makes the same 350 ms feel half as long.
Directionality is the subtle half: things entering deserve the emphasized curve and a longer duration; things exiting should get out fast with the accelerating standard curve. The user asked for the next thing — the old thing shouldn't linger to say goodbye.
Choreography over animation
A screen where twenty cards animate independently is noise. MD3's answer is shared choreography: elements move as one system, staggered just enough to establish order.
.post-card { animation: rise var(--motion-long) var(--motion-emphasized) backwards; }
.post-card:nth-child(2) { animation-delay: 40ms; }
.post-card:nth-child(3) { animation-delay: 80ms; }Forty milliseconds. The stagger should be felt, never watched. If you can count the cards arriving, triple the overlap.
On this site, page-to-page choreography rides the View Transitions API — the browser cross-fades and slides between routes with the same two curves, and a post card's cover morphs into the article header because both carry the same view-transition-name. Fifteen lines of CSS, no FLIP math, and it degrades to an instant cut in browsers that lack it. The best progressive enhancement is the kind you'd never miss.
The performance contract
Animation earns its frame budget or gets cut:
- Animate only
transformandopacity— the compositor properties. Animatingtop,heightorbox-shadowforces layout or paint on the main thread. - Fake shadow transitions by cross-fading a pseudo-element's opacity over a pre-rendered shadow.
prefers-reduced-motionis not optional. Every animation on this site collapses to a fade or nothing; the WebGL sky freezes to a still. Vestibular disorders are more common than you think.
Duration is a promise. 200 ms says "I heard you." 500 ms says "watch this." Never say "watch this" twice in a row.
Doing less, on purpose
The discipline that actually shipped this site: every animation had to name the question it answers. Where did this come from? What did my tap do? Where should I look next? If the answer was "none, it just looks cool" — deleted. What survived is maybe a dozen motions, each under half a second, each explaining something.
The aurora is the one exception. It answers no question. It's just the sky being the sky — and every garden needs weather.