Speed1×
Slow motion multiplier
Reduced motion
Show
Semantic motion tokens
every control writes a real tokenThe same three jobs, one curve and six
run at 4× to judgeToday — --ease-standard for all three
Arriving
Leaving
Moving
Proposed — the curve names the job
Arriving —
--ease-entryLeaving —
--ease-exitMoving —
--ease-moveThe sheet needed its own curve because the standard one, measured on device, “put the sheet half off-screen in 59ms of a 410ms close and 90% off in 163ms — the travel is over before the eye has followed it”. That comment, already in the codebase, is the specification for
--ease-exit. Promoting it is not a new idea; it is the second time someone has needed it.Easing
Drag a control point and every demo in the lab moves with it. Arrow keys nudge; hold shift for a bigger step.--ease-entry
cubic-bezier(0.23, 1, 0.32, 1)
today —
Within a hair of the current --ease-standard, so this is a rename with a nudge and almost nothing shifts.--ease-standardproposed —
--ease-entry--ease-exit
cubic-bezier(0.3, 0, 0.6, 0.6)
today —
Not a proposal so much as a promotion: the sheet already authors this curve locally, with a comment measuring why the standard one would not do.Already in the code: --ease-standardproposed —
--ease-exitBottomSheet/BottomSheetPanel.tsx:172--ease-move
cubic-bezier(0.77, 0, 0.175, 1)
today —
Symmetric in-out. The thing is already on screen, so it needs no arrival accent — only a legible path.--ease-standardproposed —
--ease-move--ease-state
CSS keyword — nothing to tunetoday —
Symmetric and cheap. The sites that declare a duration and no curve are silently getting this already, so naming it changes nothing except that it becomes reviewable.Already in the code: --ease-standardproposed —
--ease-stateDURATION_WITHOUT_CURVE in the audit--ease-linear
CSS keyword — nothing to tunetoday —
Correct behaviour, wrong provenance: several sites hardcode it and most of them are right to. A token makes them lintable.Already in the code: --ease-standardproposed —
--ease-linearSpinner, DateInput wheel, BottomSheet scrim, Stepper--ease-drawer
cubic-bezier(0.32, 0.72, 0, 1)
today —
The iOS curve. Matches what BottomSheet and MobileNav are reaching for when they hand-tune sheet travel.--ease-standardproposed —
--ease-drawerDuration
Aliases onto the existing primitives, so a theme retunes them for free through the motion scale it already has.--duration-instant
newReplaces the spellings in use today. The 0.01s form is the common one and it is not arbitrary — a zero duration fires no transitionend, so anything sequencing off that event breaks. Decide 0 vs 0.01ms before the sweep.
--duration-press
fast-minpress
Inside the interface budget
Reference budget 100–160ms.--duration-state
faststate
Inside the interface budget
Reference budget 100–250ms.--duration-enter
fast-maxenter
Inside the interface budget
Reference budget 125–250ms.--duration-exit
fastexit
Inside the interface budget
Reference budget 100–250ms.Deliberately shorter than enter, because old content should leave faster than new content arrives.--duration-reveal
medium-minreveal
Overlays only above 300ms
Reference budget 200–350ms.--duration-overlay
mediumoverlay
Overlays only above 300ms
Reference budget 200–500ms.--duration-continuous
slowcontinuous
Outside every interface budget
Does not reach the loops that actually exist: StatusDot runs 2s and Chat 1.5s, both outside a scale that stops at 1300ms.Stagger
--stagger-tightDense lists, small items.
tight
--stagger-baseDefault group entrance.
base
--stagger-looseFew large items. Longer than this feels slow.
loose
The primitive scale, unchanged
Theme-generated and working. Nothing here moves — the semantic layer sits on top, which is what keeps the change non-breaking.| Primitive | Value | Semantic alias |
|---|---|---|
| --duration-fast-min | 130ms | --duration-press |
| --duration-fast | 175ms | --duration-state--duration-exit |
| --duration-fast-max | 230ms | --duration-enter |
| --duration-medium-min | 310ms | --duration-reveal |
| --duration-medium | 410ms | --duration-overlay |
| --duration-medium-max | 550ms | — |
| --duration-slow-min | 730ms | — |
| --duration-slow | 975ms | --duration-continuous |
| --duration-slow-max | 1300ms | — |
Where this lands
Not a:root block — core defines its tokens through StyleX, so the proposal is an addition to two existing objects.The export page emits this with whatever you have tuned. See Export tuning.@astryxdesign/core/src/theme/tokens.stylex.ts — tsexport const easeDefaults = {'--ease-standard': 'cubic-bezier(0.24, 1, 0.4, 1)', // kept, deprecated in docs'--ease-entry': "cubic-bezier(0.23, 1, 0.32, 1)",'--ease-exit': "cubic-bezier(0.3, 0, 0.6, 0.6)",'--ease-move': "cubic-bezier(0.77, 0, 0.175, 1)",'--ease-state': "ease",'--ease-linear': "linear",'--ease-drawer': "cubic-bezier(0.32, 0.72, 0, 1)",} as const;export const durationDefaults = {// the nine primitives stay exactly as they are...// semantic layer, aliased so a theme retunes both at once'--duration-instant': "0ms",'--duration-press': "130ms", // --duration-fast-min'--duration-state': "175ms", // --duration-fast'--duration-enter': "230ms", // --duration-fast-max'--duration-exit': "175ms", // --duration-fast'--duration-reveal': "310ms", // --duration-medium-min'--duration-overlay': "410ms", // --duration-medium'--duration-continuous': "975ms", // --duration-slow} as const;