Motion

VitNode's motion rules - instant press feedback, the ease-fluid curve, interruptible transitions, critically damped springs, trigger-aware origins and reduced motion, shown live.

Motion in VitNode answers one question: where did that come from, and where did it go? Everything reacts on press, moves along a real path and can change its mind halfway. Nothing bounces for fun.

Respond on press

A press shows up in under 150ms, or it feels broken. Button dips to active:scale-97; tiny targets like Checkbox go further, to scale-90, so the dip is still visible under a thumb.

Easing

ease-fluid starts at full speed and spends its time settling, so the UI looks like it moved the instant you asked. ease-in waits before reacting, which feels like lag. linear is for progress bars and spinners only.

--ease-fluid: cubic-bezier(0.22, 1, 0.36, 1);

Timing

WhatEnterExit
Tooltip, popover, menu150ms100ms
Dialog, alert dialog200ms150ms
Sheet, drawer, big panels300ms200ms
Press feedback, hover, toggles≤ 150ms≤ 150ms

Exits are shorter than entrances: once you're done with something, it should get out of the way.

Interruptible

A transition always starts from wherever the element is right now, so it can reverse mid-flight. A keyframe animation restarts from frame one. VitNode overlays use transitions keyed on Base UI's data-starting-style and data-ending-style instead of @keyframes.

<div className="ease-fluid transition-[opacity,scale] duration-150 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 data-ending-style:duration-100 motion-reduce:transition-none" />

Springs

When a component needs a spring (Switch, ToggleGroup, Combobox chips), it uses motion/react with a critically damped spring: fast, no wobble. Save bounce for when a gesture carried momentum into the release, like flinging a drawer away.

const SPRING = { type: 'spring', duration: 0.3, bounce: 0 } as const

<m.span layout transition={shouldReduceMotion ? { duration: 0 } : SPRING} />

Grow from the trigger

Popovers, menus and tooltips scale out of their trigger with origin-(--transform-origin), which Base UI sets from the side and alignment. Leave and return along the same path: a sheet slides back to the edge it came from.

Reduced motion

With reduced motion on, slides become crossfades and springs run with duration: 0. Fading is fine, travel isn't. CSS gets motion-reduce:, motion code reads useReducedMotion(), and frosted surfaces drop their blur with reduced-transparency:.

<div className="bg-background/80 backdrop-blur-md motion-reduce:transition-none reduced-transparency:bg-background reduced-transparency:backdrop-blur-none" />