Motion

Restrained, functional motion. Every animation has to justify what it improves.

Motion only gets in if it supports what is being said. The tone is precise, not elastic: no animated text, no parallax, spring or skeletons.

Layers

LayerWhatHow
1. Smooth scrollSmoothed global scrollLenis on every page
2. Staggered entranceFade-in on load.fade-entry, random 50-200ms delay, 0.4s ease-out
3. Scroll revealElements entering the viewportIntersection Observer + CSS
4. InteractionsHover, states, modalCSS transitions per component

Duration and easing

InteractionDurationEasing
Link/button hover0.2sease
NavPill show/hide0.3sease-out
BentoGrid body reveal0.3sease-out
Modal open0.3sease-out
Modal close0.2sease-in

What doesn't animate

  • Prose word by word or line by line. Whole blocks only.
  • Parallax. Never. On no element.
  • Loading animations (skeletons, shimmers). Static site.
  • Spring physics. The tone is precise, not elastic.

Allowed stack

LibraryUseLoading
LenisSmooth scrollEvery page
CSS transitionsHover, states, revealsNative
Intersection ObserverScroll reveal triggerNative API
GSAPTimeline coordination onlyPer component, never global