Agentic Playbook

Motion presets

Twelve ways things are allowed to move here, each rendered live, so a name has something to point at.

Every number on this page is a token: three durations, three easings and two springs from the scale, and one stagger. A preset is a name for what may move, how far and on which of those, and the two decisions left are which preset and whether to animate at all. The page transition between the principles is the thirteenth, and the only one you have to leave this page to see.

The knobs under each stage retune it, on that stage alone, and the readout is the lines that would make the change permanent in the scale. Nothing here is remembered: a value worth keeping is worth committing. Then turn on reduced motion in your system settings and open each one again: every preset has a form without travel, and what it does then is part of what it is. The skill that owns this set, its rules and its check is mxa-motion.

rise, rise-group

One thing entering, or text and lists entering together. On the real page a group waits until it is scrolled into view, and rises once.

moves
opacity 0 → 1, translate 0.5rem → 0; per line or item, never per letter
default
base, ease-out; stagger token between items, counted to eight

Text arrives a line at a time,

never a letter at a time,

and the stagger is one token, forty milliseconds,

counted to eight and then no further,

so a long list lands within half a second

rather than a line a second later.

for scale.css
--duration-base: 200ms;
--motion-stagger: 40ms;
--motion-rise: 0.5rem;
--ease-out: var(--ease-out);

Last of eight lands at 480ms. The cap is slow + base, 520ms.

settle

A container changing size: a button growing a label, a card expanding. The difference is animated as a transform and the text inside is never scaled.

moves
the box, as a transform; content fades in after it has moved
default
spring-settle: damping 0.8, stiffness 380, settles in 443ms
a button growing a label

Judgement stays with people

for scale.css
/* spring "settle": damping ratio 0.8, stiffness 380; settles in 443ms, overshoots 1.5% */
--ease-spring-settle: linear(0, 0.0147, 0.0533, 0.1089, … 49 stops);
--duration-spring-settle: 443ms;
content fades in after 120ms (--duration-fast)

Copy gives the full 48-stop easing. Motion reads the same two numbers: stiffness 380, damping 31.2. Under 1 overshoots; 1 is critical and never does. Settles in 443ms.

settle, in width

Width and height changing in different kinds of element: a field that widens on focus, a row that grows as things are added, two panes sharing the room. None of them transitions a dimension.

moves
each box, as a transform; siblings shift as transforms too
default
spring-settle, the same numbers as above
width: a field that widens on focus
width: a row that grows as things are added
width and height: two panes sharing the room
for scale.css
/* spring "settle": damping ratio 0.8, stiffness 380; settles in 443ms, overshoots 1.5% */
--ease-spring-settle: linear(0, 0.0147, 0.0533, 0.1089, … 49 stops);
--duration-spring-settle: 443ms;

All three run on the settle spring, settling in 443ms. None of them transitions width or height: Motion measures before and after and animates the difference as a transform, which is why the text never stretches.

settle, for an indicator

A segmented control or tab bar. One element moves between the options rather than each option lighting up.

moves
one highlight, between positions
default
spring-snap: damping 0.9, stiffness 700, settles in 317ms
Stage
for scale.css
/* spring "snap": damping ratio 0.9, stiffness 700; settles in 317ms, overshoots 0.2% */
--ease-spring-snap: linear(0, 0.0138, 0.0496, 0.1006, … 49 stops);
--duration-spring-snap: 317ms;

The same spring drives pop, menus and tooltips. Settles in 317ms; Motion damping 47.6.

reveal-height

Disclosure, accordion, show more. The one layout transition in the set, bounded to base.

moves
grid rows 0fr → 1fr; content fades in after
default
base, ease-out; fade delayed by fast

The build compresses until it runs continuously underneath the work. The calendar does not follow it: an interview still takes an hour because a person is talking, and exploration takes as long as it takes to be surprised.

for scale.css
--duration-base: 200ms;
--duration-fast: 120ms;
--ease-out: var(--ease-out);

The box takes base; the content fades in after fast. The one layout transition in the set, which is why base is its ceiling.

flip-icon

A chevron turning over, a hamburger becoming a cross, a sort arrow. The icon starts and ends on screen, so it eases in and out.

moves
rotate, and for the hamburger a translate and a fade on the same clock
default
fast, ease-in-out
for scale.css
--duration-fast: 120ms;
--ease-in-out: var(--ease-in-out);

Ease-in-out, because the icon starts and ends on screen. A spring is available here and almost always wrong: an icon that overshoots looks broken, not lively.

press, hover

Every interactive thing, all the time. The two smallest presets, and the two that are already on everything.

moves
hover: colour only; press: scale 1 → 0.97, straight back on release
default
fast, ease-out; press returns instantly
hover changes colour; press gives a little and nothing more
for scale.css
--motion-press: 0.97;
--duration-fast: 120ms;

Hold a button down to judge the press. The return is instant on purpose; a press that eases back reads as a bounce.

swap

A skeleton giving way to content, a tab panel, an image loading. Same place, different content.

moves
a crossfade; the outgoing takes 2px of blur to hide the mismatch two layouts always have
default
base in, ease-out; fast out, ease-in
Loading
for scale.css
--duration-base: 200ms;   /* in */
--duration-fast: 120ms;   /* out */
blur on the outgoing: 2px

The outgoing leaves faster than the incoming arrives, always. The blur hides the pixel-level mismatch two layouts have; past about 3px it reads as an effect.

pop

A popover. Opens from the trigger's side, and Base UI's own attributes drive it: the class is the whole preset.

moves
opacity 0 → 1, scale 0.95 → 1 from the trigger's side
default
in on spring-snap; out fast, ease-in
for scale.css
/* spring "snap": damping ratio 0.9, stiffness 700; settles in 317ms, overshoots 0.2% */
--ease-spring-snap: linear(0, 0.0138, 0.0496, 0.1006, … 49 stops);
--duration-spring-snap: 317ms;
--duration-fast: 120ms;   /* out */
--motion-pop: 0.95;   /* scale it opens from */

Never from 0. A thing that appears from a point reads as a cartoon; under about 0.9 it starts to.

pop, as a tooltip

The smallest popup, so the same spring reads faster. The delay before it opens is the one number here that is not motion.

moves
the same as pop
default
the same as pop, after a delay
hover, or focus with the keyboard
for scale.css
/* spring "snap": damping ratio 0.9, stiffness 700; settles in 317ms, overshoots 0.2% */
--ease-spring-snap: linear(0, 0.0138, 0.0496, 0.1006, … 49 stops);
--duration-spring-snap: 317ms;
--duration-fast: 120ms;   /* out */
--motion-pop: 0.95;   /* scale it opens from */
delay before it opens: 600ms (a prop, not a token)

A tooltip is the smallest popup and moves the least: the same spring reads faster on a box this size. The delay is the one number that is not motion, and it is what makes tooltips bearable.

slide

A sheet or a drawer, and the page transition. One axis, and the backdrop only fades.

moves
translate along one axis; backdrop opacity
default
slow in, ease-out; base out, ease-in
for scale.css
--duration-slow: 320ms;   /* in */
--duration-base: 200ms;   /* out */
--ease-out: var(--ease-out);   /* in */
--ease-in: var(--ease-in);   /* out */

The sheet comes in on slow and leaves on base; the backdrop follows the same clock. Try spring-settle on the way in and ease-in on the way out, which is the pair most sheets end up with.

shake

An invalid field, a refused action. Three oscillations and done; it does nothing under reduced motion.

moves
translate ±0.25rem, three times
default
slow total, ease-in-out
for scale.css
--duration-slow: 320ms;
--motion-shake: 0.25rem;

Three oscillations whatever the duration; past about 0.5rem it stops being a refusal and becomes a tantrum.

tick

A number changing. Tabular figures so the line does not reflow as the digits do.

moves
the value, counted; the digits, not the layout
default
base, ease-out

31skills

for scale.css
--duration-base: 200ms;
--ease-out: var(--ease-out);

A spring here counts past the number and back, which is honest about the physics and wrong for a total.