Micro-interactions guide

Moving between views

Navigation & Transition Micro-Interactions

Navigation feedback answers a different question than control feedback: not what did I just change, but where am I and is the next view coming. It should start only when the wait is real, preserve the reading position on the way back, and never make the destination wait on an animation.

Delay the indicator
Show progress only after a wait becomes real.
Keep one origin
Move the element the user chose, not the page.
Never animate back
Restore scroll position instantly on return.

Pattern examples

6 ways to communicate the state change

Hover, focus, press, or toggle the specimens. Each recipe keeps the durable state in semantic markup and uses animation as a supporting layer.

01

Route progress bar

Run a thin indeterminate rail across the top of the frame while the next route resolves.

Delay it by roughly 200ms so a fast navigation never flashes a bar, and remove it as soon as the view paints.

transform: scaleX(var(--route-progress));
transition: transform 200ms ease-out;
Canvas section
02

Tab indicator slide

Travel one shared underline to the selected tab instead of redrawing a separate marker per tab.

Use real tablist semantics with a roving tabindex; the indicator is decoration layered on aria-selected.

transform: translateX(calc(var(--tab-index) * 100%));
transition: transform 220ms ease-out;
03

View transition morph

Grow the card the user activated into the detail view so the destination has a visible origin.

Give both elements the same view-transition-name and keep the navigation working when the API is unsupported.

::view-transition-old(card),
::view-transition-new(card) { animation-duration: 300ms; }
Project settings
04

Sticky header condense

Shrink a sticky header as the page scrolls so content gains room without losing the navigation.

Drive it from a scroll timeline instead of a scroll listener, and keep the condensed header tall enough to stay tappable.

animation: condense linear both;
animation-timeline: scroll(nearest);
animation-range: 0 3rem;
05

Scroll progress rail

Fill a rail in proportion to how much of a long document has been read.

Reserve it for genuinely long reading; on a short page it reports progress nobody was tracking.

transform-origin: left;
animation: fill linear both;
animation-timeline: scroll(nearest);
06

Page cross-fade

Dissolve between two views when no shared element connects them.

Keep it under 200ms and never delay input; a cross-fade is the fallback for when a morph has nothing to morph.

transition: opacity 180ms ease-out;

Implementation check

Ship the fallback with the effect.

Test keyboard focus, touch, reduced motion, slow work, failure, repeated activation, and the final resting state. A robust interaction remains understandable when every transition is removed.

@media (prefers-reduced-motion: reduce) {
  .motion {
    animation: none;
    transform: none;
    transition-duration: 0.01ms;
  }
}

Plan the complete React experience.

Map the components and states around these interactions, then generate the project structure from one visual workspace.

Start planning free

We'd like to use Google cookies to understand how Nodlume is used and to measure our advertising. Nothing loads until you choose, and declining does not affect anything in the app.