Life Is in the Transitions

I’m staring at a chat app I built. You tap a room, and the whole page just is different. No slide, no fade, no indication of where you came from or where you’d go back. Every screen is an island. It works — functionally. But it feels like a PowerPoint deck, not a place.

This is layout jank. Not just the performance kind, the experiential kind, where your brain loses the map. One frame you’re here, the next you’re somewhere else, and you have to rebuild context from scratch. The problem is missing metaphor. Nothing tells you whether you moved forward, backed out, opened a layer, or swapped modes.

Most apps ship this way. Figma hands off pixel-perfect statics, rectangles at rest, and the developer fills in the gaps with instant state swaps. What makes software feel like a place instead of a slideshow gets left to “the developer will figure it out.” The developer does not figure it out.

Metaphors that worked

The iPhone figured it out in 2007. On a 412 MHz ARM chip with 128 MB of RAM, Apple allocated 0.35 seconds of every navigation to a push animation — scarce compute spent on motion, because motion carried meaning. Push goes right, pop goes left, modal comes up, dismisses down. You always knew where you were in the stack without thinking about it.

Before that, most handheld UIs treated navigation as replacement: tap, redraw, new screen. The hierarchy existed, but it rarely moved.

The nav push was the spatial metaphor. Content is over there, to the right, and you’re moving to it. The animation isn’t decoration. It’s information.

Then the indie app scene ran with it. iPad storybook apps shipped gestural page turns where the page wasn’t a binary state but a continuous object under your finger. You could peek, hesitate, commit, or retreat. The transition was the UI.

But spatial is just one metaphor. On confession.website, I didn’t start with a transition strategy. The first builds had the same jump-cut problem. Suppress the rerender here, double-rAF the fade there, kill the 300ms tap delay on mobile. Whack-a-mole.

The divider changed everything. Once I moved it out of the card templates and into the frame — one DOM element, same position on every screen — recording no longer felt like entering a new screen. It felt like the same place changing state. Content changes above and below it. Nothing shifts. Every state change happens around the divider, not to it.

That one decision — the divider doesn’t move — resolved dozens of layout bugs at once. Not because I found the right fix, but because I found the right constraint.

On this blog, it’s a 0.15-second fade-shift on every page load. Barely perceptible. Remove it and the page slams into existence instead of arriving.

Spatial (iPhone push), anchored (confession.website divider), atmospheric (fade-shift). Three metaphors, same problem: they give your brain a story about what just happened, so it doesn’t have to reconstruct one from scratch.

Pick a metaphor

The gap between jank and butter is one decision: what is the transition about?

MetaphorWhat it does
SpatialThings slide in from where they live
AnchoredOne element stays fixed while the world changes around it
ContinuousThe user’s gesture drives the state change in real time
AtmosphericContent fades in like it was always there

These patterns survived a decade of framework churn. They outlast their implementations because the metaphor is right.

Spatial

  • Nav push/pop
  • Modal sheet slide-up
  • Drawer/side panel
  • Off-canvas menu
  • Tab bar switching
  • Carousel slide
  • Master-detail drill-in
  • Bottom sheet expansion
  • Accordion expand/collapse
  • Toast/snackbar slide-in
  • Notification banner slide-down
  • Wizard step slide
  • Card stack swipe
  • Sidebar collapse/expand

Anchored

  • Fixed header
  • Sticky nav
  • Sticky table header
  • Frozen table column
  • Pinned sidebar
  • Persistent bottom nav
  • Persistent tab bar
  • Floating action button
  • Scrollspy nav
  • Sticky section label
  • Split-pane divider
  • Docked inspector panel
  • Persistent mini-player
  • Chat composer fixed to bottom
  • Breadcrumb bar
  • Back-to-top button

Continuous

  • Pull-to-refresh
  • Swipe-to-dismiss
  • Swipe actions on list rows
  • Drag-and-drop reorder
  • Drag-to-resize panel
  • Interactive back gesture
  • Edge-swipe drawer
  • Bottom sheet drag
  • Range slider drag
  • Scrubber/timeline drag
  • Pinch-to-zoom
  • Pan-to-move canvas
  • Overscroll rubber-band
  • Scroll-linked parallax
  • Scroll-linked header collapse
  • Page curl
  • Swipe between tabs

Atmospheric

  • Skeleton shimmer
  • Content placeholder fade-in
  • Lazy-loaded image blur-up
  • Fade-and-shift entrance
  • Staggered list fade-in
  • Toast fade-in/out
  • Tooltip fade
  • Modal backdrop fade
  • Dialog fade/scale
  • Spinner fade to content
  • Route crossfade
  • Tab content crossfade
  • Theme transition crossfade
  • Inline validation fade-in

Honorary mentions

The four metaphors above are abstract — they describe how the transition moves. But you can also start from a concrete object and let the metaphor generate the interface. Pick any physical thing with a natural transition and the UI patterns fall out on their own:

The perceptual physics explain why these metaphors work — Gestalt continuity, least surprise, signal-to-noise. This post is about which metaphor to pick.

The code can be small — a CSS transition, a fixed-position element, an animation-fill-mode. The hard part is choosing, because the choice constrains every layout decision downstream. confession.website’s entire CSS flows from “the divider doesn’t move.” The iPhone’s entire navigation stack follows from “content lives on a horizontal rail.”

That constraint is the point. Without a metaphor, every state change is an ad hoc jump cut, and no amount of polish will make it feel coherent. With one, even the simplest animation — 0.15 seconds, 2 pixels — is enough. The brain only needs a hint.1

Reference implementations

Footnotes

  1. The title is from Bruce Feiler’s Life Is in the Transitions (2020). I haven’t read it, but the premise — that meaning lives in the passages between stable states, not in the states themselves — maps cleanly to UI.