Quoin design

Motion

Motion has one job — make state changes legible without turning a letterpress proof room into a demo reel.

Motion in Quoin uses the animation hooks of Base UI (data-open/data-closed, data-starting-style/data-ending-style, data-instant). Quoin does not invent a parallel animation system. It styles the states that Base UI already exposes.

Principles

The desk is quiet. The one number that matters, the interval or the verdict, can then be read. Nothing around it competes with it.

Never animate these:

  • The words of the verdict sentence. Do not type them on. Do not fade them in word by word. A sentence appears once, fully formed, the way a printed fact appears.
  • A number that a reader reads many times (flag list rollout %, table cells, the top bar), on every re-render. The count-up is for one first-reveal moment only. Never use it on a refresh.
  • Anything in a table row or list, other than a plain crossfade on insert or remove.
  • Sidebar navigation. It is instant on click. An icon hover is allowed. A route transition is not allowed.
  • Crop marks and locked-block chrome. These are print artifacts, not UI feedback.
  • Keyboard-triggered actions (command palette, shortcut toggles). They are instant or near-instant only.

May animate these:

  • State transitions that are otherwise abrupt (a rule saved, a dialog that opens, a toast that arrives)
  • Spatial consistency (popovers scale from their trigger, drawers slide from their edge, tab underlines slide and do not jump)
  • The five signature moments below
  • Micro feedback on direct manipulation (button press, switch thumb, slider thumb)

The motion budget is like ink. Spend it with care:

  • Use one entrance for each container.
  • Loop only the skeleton shimmer and the toast countdown ring.
  • Give each list one kind of motion at most.
  • If a reader touches an element 20+ times in a session (tabs, toggle groups, switches), use the fast end of its range. Use no spring bounce.

Motion tokens

Durations, easing curves and distances are real tokens. They come from @quoin/tokens/motion.css. See Brand and tokens for the live values. The token families:

Token familyValuesUsed for
--duration-*instant (0ms) → signature (700ms)from keyboard actions to the five signature moments
--ease-*enter, exit, move, emphasis, pressentrances, exits, movement on the screen, one-off deliberate beats, press feedback
--distance-*xs (4px) → lg (100%)translate offsets. Never a width/left/top animation
--scale-*press (0.97), enter-from (0.95), hover (1.02)press feedback, popup entrances, hover lift
--stagger-step40msthe tick marks of the ruler. They are the only element in the system that staggers

Every duration token has a prefers-reduced-motion: reduce override that shortens it. The override never sets a hard zero, except for decorative loops. Every per-component spec below states its reduced variant explicitly. The specs do not depend on a single global override to carry the meaning.

Per-component motion

This is a sample of the specs. docs/04-motion.md has all of them:

  • Button / row / chip — press. scale(1) → scale(var(--scale-press)), --duration-fast, --ease-press.
  • Popover / Menu / Select / Tooltip. Scale and fade from the trigger, with the origin at the trigger, --duration-base open / --duration-fast close.
  • Dialog + backdrop. Fade and a small translateY settle, --duration-slow, --ease-enter.
  • Toast stack. Slides in --distance-sm. The newest toast stacks on top. The undo countdown ring is the one other element in the system that can loop, --ease-linear.
  • CopyChip "copied" feedback. A brief label swap and a --distance-xs lift, --duration-base.

The five signature moments

These are the only places where Quoin spends real motion budget. Each one happens rarely (once for each experiment, once for each lock, once for each session). Each one is the brand. None is decoration on top of it.

1. The ruler drawing in

This motion plays on the first load of a concluded or running report (~700ms total, --duration-signature). The steps are:

  • The zero line draws from left to right with stroke-dashoffset.
  • The interval bracket grows outward from the position of the estimate. It never grows from an edge.
  • The tick marks stagger in at --stagger-step.
  • When the interval stops growing, the dashed target and the shaded harm zone fade in.
  • The estimate dot settles with a small --spring-settle beat (bounce 0). The beat says "this is the number, now read the sentence below."

With reduced motion, the whole assembled ruler fades in once. There is no drawing and no dot-scale beat. The number stays legible as static SVG in both cases.

2. Verdict sentence reveal

The container of the sentence fades in and settles --distance-xs upward. The motion starts when the dot of the ruler stops settling. The reader then understands "the ruler finished, here's what it means." A broken or harmed verdict gets one slow (--duration-slower) background wash to negative-subtle. Bad news gets a slower treatment, never a louder one.

3. The stamp — experiment concluded

A rubber stamp comes down and presses the decision ("shipped" / "rolled back" / "inconclusive") onto the report. This is the one place where the rubber-stamp capitals of Quoin appear. It is also the one place in the system where a slight rotational overshoot (±2°) is allowed. The overshoot is allowed because it happens once in the lifecycle of an experiment. With reduced motion, the stamp does not descend or rotate. The stamp only fades in at rest.

4. Number impact ticking

The headline lift number counts up from 0 to its true value on the first reveal only. It never counts up on a re-render. The count-up takes --duration-signature at most, for any magnitude. The number uses tabular-nums, so the digits do not shift the layout. With reduced motion, the page shows the final value immediately. Nothing is lost, because the number is equally informative when it is still.

5. Locked pre-registration block

The block washes from --color-surface to --color-surface-2. Its lock icon crossfades from open to closed. A 1px border-color shift lands at the end. There is no scale and no shake. The job of the moment is "this is now solid," and the block shows solidity when it does not move.

Kill-switch confirm — deliberately the least animated moment

A destructive, high-stakes action gets an opacity-only entrance. It has no scale and no color-wash flourish. A kill switch gets clarity, not delight.

Marketing hero

The landing page reuses the ruler-drawing-in sequence at half intensity. It has no tick stagger and no dot-settle spring. It plays once, when the sequence scrolls into view. There is no parallax and no looping background motion. The "printed" mood is the answer of Quoin to a category full of looping dashboard hero videos.

On this page