Bevel

Get started

Motion

Why things move the way they do, and how to keep it fast.

Physical objects move with weight: a key gives under a finger, a card slides, a needle settles with a little overshoot. Bevel gets that from CSS transitions and a handful of tokens.

Durations by purpose

  • --bevel-duration-press (100ms): a control giving under a finger.
  • --bevel-duration-small (200ms): hover lifts, small state changes.
  • --bevel-duration-move (300ms): objects moving: cards, flaps, levers.
  • --bevel-duration-ambient (520ms): whole-scene changes, needles, motors.

Pick the duration by what's happening, not by feel. Curves: --bevel-ease-out for things arriving, --bevel-ease-in-out for things travelling, and --bevel-ease-spring, a CSS linear() spring with a slight overshoot, for things that settle.

Only the compositor moves

Components animate transform, translate, rotate, scale, opacity and filter. They never animate height, width, top, left, margins or padding. Animating layout reflows the whole page every frame: adding a card to a wallet cost 678ms of main thread work with a height transition and 94ms without.

Objects keep their size; their contents move
<span
  className="transition-[translate] duration-(--bevel-duration-ambient) ease-(--bevel-ease-in-out)"
  style={{ translate: `0 calc(var(--motorized-fader-travel) * ${-t})` }}
/>

When JavaScript moves things

Only for gestures with velocity, like swiping a keycard. Everything else is a CSS transition between states, which also makes it interruptible for free.

Every transition includes motion-reduce:transition-none. With reduced motion, things change state immediately, or crossfade where a jump would be lost.