Motion

Okami moves the way a good receptionist does: once to welcome you, then only in response to you. Every motion, from a focus ring to a closing sidebar, times itself on one scale: the motion system of transitions.dev.

Rules

  • One orchestrated moment per page. On the chat page it is the venue hero handing over to the compact header; a new page earns exactly one such moment, deliberately chosen.
  • Everything else answers an action: a message rising in, a chip scaling under the finger, a focus ring settling. Nothing loops for decoration. The drifting sky is the single sanctioned exception, and it is imperceptible on purpose.
  • The scale below is the only timing there is. Pick a step by what the motion does, never by the nearest number: a dialog closing is duration-quick whatever it was before. No ad-hoc ms, no hand-rolled cubic-bezier.
  • Opening invites, closing gets out of the way: a surface opens on duration-fast and closes on duration-quick. Never delay a close, and never bounce one.
  • Choices glide. Where every item can be chosen, they sit close and they stay put — a menu, a select, the sidebar, tabs, a calendar's days — one wash travels to the nearest item instead of each item blinking on and off: Glide from shared/glide. Not on tables, sparse cards, lists that move, or anything a wrong click would hurt.
  • Animate on the compositor: transform and opacity, never a custom property, never a style read per frame. A blur is the one exception, and only on words (a headline's reveal): on chrome — the nav — it stuttered while the page loaded. The craft timer's ring is the other: one small SVG's stroke-dashoffset.
  • Every animation stills under prefers-reduced-motion. This is part of the definition of done, not an enhancement.

The recipes come from transitions.dev by Jakub Antalik, installed in the repository as the transitions-dev and transitions-polish skills. Ask for transitions review to audit a component against the scale.

Durations

Tailwind's duration-* scale. A bare transition is duration-quick and ease-smooth-out.

  • duration-stagger
  • duration-micro
  • duration-quick
  • duration-fast
  • duration-medium
  • duration-slow
  • duration-very-slow
TokenValueTailwindFor
--duration-stagger40msduration-staggerThe offset between items entering one after another.
--duration-micro80msduration-microA tooltip's intent delay, a shake segment, a stagger of few large items.
--duration-quick150msduration-quickClosing a dropdown or dialog, swapping text, a tooltip appearing. A bare transition.
--duration-fast250msduration-fastOpening a dropdown or dialog, swapping an icon, sliding tabs or pages.
--duration-medium350msduration-mediumClosing a panel, a toast leaving.
--duration-slow400msduration-slowOpening a panel, revealing loaded content, clearing an input.
--duration-very-slow500msduration-very-slowEmphasis: a badge appearing, a text reveal, a success check.

Easings

Tailwind's ease-* scale. Smooth-out is the default; the others each carry their own intent. Played here on duration-very-slow.

  • ease-smooth-out
  • ease-in-out
  • ease-out
  • ease-linear
  • ease-bounce
  • ease-bounce-strong
TokenValueTailwindFor
--ease-smooth-outcubic-bezier(0.22, 1, 0.36, 1)ease-smooth-outThe default. Surfaces opening and closing, slides, resizes, anything changing position.
--ease-in-outease-in-outease-in-outSwaps in place: icons, text, a reveal, a skeleton giving way.
--ease-outease-outease-outTooltips, in and out.
--ease-linearlinearease-linearLoops that never settle: a shimmer, a pulse, a spinner.
--ease-bouncecubic-bezier(0.34, 1.36, 0.64, 1)ease-bounceA small pop on entering, like a badge. Never on a close.
--ease-bounce-strongcubic-bezier(0.34, 3.85, 0.64, 1)ease-bounce-strongA springy return when the pointer leaves a row it lifted.

Distance, scale and blur

Where a motion starts from; it always settles at rest. Read them as var(--…) in a transition or keyframes.

TokenValueFor
--distance-micro4pxText swapping in place.
--distance-small6pxAn error shake's small segment.
--distance-base8pxA page slide, a badge's reveal, an error shake's large segment.
--distance-medium12pxLines of text rising in.
--distance-large30pxA one-off celebration, like a success badge.
TokenValueFor
--scale-large0.96A dialog opening and closing.
--scale-medium0.97A dropdown opening.
--scale-small0.98A tooltip opening.
--scale-tiny0.99A dropdown closing.
TokenValueFor
--blur-small2pxA panel revealing, an icon or text swapping, a number popping in.
--blur-medium3pxA page slide, text rising in.
--blur-large8pxA success check arriving.

Which motion, where

ElementMotionTiming
Input, textarea, code slot: focusThe ring settles inquick · smooth-out
Button, chip: hover and pressLift in, springier settle outquick · smooth-out
SwitchToggle: the thumb travels with a double bouncemedium · bounce
Sidebar items, select options, calendar days: hoverGlide: one wash travels to the nearest item, keys or mouse; appears in place firstquick · smooth-out · fades in place when motion is reduced
Sidebar: the current pageGlide: the current wash slides to the next pagefast · smooth-out, as tabs slide
Popover, select, menuMenu dropdown: grows from its triggerfast open · quick close · scale medium → tiny
DialogModal: scales up, softer downfast open · quick close · scale large
Sidebar, sheet, fullscreen menuPanel reveal: slides in with a cross-blurslow open · medium close · blur small
ToastToast: rises from below, slower in than outmedium · smooth-out · blur small
Field errorError state shakemicro segments · distance small → base
Copy, send, any icon that changesIcon swap: cross-fade with blur and scalefast · in-out · blur small
Text that changes in placeText states swapquick · in-out · distance micro
Messages, headlines, step titlesTexts reveal: a staggered blurred risevery slow · distance medium · stagger
Okami thinkingThinking states: a shimmering status linelinear loop · quick swap
Booked, saved, doneSuccess checkvery slow · blur large
A full-screen chat, a phone's keyboard coming and goingKeyboard: the page makes room as the keyboard slides, from the tapfast · smooth-out · touch screens only
Days, steps, list ↔ detailPage side-by-sidefast · distance base · blur medium

The standard entrance

Rises 12px through a soft blur, settles in 500ms
/* The texts reveal: animate-text-reveal (or .aura-rise on the aura) —
   a 12px blurred rise on duration-very-slow, smooth-out. */
<h1 className="motion-safe:animate-text-reveal">…</h1>
/* stagger a load sequence in steps of --duration-micro */
<p className="motion-safe:animate-text-reveal animate-delay-micro">…</p>

The landing's arrival

The landing's one orchestrated moment: the nav rises in piece by piece, then the headline sweeps in line by line, then the subhead, faster, then the way in — about 1.7s. The words and the way in run on one clock, held until the page runs, so nothing shows before the headline. Replay it here; the pieces are on BlurReveal.

Your craft, uninterrupted

Okami answers every message and call, books your clients, and brings them back — while you stay with the one in your chair.

ElementMotionTimingCode
Nav: wordmark, links, CTA, menuEach rises in (opacity, 6px), one after another, on loadvery-slow · smooth-out, a micro step apartblur-in, --blur-in-step
Hero headlineLetter by letter out of a 12px blur along each line, line after linevery-slow a letter, a line every mediumBlurReveal by="line" pace="duration-medium"
Hero subheadThe same, once the headline's two lines are ina line every quick, after 2 × mediumBlurReveal pace="duration-quick" wait
Hero's way inRises in once the words are in, on their clockvery-slow · smooth-out, after 2 × medium + 3 × quickRiseIn
Hero washThe ball's colours laid flat, running into each other, drifting through the mixes; fades in over its own first framea mix every 5 × very-slow, in-out; the fade slowGradientBall shape="field", HeroWash
Conversation cardsA new card rises from the foot, pushing the rest up; the oldest dissolve into the stack's eased top; a finished exchange fadesmedium · smooth-out; the fade 700ms (paced to the exchange)HeroStage, mask-stack
Craft rowGlides the craft on to the middle, round and round; the rest at 65%very-slow · smooth-out; the fade quickCraftRow (three copies)
Craft timerThe chosen tile's ring traces once round as its conversation runslinear, the conversation's lengthPeopleRow trace, trace-ring
/* The nav: each piece a --duration-micro step after the last. */
<li className="blur-in" style={{ "--blur-in-step": 2 }}>…</li>

/* The hero: the headline, then the subhead, then the way in. */
<h1><BlurReveal by="line" pace="duration-medium">{headline}</BlurReveal></h1>
<p>
  <BlurReveal by="line" pace="duration-quick" wait="calc(var(--duration-medium) * 2)">
    {subhead}
  </BlurReveal>
</p>
<RiseIn after="calc(var(--duration-medium) * 2 + var(--duration-quick) * 3)">
  <ButtonLink href="#early-access">{cta}</ButtonLink>
</RiseIn>

Waiting

  • Waiting is shown by the thinking orb, not a spinner: Okami's dots bloomed into a slowly turning sphere of glass. Spinners are a last resort for the dashboard's true loading states.