Search

Search components, tokens, patterns, architecture

Foundations

Motion

Decisive spring-like entries, fast exits, nothing bouncy. Motion communicates layer changes — a sheet rising, a calendar unfolding — never delight for its own sake.

Sheet rise — the signature curve

Every sheet enters with cubic-bezier(0.32, 0.72, 0, 1) over 0.48s: fast initial commitment, long gentle settle. Exits use the inverse curve with duration scaled to remaining travel — a half-dismissed sheet finishes quickly instead of replaying the full animation.

Micro-motion, live

Step dots: width 7→20px · 0.25s

Syncing…

Sync pulse: opacity 1→0.4 · 0.8s loop

syncSpin: 0.9s linear infinite

Specification

MotionSpecUsage
Sheet entersheetRise · 0.48s · cubic-bezier(0.32, 0.72, 0, 1)All bottom sheets and drawers
Sheet exitcubic-bezier(1, 0, 0.68, 0.28) · duration scaled to remaining distanceDismiss
Drag-to-dismissuseSheetDrag() · 12px commit thresholdAll sheets
CalendarcalExpand/calCollapse · 0.28s · cubic-bezier(0.34, 1.4, 0.64, 1) · origin top-rightMonth calendar panel
SyncsyncSpin 0.9s linear · pulse opacity 1→0.4Sync indicator
Toastfade/slide · auto-dismiss 2.4sSave confirmations
Step dotswidth 7→20px · 0.25sOnboarding progress
Assumption handoffsheet closes → target log screen opens after 320msExplainability → action

The 320ms handoff

When a user taps an assumption action inside a drawer, the sheet closes and the matching log screen opens after 320ms. The pause is deliberate: it lets the dismissal finish reading as “that surface answered me” before the next surface asks for input.

Accessibility

  • All animations are layer transitions or status indicators — none carry sole meaning, so prefers-reduced-motion can safely shorten them to fades.
  • Drag-to-dismiss (12px commit threshold) is always duplicated by an explicit close button and scrim tap.
  • Nothing autoplays on a loop except status indicators (sync), which pair with a text label.