Design
llms.txt

Desktop

Desktop motion

The desktop's shared springs, window and menu variants, and how they step down to fades under reduced motion and in performance mode.

Every desktop surface moves on the same few presets, so windows, menus, widgets and icons feel like one material: ui/motion/motion-presets.ts for windows, sheets and menus, and ui/desktop/desktop-motion.ts for the desktop, Finder and widgets, both on motion/react. Import them rather than writing new springs. They animate opacity and transforms only, so they stay on the compositor; nothing animates left, top, width or height. These sit beside the site-wide motion tokens (see Motion).

Springs and timings#

Preset Values For
springs.snappy Stiffness 560, damping 36, mass 0.7 Menus, buttons, the + panel: small things that should feel instant but alive
springs.window Stiffness 380, damping 32, mass 0.9 Windows opening and restoring, phone sheets settling
springs.gentle Stiffness 220, damping 28, mass 1 Larger, softer settles: widgets, panels, selection pops
easings.out cubic-bezier(0.16, 1, 0.3, 1) Anything arriving
easings.in cubic-bezier(0.4, 0, 1, 1) Anything leaving
easings.inOut cubic-bezier(0.65, 0, 0.35, 1) The minimize pour
durations instant 0, fast 120 ms, base 200 ms, slow 360 ms Fades, and the pour
SPRING_SNAPPY Stiffness 520, damping 34, mass 0.6 Hover, press, sliding highlights, the widget drawer
SPRING_SETTLE Stiffness 380, damping 30, mass 0.8 An icon settling into its cell after a drop or a Clean up
SPRING_SOFT Stiffness 260, damping 28, mass 0.9 Soft entrances: icons, widgets, panels, empty states
FADE_FAST 140 ms, cubic-bezier(0.32, 0.72, 0, 1) View switches, kept brief so keyboard users never wait

Windows#

windowVariants take the window's motion as custom: whether motion is reduced, where it grows from and where it minimizes to.

Moment Motion
Open Grows from where it was launched (the last pointer press, if within 1.2 s, or an icon's centre) from 55%, or from its own centre at 92% when opened from the keyboard, on springs.window; opacity in 120 ms
Close Scales to 94% and fades, 160 ms, easings.in
Minimize Pours into its WINDOWS entry: width 100 → 55 → 4%, height 100 → 80 → 4%, opacity 1 → 0.9 → 0, over 360 ms on easings.inOut, with the middle step at 55% of the time. Hidden only once it has played
Restore The pour in reverse, out of the same point
Focus The shadow deepens over 220 ms; an unfocused window's title dims to 62% over 180 ms
Phone sheet Slides up from the bottom edge on springs.window; leaves downwards in 220 ms
A reload Windows it reopens appear without an entrance; a frame remounted for a window already on screen does not grow in again

The main window pours the same way through the Web Animations API, from where it stood.

Preset Motion
popoverVariants From 94% and 2 px up to rest on springs.snappy, opacity in 100 ms, its entries staggered 14 ms apart after 20 ms; out to 97% with a 100 ms fade. Grows from the pointer, or from its row for a submenu
listItemVariants Each entry drops in 3 px and fades in over 140 ms (easings.out); out in 60 ms
fadeScale From 96% on springs.snappy; out to 98% in 120 ms
fadeOnly Opacity only: in 120 ms, out 80 ms. The reduced-motion fallback
staggerChildren(step, max) Children 20 ms apart by default, capped so long lists never crawl
The + panel Rises out of the + from 92% and 8 px lower on springs.snappy, its sections 20 ms apart; out to 96% in 100 ms
Widget drawer Slides in on SPRING_SNAPPY; away in 180 ms
WINDOWS entries Settle in from 6 px to the left over 260 ms

The desktop#

Moment Motion
Icons appear Rise 6 px from 86% on SPRING_SOFT, 25 ms apart, the delay capped at 300 ms
Widgets appear Rise 12 px from 97% on SPRING_SOFT, 45 ms apart, capped at 400 ms
A panel or empty state Rises 8 px from 98% on SPRING_SOFT
An item opens Its glyph pulses to 116% and back over 320 ms
Finder changes folder Going in zooms from 96.5%, going back from 103.5%; switching views cross-fades in place
Hover on tiles and widgets A 2 px lift over 200 ms
An icon is dropped It springs into its cell on SPRING_SETTLE

Reduced motion and performance mode#

  • useReducedMotionSafe() is true when the OS asks for reduced motion or the app runs in performance mode, which sets MotionConfig reducedMotion="always". Then every preset fades only, briefly (pickVariants swaps in fadeOnly): windows, sheets, menus and panels appear and leave by opacity.
  • Performance mode is the default; Full effects (Settings › Appearance › Visual effects) brings movement and the blur back (see Wallpaper & glass).
  • Under the OS setting, the CSS motion steps down too: window shadows and title dimming take 1 ms, WINDOWS entries fade in over 120 ms, and hover lifts and pressed lights stay still.
  • Keep information in the end state, never only in the animation.
  • The exceptions: teammates' live cursors keep their smoothing in performance mode (a cursor that jumps several times a second is hard to follow) and move only transforms; they snap to their latest place only when the OS asks for reduced motion. The World Clock's hands keep running in performance mode for the same reason, and stop under reduced motion.

Guidelines#

Use springs.window for anything window-sized, springs.snappy for menus and controls, and the reveal variants for what appears on the desktop.

Behaviour: motion follows the pointer (windows grow from the press that opened them, menus from the click, minimizing pours into the sidebar), so the member can always see where a thing came from and went.

Don't animate layout, write a spring of your own, play an entrance on reload, or carry meaning only in motion.