Skip to content
Brand guide

Chapter 08

Motion

Motion in this brand confirms that something happened. It never performs.

8.1 The principle

Since Labs is calm software for people who are busy. Animation is feedback, not personality. If a movement is there to be admired rather than to explain a change of state, cut it.

The test: would a person notice if this were instant? If the answer is no, make it instant. If the answer is "they would be confused about what just happened", that is what the motion is for.


8.2 The scale

TokenDurationEasingUse
Instant0msState that must read as a fact, not an animation
Fast120mscubic-bezier(0.2, 0, 0, 1)The default. Colour, opacity, border on hover and focus
Base200mssameDrawers, disclosure, position changes
Slow320mssameFull-surface transitions. The ceiling

Nothing in this brand animates longer than 320ms. One easing curve throughout — an ease-out that starts immediately and settles. No bounce, no elastic, no spring.


8.3 What animates

PropertyVerdict
color, background-color, border-color✅ The overwhelming majority of motion in the product
opacity✅ Fades, disclosure
transform: translate✅ Drawers and sheets only
box-shadow✅ Card hover, sparingly
width, height, top, left❌ Layout-thrashing. Use transform
rotate, scale❌ Except a loading spinner

Prefer transition-colors over transition-all. transition-all animates properties you did not intend and produces the jelly effect that makes an interface feel cheap.


8.4 Patterns

Hover — 120ms colour change. Never a size change; a control that grows under the cursor moves the thing you were about to click.

Focus — the ring appears instantly. Never fade a focus ring: a keyboard user tabbing quickly must see where they are without waiting.

Drawer / mobile sidebar — 200ms translateX, backdrop fades over the same duration.

Modal — appears instantly. It is a mode change, and a fade makes the whole app feel slow.

Loading — a 1s linear spin for a spinner. For content, prefer a skeleton in surface-mist with a 2s pulse over a spinner, and prefer nothing over a skeleton if the wait is under ~300ms.

Route change — nothing. The page changes. Page transitions are a personality we have not earned.


8.5 Reduced motion

Non-negotiable. Ships in tokens/since-brand.css:

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Anything driven by JavaScript must check the same signal:

const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

Reduced motion means the end state, immediately — never a missing state.


8.6 Do and don't

Do

  • ✅ 120ms colour transitions as the default
  • ✅ Instant focus rings and instant modals
  • ✅ One easing curve everywhere
  • ✅ Honour prefers-reduced-motion

Don't

  • transition-all
  • ❌ Animate anything past 320ms
  • ❌ Bounce, spring, or elastic easing
  • ❌ Scale a control on hover
  • ❌ Add a page transition

This chapter as plain markdown: /docs/motion.md