Animation

Motion design tokens, easing curves, duration scales, and patterns for building animations that feel responsive, consistent, and purposeful.

Principles

Three core beliefs guide every animation decision in the design system.

Motion serves clarity

Every animation must make the interface easier to understand. In a business continuity platform, users are often under pressure. Motion should reduce cognitive load, not add to it.

Invisible correctness compounds

Most animation details go unnoticed. That is the goal. When a component animates exactly as expected, users proceed without friction. The aggregate creates an interface people trust.

Consistency over novelty

Use the same easing curves, durations, and patterns across the entire system. A popover in the risk matrix should feel identical to a popover in the recovery plan.

Duration tokens

Three duration tokens cover all standard UI transitions. Click any row to preview the timing.

TokenValuePreview
--transition-fast150ms
--transition-normal200ms
--transition-slow300ms

Duration by element

ElementDuration
Button press feedback100–160ms
Tooltips, small popovers125–200ms
Dropdowns, selects150–250ms
Modals, drawers, sheets200–500ms

Easing curves

Custom easing curves that feel more intentional than browser defaults. Click any curve to see it in motion.

ease-out
ease-in-out
ease-drawer
ease (default)
linear
CurveValueWhen to use
--ease-outcubic-bezier(0.23, 1, 0.32, 1)Element entering, exiting, or responding to user action
--ease-in-outcubic-bezier(0.77, 0, 0.175, 1)Element moving or morphing on screen
--ease-drawercubic-bezier(0.32, 0.72, 0, 1)Drawer or sheet animations
easeeaseHover states, color transitions
linearlinearSpinners, progress bars, marquees

Decision framework

Before writing animation code, answer: should this animate at all? Use frequency of interaction as the primary signal.

Interaction frequencyExampleDecision
100+ times/dayKeyboard shortcuts, command paletteNo animation
Tens of times/dayHover effects, list navigationRemove or drastically reduce
OccasionalModals, drawers, toastsStandard animation
Rare / first-timeOnboarding, celebrationsCan add delight

Interactive demos

Try these patterns to feel the difference good animation makes.

Button press feedback — scale(0.97) on :active
scale(0) vs scale(0.95) — never animate from nothing
Bad: scale(0)
Good: scale(0.95)
Stagger animation — 60ms delay between items
List item 1
List item 2
List item 3
List item 4

Patterns

Reusable animation patterns for common component behaviors.

Button press feedback

Add scale(0.97) on :active for instant feedback on any pressable element.

Button press feedback
.button {
  transition: transform 160ms var(--ease-out);
}
.button:active {
  transform: scale(0.97);
}

Never animate from scale(0)

Nothing in the real world disappears completely. Start from scale(0.95) with opacity.

Never animate from scale(0)
/* Bad */
.entering { transform: scale(0); }

/* Good */
.entering {
  transform: scale(0.95);
  opacity: 0;
}

Origin-aware popovers

Popovers scale from their trigger. Modals keep transform-origin: center.

Origin-aware popovers
.popover {
  transform-origin: var(--radix-popover-content-transform-origin);
}

Stagger animations

When multiple elements enter together, stagger by 30–80ms between items.

Stagger animations
.item {
  opacity: 0;
  transform: translateY(8px);
  animation: fadeIn 300ms var(--ease-out) forwards;
}
.item:nth-child(1) { animation-delay: 0ms; }
.item:nth-child(2) { animation-delay: 50ms; }
.item:nth-child(3) { animation-delay: 100ms; }

Entry with @starting-style

Modern CSS entry animation without JavaScript.

Entry with @starting-style
.toast {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 400ms ease, transform 400ms ease;

  @starting-style {
    opacity: 0;
    transform: translateY(100%);
  }
}

Asymmetric enter/exit

Deliberate actions are slow. System responses are fast.

Asymmetric enter/exit
/* Release: fast */
.overlay {
  transition: clip-path 200ms var(--ease-out);
}
/* Press: slow and deliberate */
.button:active .overlay {
  transition: clip-path 2s linear;
}

Performance

Rules to keep animations smooth at 60fps, even under load.

Only animate transform and opacity

These skip layout and paint, running on the GPU. Never animate padding, margin, height, or width.

Specify exact transition properties

Use "transition: transform 200ms ease-out" instead of "transition: all 300ms".

Prefer CSS animations over JS under load

CSS animations run off the main thread. JS animations drop frames when the browser is busy.

Gate hover animations

Use @media (hover: hover) and (pointer: fine) to prevent false hover on touch devices.

Respect prefers-reduced-motion

Reduced motion means fewer and gentler animations, not zero. Keep opacity and color transitions.

Review checklist

Common animation issues and their fixes. Use this when reviewing UI code.

BeforeAfterWhy
transition: all 300mstransition: transform 200ms var(--ease-out)Specify exact properties; avoid all
scale(0) entryscale(0.95); opacity: 0Nothing appears from nothing
ease-in on dropdownvar(--ease-out)ease-in feels sluggish
No :active on buttonscale(0.97) on :activeButtons must feel responsive
transform-origin: center on popoverRadix CSS variablePopovers scale from trigger
Duration > 300ms150–250msUI animations stay under 300ms
Hover without media query@media (hover: hover)Prevents false hover on touch
Keyframes on rapid elementCSS transitionsTransitions are interruptible

Guidelines

UI animations stay under 300ms

Faster animations make the entire app feel more responsive. A 180ms dropdown feels snappier than a 400ms one, even when the user can not articulate why.

Never animate keyboard-initiated actions

Keyboard shortcuts and command palette actions are repeated hundreds of times daily. Animation makes them feel slow and disconnected.

Use transitions for interruptible UI

CSS transitions retarget smoothly when interrupted. Keyframes restart from zero. For toasts, toggles, and rapidly-triggered elements, always prefer transitions.

Test animations with fresh eyes

Review animations the next day. Play them in slow motion or frame by frame to spot timing issues invisible at full speed.

Use this as a Claude skill

This page is also a Claude Code skill. The same guidance you read here is what the agent applies when it writes or reviews code — so the product stays consistent with what is documented.

fortiv-animation
How things move — easing, duration, and motion patterns

For everyone

Read this page before writing a marketing piece, a product screen, or any user-facing copy. It is the single source of truth for how Fortiv should look, feel, and sound.

For Claude Code

The skill lives at design/.claude/skills/animation.md and loads automatically when relevant. To invoke it explicitly, ask in plain language — for example:

Animate this popover following the animation skill
Review this transition against our animation guidelines
What easing and duration should this dropdown use?