Chapter 06
Motion
Nothing moves without a reason. Movement explains a change, points at what matters, and then gets out of the way. Underneath it all is a very small set of curves.
Built with
- Framer Motion
- CSS transitions
- Lenis
Timing
Five easings, and what each is for.
Most of the site's motion uses one curve, Settle: it starts fast and lands softly, like something being put down gently. Press play to compare how each one arrives.
Settle
0.5s · cubic-bezier(0.23, 1, 0.32, 1)
Entrances. Reveal, the prism mark and most Framer Motion transitions.
Rise
0.8s · cubic-bezier(0.22, 1, 0.36, 1)
CSS keyframe entrances, like the chapter titles on this page.
Slide
0.5s · cubic-bezier(0.87, 0, 0.13, 1)
Accordions: a firm start and a firm stop.
Ease-out
0.4s · ease-out
Hover and press states: 300 to 500 ms, handled by plain CSS.
Linear
0.8s · linear
Things that never stop: spinners and orbits.
Entrances
Things arrive; they do not pop.
Content rises fourteen pixels while it fades in, over half a second, once, when it scrolls into view. Siblings are staggered by a fraction of a second so the eye is led through them in reading order.
const EASE = [0.23, 1, 0.32, 1] as const; <motion.div initial={{ opacity: 0, y: 14 }} animate={isInView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.5, delay, ease: EASE }}/>Choosing
Which tool, when.
CSS transitions
Hover, press and focus. Anything that is a state change on one element.
Zero JavaScript, and the browser handles it on the compositor.
Framer Motion
Entrances, exits and anything driven by React state, like the prism mark changing quadrant.
It lets an element animate between states without hand-written timing code.
Lenis
Smooth page scroll, and the scroll-to behaviour behind the top navigation.
Inertia that feels native, applied once at the page level.
Care
When to stand still.
Some people ask their device to cut motion, and that is a setting to honour, not a suggestion. Loops and travelling hovers carry a fallback, and the entrance animations on this page switch off with it.
/* A loop or a hover movement always gets a way out */<Loader2 className="animate-spin motion-reduce:animate-none" /> transition-transform duration-300motion-reduce:transition-none /* CSS keyframes switch off in the stylesheet */@media (prefers-reduced-motion: reduce) { .ink-rise { animation: none; }}