Ship-It Designv0.0.3
GitHub

Motion

Motion is functional, not decorative. Two named durations, two easings, and a reduced-motion override that's enforced globally.

Durations#

TokenValueWhen
duration.micro150mshover, press, color, switch toggles, focus rings
duration.step360msdialog / drawer / sheet entrances, toast in/out

Hand-rolled values are not allowed. If you reach for 200ms or 300ms, use micro (150ms) or step (360ms) and the system stays consistent.

Easings#

ts
easing.out = 'cubic-bezier(.2, .7, .2, 1)'; // entrances — fast settle
easing.in = 'cubic-bezier(.4, .1, .8, .3)'; // exits — linger then fall

Reduced motion#

tokens.css injects:

css
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-micro: 0ms;
    --duration-step: 0ms;
  }
}

Components consuming the duration tokens (transition-[…] duration-(--duration-micro)) get this for free. Don't hand-write duration: 150ms — use the token, or your component will animate even when the user has asked it not to.

Animation library#

Keyframes live in packages/ui/src/styles/animations.css and are bundled through @ship-it-ui/ui/styles/globals.css:

KeyframeWhen
ship-spinSpinner
ship-pulsestreaming caret, pulsing dot
ship-pulse-ringlive indicator (radiating ring)
ship-indeterminateindeterminate Progress
ship-skeletonSkeleton
ship-fade-inoverlays
ship-pop-intooltips, popovers, dropdown content
ship-dialog-inDialog content
ship-slide-in-{right,left,bottom}Drawer, Sheet
ship-toast-inToast

Compose with the Tailwind arbitrary-property syntax:

tsx
<span className="animate-[ship-pop-in_140ms_var(--easing-out)]" />