← All posts

how tos · 6 min read

MOTION.md: adding animation tokens to your design system

DESIGN.md captures colors, type, and spacing. MOTION.md adds the missing layer: duration scales, easing curves, spring configs, and stagger patterns.

DESIGN.md has a well-established scope: colors, typography, spacing, radii, shadows, and occasionally brand voice. These are the static properties of a design system.

Motion is not static. It is the fourth dimension of your design system, and it does not fit cleanly into DESIGN.md without either expanding the file beyond its natural scope or compressing the motion system into a few underspecified lines.

MOTION.md is a separate file with a single responsibility: defining how your interface moves.

What belongs in MOTION.md

Duration scale. A named set of durations with intended uses:

## Duration scale

| Token       | Value | Use                                            |
|-------------|-------|------------------------------------------------|
| duration-xs | 100ms | Hover states, focus rings, immediate feedback  |
| duration-sm | 150ms | Tooltips, small dropdowns                      |
| duration-md | 200ms | Panels, menus, most transitions                |
| duration-lg | 350ms | Page transitions, modal entries                |
| duration-xl | 500ms | Onboarding, trust-signal moments               |

Not just values — intended uses. Without the use column, a developer picks a value that feels right to them, and you end up with six durations in a codebase instead of five.

Easing curves. Named presets with CSS values and, critically, descriptions of what each communicates:

## Easing

| Token          | CSS value                         | Character                       |
|----------------|-----------------------------------|---------------------------------|
| ease-standard  | cubic-bezier(0.4, 0, 0.2, 1)     | Balanced, general-purpose       |
| ease-out       | cubic-bezier(0, 0, 0.2, 1)       | Natural deceleration, arriving  |
| ease-in        | cubic-bezier(0.4, 0, 1, 1)       | Accelerating, departing         |
| ease-out-expo  | cubic-bezier(0.19, 1, 0.22, 1)   | Snappy, precision, tool feel    |
| ease-spring    | (use spring config instead)      | Physics-based, interactive      |

Spring configurations. Springs need more than a curve — they need mass, stiffness, and damping. Document them as named presets:

## Spring configs

interactive-drag:
  stiffness: 400
  damping: 30
  mass: 1
  restDelta: 0.001

panel-enter:
  stiffness: 300
  damping: 35
  mass: 0.8

list-reorder:
  stiffness: 500
  damping: 40
  mass: 0.6
  restSpeed: 0.5

These numbers are not arbitrary — they represent different interaction characters. interactive-drag is looser, responsive to velocity. panel-enter is firmer, controlled. list-reorder snaps quickly. Document the intent, not just the values.

Stagger patterns:

## Stagger

list-items: 20ms per item, max 400ms total
card-grid: 30ms per item, start after 50ms delay
navigation-items: 15ms per item

Cap the total stagger time explicitly. A 100-item list with a 20ms stagger runs for 2 seconds — that is a bug, not a feature.

Enter/exit patterns. Define the canonical motion behavior for common component types:

## Enter/exit patterns

Modal:
  enter: scale 0.95→1, opacity 0→1, ease-out, duration-lg
  exit: scale 1→0.95, opacity 1→0, ease-in, duration-md

Dropdown:
  enter: translateY -4px→0, opacity 0→1, ease-out, duration-sm
  exit: translateY 0→-4px, opacity 1→0, ease-in, duration-xs

Toast/notification:
  enter: translateX 100%→0, opacity 0→1, ease-out-expo, duration-md
  exit: opacity 1→0, ease-in, duration-xs

Panel (side):
  enter: translateX -100%→0, ease-out, duration-lg
  exit: translateX 0→-100%, ease-in, duration-md

This is where motion systems are most frequently inconsistent. Without explicit documentation, modals and dropdowns and toasts all get different enter/exit behavior from whoever wrote each component.

A minimal complete MOTION.md

# Motion system

## Philosophy
Motion should confirm and orient, not entertain. Prefer fast and controlled over
slow and elaborate. Interactive elements respond to input velocity. UI transitions
use fixed duration and easing.

## Duration scale
xs: 100ms — hover, focus
sm: 150ms — small reveals
md: 200ms — standard transitions
lg: 350ms — significant entries
xl: 500ms — deliberate, high-stakes moments

## Easing
standard: cubic-bezier(0.4, 0, 0.2, 1)
out: cubic-bezier(0, 0, 0.2, 1)
out-expo: cubic-bezier(0.19, 1, 0.22, 1)

## Springs
drag: { stiffness: 400, damping: 30, mass: 1 }
panel: { stiffness: 280, damping: 32, mass: 0.9 }

## Stagger
list: 20ms/item, max 500ms
grid: 30ms/item, 40ms initial delay

## Enter/exit
dropdown: translateY(-4px)→0 / opacity 0→1, sm, ease-out
modal: scale(0.96)→1 / opacity 0→1, lg, ease-out
panel: translateX(-100%)→0, lg, ease-out
toast: translateX(100%)→0, md, ease-out-expo

That is a complete motion system in 35 lines. It does not need to be longer. Brevity is a feature — a shorter file is more likely to be read and followed.

Why a separate file from DESIGN.md

Three reasons.

First, scope clarity. DESIGN.md is for static visual properties. It describes what something looks like at rest. MOTION.md describes what something does over time. Mixing them creates a longer, harder-to-navigate file where neither concern is fully served.

Second, consumption patterns. A developer building a static page only needs DESIGN.md. A developer building interactive components needs both. A developer working only on animations needs only MOTION.md. Separating them makes it easy to include only what is relevant.

Third, the motion system evolves differently than the visual system. Colors and typography change rarely and intentionally. Motion systems often need iteration — durations get tuned, spring configs get refined as interactions feel too loose or too snappy. A separate file with a clear single purpose is easier to update without disturbing the visual token documentation.

Using MOTION.md with Claude

Add it to CLAUDE.md alongside your DESIGN.md:

@content/designs/my-project.md
@content/motion/my-project.md

Or reference a pre-built motion system from DesignMD's catalog:

@content/motion/linear.md

When both files are loaded, Claude has the full picture: static design tokens from DESIGN.md and motion behavior from MOTION.md. Animation code it generates will use your duration scale by name, your spring configs by preset, and your enter/exit patterns by component type.

What does not belong in MOTION.md

Loading states and skeleton screens — these are component patterns, not motion tokens. Document them in component specs.

Page transition frameworks — if you are using a routing-level transition library, its configuration belongs in the project README or a dedicated framework config file, not MOTION.md.

Lottie files or keyframe animation source files — MOTION.md is a token and pattern spec, not an asset library. Reference these from component documentation.

The goal of MOTION.md is to answer one question: "Given a new component with enter/exit behavior, what motion should it use?" If the answer is not in the file, the file is not complete. If the file answers more than that question, it is overscoped.