Component patterns

Every component has a motion vocabulary. Most ship without one.

Buttons

Scale to 0.97 on :active. 50-80ms, ease-out.

No springs on buttons in product UI. No background-color animation on press. No transition on disabled states.

Popovers and dropdowns

Enter: Scale 0.9 to 1, opacity 0 to 1. 150-200ms. Transform-origin: edge nearest the trigger.

Exit: Opacity to 0, scale to 0.95. 100-150ms. Faster than entrance.

Don't use translateY for popovers. Scale from the trigger point. Do use translateY for nav dropdowns, where "dropping down" is the right metaphor.

Tooltips

Enter: Opacity 0 to 1, scale 0.85 to 1. 100-150ms. Slight translateY (4px toward trigger).

Exit: Opacity to 0. 75-100ms. No scale on exit.

Enter delay: 300-500ms prevents flickering across toolbars. Once one tooltip is visible, the next appears instantly.

Drawers and sheets

translateX (side drawers) or translateY (bottom sheets).

Enter: 250-350ms. cubic-bezier(0.32, 0.72, 0, 1) or spring { stiffness: 400, damping: 40 }.

Exit: 200-250ms. cubic-bezier(0.4, 0, 1, 1).

Pair with a backdrop at opacity: 0.5. Don't scale drawers. A drawer slides on one rail.

Modals and dialogs

Enter: Scale 0.95 to 1, opacity 0 to 1. 200-300ms. translateY 8-12px upward. Transform-origin: centre or trigger position.

Exit: Opacity to 0, scale to 0.95. 150-200ms. Slight translateY downward.

Toasts and notifications

Enter: Translate from off-screen. 200-250ms. Spring { stiffness: 400, damping: 30 }.

Exit: Translate back or fade with scale to 0.95. 150-200ms.

Slides in from the edge it lives on. Stagger multiple toasts with layout animations.

Hover effects with CSS

transform and box-shadow only. Don't scale cards above 1.02. For list items, transition: background-color 100ms ease beats a transform.

Text state swaps

Save. Saving. Saved. A three-phase swap: old text exits, container holds, new text enters.

mode="wait" finishes the exit before starting the enter. Without it, both labels render simultaneously.

Error shake

Decaying oscillation, 400ms total. Pair with a red ring. One shake per error event.

Notification badge

Two-stage entrance: slide in, then pop to full size with overshoot.

times controls where each keyframe lands: start at 0.8, overshoot to 1.15 at 60%, settle at 1.

Success checkmark

A static checkmark says "done." A drawn checkmark says "just now."

Circle first, checkmark 300ms later. Add slight rotation (-10 to 0 degrees) and it feels written rather than displayed.