Motion
Motion shows where something came from or where it went. If it teaches nothing, Graphite does not animate it. Ten tokens from the kit’s Motion collection carry every timing, and fifteen components name them in their contracts. Play with the demos: each runs on the real tokens.
The race
Four dots, one track, one duration. Snap the slider to any of the five duration tokens and press Go.
duration-slow · 300ms
- GraphiteLinear Progress only
- NeverIn Never: it starts late and arrives at full speed
- GraphiteOut Enter and exit
- GraphiteIn-out Already on screen, moving across it
Every dot crosses the same track in the same time. Only the curve differs. In spends its start doing nothing, which reads as lag, then arrives at full speed, which reads as a crash. Graphite never uses it.
Feel it
Four cards arrive, one curve each. Pick the one that feels fastest.
Play them, pick one, then reveal.
The hundredth time
Anything opened by keyboard or used many times a session appears instantly.
Open it, close it, open it again. Then turn the animation off.
Opened 0 times · 0.0 s spent waiting for it
A 300ms open is charming once. A command menu is opened from the keyboard, many times a session, so Graphite opens it at once and the waiting stops climbing.
Where did that come from?
Things enter from their source: a popover from its trigger, a sheet from the bottom edge, a toast from the bottom.
The panel grows from the point it hangs from. Open it a few times.
Same panel, same timing, origin at the centre of the screen. Feel the eye jump.
Catch it
Anything the reader drags can be caught mid-motion. Drag the sheet, fling it, and grab it on its way back.
Drag me down. Fling me. Grab me while I come back.
Try to grab me while I move. You can’t: I ignore you until I finish.
Tooltip runway
Tooltips wait on first hover, then show instantly while the pointer moves between targets.
Run the pointer along the toolbar. The first label waits half a second; the rest are instant, because you are already reading.
Every label waits the full half second, every time. Reading the toolbar becomes a chore.
Confirmation
An exit can carry a short confirmation. The picked item flashes, then the menu closes.
Pick an action. The row you chose flashes before the menu closes.
Pick an action. The menu just vanishes, and leaves you wondering what you hit.
One at a time
One toast at a time, bottom only. The rest wait their turn.
Five toasts, one at a time, at the bottom. Each waits its turn.
Five at once, piled over the page. Which one mattered?
Reduced motion
Movement becomes a fade or nothing. Feedback stays. Turn the simulation on and go back through the demos.
Your system setting is no preference. With either, every demo above keeps its feedback and drops its movement: rises become fades, the race dots jump, the tabs indicator jumps, and color changes still ease. Nothing here needed its own code for it. It is one layer in the stylesheet, and a multiplier, --graphite-motion-travel, that every distance is written through.
Tokens
The kit's ten, with the components whose contracts name each one. Copy the CSS variable or the Tailwind class.
--graphite-motion-duration-instant0ms--graphite-motion-duration-fast120msUsed by Accordion, Button, Data table, Dropdown, Menu buttons, Menu, Modal, Notification, Popover, Tabs, Toast, Toggle, Tooltip
--graphite-motion-duration-base200msUsed by Accordion, Menu, Modal, Notification, Popover, Progress bar, Toast
--graphite-motion-duration-slow300msUsed by Modal
--graphite-motion-duration-emphasis400msNo component names it yet.
--graphite-motion-delay-tooltip500msUsed by Tooltip
--graphite-motion-easing-outcubic-bezier(0.2, 0, 0, 1)Used by Accordion, Button, Data table, Dropdown, Menu buttons, Menu, Modal, Notification, Popover, Toast, Toggle, Tooltip
--graphite-motion-easing-in-outcubic-bezier(0.4, 0, 0.2, 1)--graphite-motion-easing-linearlinearUsed by File uploader, Modal, Progress bar
--graphite-motion-spring-sheetlinear(), a critically damped spring in 20 pointsNo component names it yet.
Two more live beside them in code only: --graphite-motion-indeterminate, the 1400ms period every loop shares, and --graphite-motion-travel, the multiplier the reduced-motion layer sets to 0. Figma cannot play the sheet spring, so the kit uses duration/slow in its place.
The rules
Quoted from the Motion contract (1.0.0). The kit's Read Me carries the same text.
Rules
- Motion shows where something came from or where it went. If it teaches nothing, don't animate it.
- Anything opened by keyboard or used many times a session appears instantly. Menus, command search, tabs, sidebar items, toggles.
- An exit can carry a short confirmation. The picked item flashes, then the menu closes.
- Things enter from their source. Popover from its trigger, sheet from the bottom edge, toast from the bottom.
- Enter and exit use Out. Things moving across the screen use In-out. Linear only for progress. Never ease in.
- Anything the user drags can be caught mid-motion.
- Most motion is 120 to 200ms. Nothing runs past 400ms.
- Tooltips wait on first hover, then show instantly while the pointer moves between targets.
- Reduced motion. Movement becomes a fade or nothing. Feedback stays.
- One toast at a time, bottom only. Overlays never stack on overlays.
- No bounce anywhere. Graphite is a tool.
Never
- Never write a raw duration, cubic-bezier() or keyword easing in a stylesheet. naming-check fails on it. A real exception carries
motion-allow: <reason>in the comment directly above. - Never ease in. An exit is Out at fast, not In.
- Never overshoot. No back curves, no springs with a damping ratio under 1.
- Never write a movement distance without
--graphite-motion-travel, or reduced motion cannot reach it. - Do not add a duration past emphasis. Raise it with the kit, which owns the scale.
Next steps
Where the tokens live, and the components that move.
- Tokens
Every foundation variable, read live from the page.
- Menu
Instant from the keyboard, a flash on the way out.
- Accessibility
Contrast and reduced motion, checked as a set.
