Motion

signature · blur-at-rest
default curve · ease-out-cubic
Motion should read as confident restraint. Fast, quiet, purposeful. Nothing bounces. Nothing overshoots. The one exception is the signature link hover, a blur-to-focus that reads as clarity arriving.

Duration scale

6 tokens · use by role
TokenWhere it livesDurationRelative speed · shared 3s cycle --dur-xsMicrofeedback · button press, tap state, focus ring120ms
--dur-smHover states · tooltip, link underline, small transforms200ms
--dur-mdUI transitions · panel open, tab switch, accordion300ms
--dur-lgPage-level · crossfade, modal, route transition600ms
--dur-xlNarrative · section reveal, stat count-up, hero entrance900ms

Easing curves

3 total · never spring, never bounce
--ease-outcubic-bezier(0.25, 0.8, 0.25, 1)
default · UI arrives, then settles
--ease-in-outcubic-bezier(0.4, 0, 0.2, 1)
crossfades · A-to-B, no focal point
--ease-readcubic-bezier(0.2, 0.8, 0.2, 1)
text reveals · settles quickly, reads calm

Signature · blur-at-rest link

site-wide identity · do not rename
At rest · 0.8px blur · dotted border · 0.8 opacity
Build with conviction
hover to see the resolve
Transition · 350ms · ease-out · blur, opacity, border
.link { border-bottom: 1px dotted; opacity: 0.8; filter: blur(0.8px); transition: filter 350ms var(--ease-out), opacity 350ms var(--ease-out), border-color 350ms var(--ease-out); } .link:hover { filter: blur(0); opacity: 1; border-bottom: 1px solid; }

Primitives

hover any to trigger
Button lift · 1px Y · 200ms · ease-out
Stat count-up · 900ms · ease-out · tabular
0%
Text reveal · 500ms · ease-read · 60ms stagger
Predict the choice, not the word.
Page crossfade · 400ms · ease-in-out · opacity only
Before
After

Rules

enforced
01Never spring. Never bounce. Never overshoot.
02Default curve is ease-out. Only deviate with reason.
03If you can't justify motion > 600ms, cut it.
04Scroll-triggered reveals fire once. No replay on scroll-back.
05Respect prefers-reduced-motion; drop all non-essential motion.
06Color never animates alone. Pair with transform or opacity.
07Loading is not motion. Use a static mono "…" or a 1px progress rule.
08Stagger in groups of ≤6, ≤60ms apart. Beyond that, use a single reveal.

Rule 07 · Simulation running

1px rule · cursor blink · no spinner
SIMULATION · RUNNING
Stated · intent
"73% would buy"
Revealed · pending
——
Loading is not motion. One 1px ink rule fills left-to-right over 8s. Cursor blinks at 1s step-start. The revealed column resolves when the simulation completes — not before.