Theme navigation

Motion

Motion

Motion is feedback, not decoration. Hulian UI defines easing and duration tokens so CSS utilities and motion-driven animation share the same feel.

First ask whether it should move

Decide by how often users see the action, not by how attractive the animation looks. Motion on frequent actions makes software feel slow.

FrequencyTypical scenarioGuidance
100+ times a dayCommand palette and keyboard shortcutsNo animation
Dozens of times a dayHover states and list navigationRemove or greatly reduce
OccasionallyDialogs, drawers, and toastsStandard motion
Rarely or onceOnboarding and success celebrationsA moment of delight is appropriate
The Command palette deliberately skips scale and uses only a 150ms fade. A frequent keyboard action should not wait for spatial motion.

Easing curves

Hover a track to compare its feel. Hulian UI replaces the weak default ease-out with a more decisive shared curve.

Deceleration (default)

ease-outcubic-bezier(0.16, 1, 0.3, 1)

Entrance, exit, hover, and press feedback. It starts at full speed, so users see an immediate response. Use this curve when unsure.

Accelerate, then decelerate

ease-in-outcubic-bezier(0.65, 0, 0.35, 1)

Movement or transformation within the viewport. Both ends are gentle, like a physical object starting and stopping.

Drawer (iOS and Ionic)

--ease-drawercubic-bezier(0.32, 0.72, 0, 1)

Full-screen sliding surfaces such as Drawer and ActionSheet. Its longer tail prevents large surfaces from stopping abruptly.

Avoid ease-in. It is slowest at the moment users pay the most attention, so it feels less responsive than ease-out at the same duration.

Duration

Keep interactive motion within 300ms. A 180ms menu feels much faster than a 400ms one.

TokenDurationUtilityUsed for
fast150msduration-150Press feedback and close micro-interactions (100-160ms)
base200msduration-200Overlay entrances and exits: Tooltip, Popover, Select, and Menu
slow300msduration-300Large transitions: Drawer and ActionSheet panels
entrance600msduration-600First-paint reveals and scroll entrances, not interaction feedback

Press feedback

Every pressable element should respond immediately so the interface acknowledges the action.

Scale ordinary controls to 0.97 on press and large cards to 0.99 so their children do not appear to jump.

// Component already using motion
import { pressable } from "@hulianui/ui";
<m.button whileTap={pressable.whileTap} transition={pressable.transition} />

// Pure CSS alternative without the motion runtime
import { pressableClass } from "@hulianui/ui";
<button className={cn("…", pressableClass)} />
pressableClass includes a complete transition-property list. Put it last in cn() and replace any existing transition-colors utility.

Overlays grow from their trigger

An anchored overlay should open from its trigger, not from its own center.

/* Base UI exposes the computed origin on the Positioner */
<Popup className="origin-[var(--transform-origin)] data-[starting-style]:scale-95 …" />
Dialog, AlertDialog, and Modal are not anchored to a trigger and should keep a centered origin. Do not enter from scale(0); scale(0.95) with opacity looks natural.

Accessibility

Reduced motion does not mean removing all feedback.

Under prefers-reduced-motion, preserve helpful opacity and color transitions while removing spatial movement.