Leave faster than you arrive

The internal tools I build at work have almost no animation, on purpose. What they do have follows one rule: things arrive on a deceleration and leave faster. Arriving is news, so it gets a moment. Leaving is you being done with something, so it gets out of the way.

The whole motion system is four durations and two curves:

--m-fast: 120ms;  /* colour, presses */
--m:      200ms;  /* chevrons, carets */
--m-slow: 280ms;  /* toasts, drawers arriving */
--m-focal:560ms;  /* the one authored moment */
--ease-out:  cubic-bezier(.16, 1, .3, 1);  /* arriving */
--ease-exit: cubic-bezier(.4, 0, 1, 1);   /* leaving */

Buttons and chips shrink to 0.97 when pressed, bigger targets like cards only to 0.985, since 3% of something big is a lot of pixels.

Toasts

A toast slides up 8px and fades in over 280ms on the ease-out. When it goes, it doesn't play that backwards: it drops 4px and fades over 180ms on the exit curve, which starts slow and accelerates away. Press both buttons a few times and you'll feel the difference before you can name it. The symmetric one hangs around like a guest who has said goodbye twice.

Left: 280ms in, 280ms out on the same ease-out. Right: 280ms in, 180ms out on the exit curve. The toast stays up for 1.4s here (four in the real thing).

There's only ever one toast element, with role="status", reused for every message so screen readers announce it and nothing stacks. The exit is a Web Animations call, so it can hide the element when it finishes. One trap I only noticed while rebuilding it for this page: the real toast is centred with translateX(-50%) on itself, and an animation that sets transform replaces that, so it drifts sideways as it comes and goes. Here the centring lives on a wrapper, and the fix is going back to work with me.

Drawers and themes

On a phone the navigation is a drawer: 280ms in on the ease-out, 180ms out on the exit curve, with the backdrop fading on the same split. Switching theme crossfades the whole page in 220ms with a View Transition, so nothing flashes.

Playground
Open and close the menu (tap the backdrop to close), then switch the theme.

The bar that never finishes

When you click a link and the next page takes a moment, a 2px bar crawls along the top. It heads for 85% over six seconds on cubic-bezier(.1, .7, .2, 1), which gets two-thirds of the way there in the first second and then slows to a creep. It never claims to be done. When the page arrives the bar just fades out over 300ms while retracting, and if nothing arrives in twelve seconds (a download, an error) it gives up on its own.

Fake navigations of 350ms, 3.2s and forever.

A clock that goes negative

One of the tools has a presenter mode for giving a five-minute version of a story: six frames, arrow keys, a script you can toggle, and a timer counting down from 5:00. The detail I like is what happens at zero. It doesn't stop or beep; it keeps going, prints a plus sign, +0:12, and changes colour. You're over, it says, by this much. That's all. Each new frame rises 6px over 280ms, so moving forward feels like turning a page rather than a cut. This one starts at 0:10 so you don't have to wait.

Pitching a better office kettleThe five-minute version
0:10
Press play, then talk too long. ← → or Space move between frames once it has focus.

Being pointed at

Long pages have an "On this page" bar. Click an entry and the page scrolls there, and then the section briefly tints: an inset box-shadow with a 100vmax spread, which fills the box with 9% of the accent and fades over 1.1 seconds. It waits 220ms first, so the tint happens where you're looking, after the scroll lands. Click the same entry twice and it flashes twice, because the class is removed, the layout is read with void el.offsetWidth, and the class goes back on, which restarts the animation.

Brief

Numbers

Risks

Next steps

Scrolls inside the box, then flashes. Try clicking one twice.

With reduced motion on, nothing travels or scales: the toast just fades in 150ms, the drawer snaps, the flash is skipped. Colour changes stay, so feedback still reads.

Built
October to November 2026
Stack
CSS custom properties, Web Animations, View Transitions
This page
Vanilla JS and CSS
Lives at
Internal tools at work
Source
Private

Built with Claude Code. The kettle pitch is fictional, as is the kettle.