Motion guidelines.
Motion on this site works like film editing: it draws the visitor into each section, then gets out of the way. Every value here is the one the site ships with, and most sections come with a live demo. See also the brand guidelines.
Your system asks for reduced motion, so the demos show their finished state, as the site does.
Pure Experience Design
Draw them in, then get out of the way.
Six principles, a handful of tokens and one rule for timing. Everything else follows from them.
Motion
Principles
What every animation on the site has to answer to.
- 01
Cinematic
Each section arrives like a cut in a film. Its animation waits until the visitor is there, so they see it begin.
- 02
Orient, never decorate
Motion says where something came from, what changed or where to look next. If it does none of those, it goes.
- 03
One feel
Every reveal, panel and hover uses one family of ease-out curves and a short list of durations.
- 04
Measured, not guessed
Blur and travel were measured from reference sites; timing is checked frame by frame in storyboards.
- 05
Reduced motion is a design
Not a faster version: a still, finished one that makes sense on its own.
- 06
Only when seen
Nothing animates off-screen. Backgrounds pause when they leave the view and resume when they return.
Tokens
Duration
Three durations cover the site. Press Play to compare them on the same ease.
| Token | Value | Use |
|---|---|---|
| --m-stagger | 0.045s | Between words in a reveal |
| --m-stagger-ch | 0.018s | Between characters (big numbers and short titles) |
| --m-blur | 10px | Starting blur of a reveal, measured |
| --m-shift | 4rem | Starting offset, sideways reveals |
| --m-shift-y | 1.25rem | Starting offset, upward reveals |
Tokens
Easing
One family: ease-out. Things arrive quickly and settle slowly, the way a camera lands on a subject. Nothing on the site eases in, bounces or overshoots.
Text
The reveal
Headings arrive in pieces: each word (or character) fades in from a blur and a short offset, one after another. The text is real in the page first; it is only split in the browser, with one whole copy kept for screen readers.
Seven phases, one thread.
Word · from the side (--m-shift) · titles and section headings
98.8
Character · from below (--m-shift-y) · big numbers and short titles
| Setting | Value | Why |
|---|---|---|
| Start | opacity 0 · blur 10px · offset 4rem (x) or 1.25rem (y) | Measured from the reference sites |
| End | opacity 1 · blur 0 · offset 0 | Blur is cleared afterward, so text stays crisp |
| Duration and ease | 0.9s · expo-out | --m-dur, --m-ease |
| Stagger | 0.045s per word · 0.018s per character | --m-stagger, --m-stagger-ch |
| When | At 90% of the screen; immediately if already on-screen when the page loads | A half-revealed heading under a visible eyebrow reads as broken |
| Long text | Over 42 pieces, falls back to words, then lines | Blur on too many pieces drops frames |
| Width change | Re-split after resize and after fonts load | Lines are measured, so they must match the font on-screen |
| Phones and tablets | Off below 1024px: headings are simply there. On home, a case’s statement fades in with its client mark (0.9s, expo-out) | Splitting and blurring words ran poorly on phones and tablets |
Timing
Cinematic start
A section’s background animation starts only once the section’s top has passed 65% of the screen, never while the section above still fills the view. The visitor arrives, then the story begins.
- Applies to every section background: canvas, WebGL, SVG and film.
- The headline and text reveals are not delayed; only the background story waits.
- New backgrounds use the same rule:
START_ATin the component.
Page
Scroll
Smooth scrolling gives the page its weight; the header stays on-screen throughout.
| Behavior | Spec |
|---|---|
| Smooth scroll | Lenis · duration 1.1s · expo curve 1.001 − 2^(−10t) · on GSAP’s ticker, so ScrollTrigger agrees · off on touch screens (phones and tablets scroll natively, with no soft snap) |
| Header | Always on-screen: the mark on its dark pill, the nav with a soft dark halo; on phones and tablets, a full-width dark bar with the nav centered on the mark |
| Anchor jumps | Smooth, with the header’s height subtracted |
| Soft snap (home) | A wheel or trackpad scroll that comes to rest within 30% of a screen of a full-screen section’s top glides the rest of the way (160ms after the input goes quiet, a 0.075 lerp); farther, it stays put. Only sections at least a screen tall count; touch, keys, the scrollbar and links are never snapped, nor anything while the Work overlay is open |
| Inner scrollers | Opt out of smooth scroll (data-lenis-prevent) so they scroll natively |
| Reduced motion | Smooth scroll is off entirely, so nothing snaps |
Interface
Interaction
Small, quick and always paired with a change of color or state, never movement alone.
Hover · color 0.5s · translate 6px over 0.6s · accordion ease
The height opens with the grid-rows trick over 0.8s; the accent rule draws across the top over 0.9s; the plus turns into a minus.
Disclosure · grid-template-rows 0fr → 1fr · 0.8s · one open at a time
| Pattern | Spec |
|---|---|
| The mark | Pill widens 0.7s expo; words fade in 0.6s, staggered 0.1 / 0.16 / 0.22s; the tagline follows at 0.35s |
| Links in running text | White, at the text’s weight, with an accent underline; the text turns to the accent over 0.2s. Navigation, breadcrumbs and cards have no underline |
| Calls to action | Opacity to 70% over 0.25s |
| Frames and cards | Image scale 1.03 over 0.8s on hover; no lift, no shadow |
| Accordion scroll | The row you open stays steady while one above closes, then glides up under the header (cubic ease-out, 0.8s), so what you opened is what you see |
| Lightbox and overlays | Fade in; focus moves in and returns to where it came from |
| Focus | Never animated away: the ring appears instantly |
Media
Backgrounds and film
The live backgrounds and films behind the words, from the home hero to the case studies. They are storyboarded frame by frame on the About this site page.
- Start at 65%, run only while on-screen, pause when off-screen.
- Films are muted and play inline; they wait in the dark, fade up as their section arrives and pause when it leaves. 10-bit AV1 and HEVC first, so dark gradients don’t band, then H.264 and VP9; each file’s URL carries a content stamp, so a re-cut film is never served stale. A poster frame for each.
- Endings hand off rather than cut: a film into its still, footage into a live halftone, dots into a mark.
- Vignettes are soft and shaped by the footage, never an oval.
- Legibility comes first: anything behind text is checked against the brightest frame.
- Testable: heavy canvases expose a seek hook (
qaSeek(t)) that renders one exact moment, paused, for tests and storyboards.
Accessibility
Reduced motion
When a visitor’s system asks for reduced motion, every effect has a still alternative. One switch in the tokens zeroes blur, travel and stagger site-wide.
| Effect | Under reduced motion |
|---|---|
| Text reveal | Blur, travel and stagger go to zero; text fades in over 0.15s, whole. |
| Smooth scroll | Off: the browser scrolls natively. |
| Intro curtain and headline rotator | The curtain never plays; the rotator shows its last word. |
| Backgrounds | A finished still: the last frame of a story, not the first; a loop holds one of its frames. |
| Films | The poster frame; no autoplay. |
| Accordions and the mark | Open and close instantly; state still changes and is announced. |
| Scroll-driven sequences | Shown in their end state. |
Engineering
Performance
Motion is only cinematic if it’s smooth.
- Animate transform, opacity and filter only; never layout properties per frame (the accordion’s grid-rows height is the one exception).
will-changeis set for a tween and released the moment it ends.- Blur is capped at 42 pieces per reveal; past that, coarser pieces.
- Canvases and films stop drawing off-screen (IntersectionObserver), when the tab is hidden and while the Work overlay is open: the page under it is held, so nothing draws behind the overlay.
- Every animation that can start two ways guards itself with a flag, so it never plays twice.
- A script that rebuilds animations kills only the tweens it made, never every ScrollTrigger on the page.
Code
Tokens
The motion block of packages/tokens/tokens.css, printed from the file itself, with the reduced-motion switch.
:root {
/* ---- Motion ---------------------------------------------------------
Motion is tokenized exactly like color. Change the feel of every
reveal on every site from these values. */
--m-dur-fast: 0.25s;
--m-dur: 0.9s; /* the reveal itself */
--m-dur-slow: 1.4s;
--m-stagger: 0.045s; /* per word */
--m-stagger-ch: 0.018s; /* per character (hero only) */
--m-blur: 10px; /* measured off fantasy.co */
--m-shift: 4rem; /* measured off fantasy.co */
--m-shift-y: 1.25rem;
--m-ease: cubic-bezier(0.16, 1, 0.3, 1); /* expo-out */
--m-ease-soft: cubic-bezier(0.33, 1, 0.68, 1);
}
/* One switch kills the whole motion layer. Blur + translate + smooth
scroll is a vestibular trigger stack; this is not optional. */
@media (prefers-reduced-motion: reduce) {
:root {
--m-dur: 0.15s;
--m-dur-slow: 0.15s;
--m-stagger: 0s;
--m-stagger-ch: 0s;
--m-blur: 0px;
--m-shift: 0px;
--m-shift-y: 0px;
}
}
More about this site