Skip to content
Foundations

Motion

Motion is tokenised like color and type. Six durations, four curves, and nothing hand-typed in between — which is the whole point: a generated interface gives itself away withon everything, because that is what every generator reaches for by default.

Tune it

Every control below writes straight to the CSS variables on this document, so the components on this page — including the ones that render through portals — re-time as you drag. Nothing is saved; reload to get the shipped values back.

Duration

Six steps, no free numbers. Drag one and every component below re-times as you drag.

Easing

Drag either handle. The y axis runs past 1 on purpose — that is where overshoot lives.

ease-entrance

Anything arriving. Fast start, long settle.

cubic-bezier(0.22, 1.00, 0.36, 1.00)
ease-exit

Anything leaving. Accelerates away.

cubic-bezier(0.40, 0.00, 1.00, 1.00)
ease-move

Repositioning while staying on screen.

cubic-bezier(0.65, 0.00, 0.35, 1.00)
ease-overshoot

Dialog, popover, switch. Once per screen.

cubic-bezier(0.34, 1.35, 0.64, 1.00)

These are the values WunderUI ships with. Change something and the CSS to paste appears here.

Press feedbackduration-fast · ease-entrance
Tooltipduration-fast in · duration-instant out
Popoverduration-base · ease-overshoot in · ease-exit out
Dropdown menuduration-base in · duration-fast out
Dialogduration-slow · ease-overshoot in · ease-exit out
Sheetduration-deliberate in · duration-slow out
Switchduration-slow · ease-overshoot
Accordionduration-base · ease-entrance

Progressduration-ambient · ease-entrance
x

Using it in code

The duration steps are custom utilities, the easings are a Tailwind theme namespace. Both work with every variant, including the and states Base UI exposes.

<Dialog.Popup className="
  data-open:animate-in   data-open:fade-in-0   data-open:zoom-in-92
  data-open:duration-slow  data-open:ease-overshoot
  data-closed:animate-out  data-closed:fade-out-0
  data-closed:duration-fast data-closed:ease-exit
" />

Because the easing tokens are declared with plain rather than , each utility emits instead of a baked-in curve. Redefine the variable on any wrapper and everything inside it takes on that feel — one marketing section can breathe more slowly than the app around it without forking a single component.

Travel & scale

How far a thing moves matters as much as how long it takes. These are deliberately small.

TokenValueReach for it when
--travel-xs2pxTooltip — stays glued to its anchor
--travel-sm4pxPopover, dropdown, context menu
--travel-md8pxDialog, toast, inline panel
--travel-lg16pxSection reveal on scroll
--scale-press0.97Pressed button
--scale-enter0.96Dialog arriving

Rules

  1. 1
    Duration and easing are class names, not numbers

    duration-base, ease-entrance. Tailwind v4 has no --duration-* theme namespace, so WunderUI ships the scale as @utility rules that also set --tw-duration — which means they work on animate-in and animate-out, not just on transitions.

  2. 2
    Entrances decelerate, exits accelerate

    ease-entrance in, ease-exit out, and the exit is one step shorter on the duration scale. Never the entrance duration played backwards: the user already decided, and making them watch the decision play out is the most common motion mistake there is.

  3. 3
    Only transform and opacity

    Everything else goes through layout or paint. Height animates via grid-template-rows: 0fr → 1fr, never via height — that is the one trick that works in every browser and stays on the compositor.

  4. 4
    Restrain the travel

    Anything anchored to a trigger moves 2px or 4px. A menu that slides 20px does not look like it opened; it looks like it fell in.

  5. 5
    One overshoot per screen

    ease-overshoot is punctuation. Two of them in one view and the interface reads as a toy. A dialog overshoots; a sheet that fills half the viewport does not.

  6. 6
    Never transition-all

    Name the properties, or use bare transition, which covers color, shadow, transform and opacity but no layout property. transition-all animates things you never meant to animate, including on first paint.

  7. 7
    Reduced motion is opt-in, not a kill switch

    Wrap the animation in @media (prefers-reduced-motion: no-preference) so the still state is the default. Written the other way round, you only have to forget one selector for someone to get motion they asked not to have.

What your agent gets

Everything on this page is also on the machine-readable side. The motion scale, the curves, the rules above and a set of copy-paste recipes ship inside DESIGN.md and design.json, so an agent generating a component against WunderUI reaches for the same way it reaches for — and produces motion that matches the rest of the system instead of the default it was trained on.