Motion
Motion shows what changed and where it went. Most of it lasts 150 to 300 milliseconds, and movement stops for readers who ask for less motion.
What motion is for
Animate a change the reader could otherwise miss: a menu opening from its button, a row arriving in a list, a panel sliding in from the edge it belongs to. Leave out motion that only decorates.
Keep frequent actions nearly still. A movement seen all day turns into a wait, so hover and focus change fill and ink, and a card lifts 1 px at most.
Nothing moves on the first paint. A page arrives complete, and motion starts when the reader acts or new content arrives.
Durations
Choose the duration by the size of the change. Small controls answer at once. Larger surfaces take longer, since they travel further.
No single movement lasts longer than 300 ms. A list whose rows arrive one after another takes longer as a whole, and a loop that shows work in progress keeps running.
--duration-instant100 ms--duration-fast150 ms--duration-base200 ms--duration-slow300 ms
- Instant: the pressed state, a tooltip leaving
- 100 ms
- --duration-instant
- Fast: hover, focus, fill and color changes
- 150 ms
- --duration-fast
- Base: menus, popovers, a new page, a card lifting
- 200 ms
- --duration-base
- Slow: dialogs, sheets, the header condensing, a section opening
- 300 ms
- --duration-slow
Easing
Use the soft curve for anything entering the screen: it starts fast and settles gently. Use the standard curve for movement within the screen and for anything leaving it, one duration step shorter than its entrance. A menu enters in 200 ms and leaves in 150.
Keep linear timing for rotation, as in the loading icon. Do not use a curve that overshoots its end.
--ease-standard--ease-soft- Standard
- cubic-bezier(0.4, 0, 0.2, 1)
- --ease-standard
- Soft, for entrances
- cubic-bezier(0, 0, 0.2, 1)
- --ease-soft
- Linear
- the rotation of the loading icon
Springs in WerkOS
Where a gesture drives the motion, WerkOS uses springs. Each one settles within a quarter of a second, with no visible overshoot. Where a spring cannot be used, take the nearest duration on the soft curve.
- The current item sliding in navigation
- 700, 38, 0.5
- A tap on a navigation item
- 560, 32, 0.6
- A value changing in place
- 480, 38, 0.9
What moves, and how
Menus, popovers and dialogs fade in and rise 8 px from where they open. They fade out in place.
A card rises 1 px on hover. A new page fades in and slides 20 px in 200 ms.
When rows arrive in a list after the page has loaded, they enter one after another, 45 ms apart after a 40 ms pause, each fading in and rising 8 px over 280 ms. Rows after the sixth arrive with it, so a long list does not trickle in.
Animate opacity and position. A size animates only when a section opens or closes in place. Switching between the light and dark theme is instant.
- Rise of a menu, a dialog or a new row
- 8 px
- Slide of a new page
- 20 px in 200 ms
- Pause, then step between rows
- 40 ms, then 45 ms
Less motion
When the reader’s system asks for reduced motion, only the loading icon keeps moving, since it is small and is the only sign of work in progress. Springs jump to their end, and the stagger and the page slide disappear. The WerkAI caret stops blinking.
Fades and color changes stay at 150 ms, so a menu still shows that it opened.
Motion is not the only signal. A saved change also says Saved, and new content is announced to screen readers.
- Movement
- stops, except the loading icon
- Fades and color changes
- 150 ms
- --duration-fast
- Blinking caret
- stops
Permitted and not permitted
Permitted
A menu that fades in and rises 8 px
It shows where it came from and is done in 200 ms.
A stagger on rows that arrive after the page
The eye follows where the new content lands.
A fade kept under reduced motion
The reader still sees that something opened.
Not permitted
A bounce or an overshoot
It delays the end state and pulls the eye for no reason.
A width that animates in a line of text
The whole line reflows while it moves.
Content that animates in on first load
The reader waits for a page that was already there.
A single movement longer than 300 ms
Past that the interface feels slow.