Motion design tokens: building a spring scale for your design system
Why design systems need motion tokens, how to define a small spring scale that works in both Motion and Reanimated, and how to roll it out across components.
Most design systems have tokens for colour, type, spacing and radius. Far fewer have tokens for motion, and it shows: one component bounces, another eases out over 400ms, a third snaps instantly. Motion tokens fix that the same way colour tokens fixed a hundred slightly different greys.
What a motion token is
A motion token is a named, reusable animation setting. For duration-based animation that is a duration plus an easing curve. For interactive UI we recommend springs, so a token is a set of physical parameters:
// tokens/tokens.ts
export const spring = {
snappy: { type: "spring", stiffness: 520, damping: 34, mass: 1 },
soft: { type: "spring", stiffness: 220, damping: 26, mass: 1 },
heavy: { type: "spring", stiffness: 140, damping: 22, mass: 1.4 },
} as const;
export const nativeSpring = {
snappy: { stiffness: 520, damping: 34, mass: 1 },
soft: { stiffness: 220, damping: 26, mass: 1 },
heavy: { stiffness: 140, damping: 22, mass: 1.4 },
} as const;These are the exact tokens Moveset uses. The web version plugs into Motion's transition prop; the native version plugs into Reanimated's withSpring.
Keep the scale small
Three springs cover nearly every interaction. Name them by feel, not by component, so they stay reusable:
- snappy: high stiffness, enough damping to avoid wobble. Toggles, tabs, button presses, anything frequent.
- soft: lower stiffness for things that follow the user, like tilt, hover lift or a magnetic pull.
- heavy: more mass, used sparingly for big one-off moments such as a hero reveal or a payment success.
If you find yourself adding a fourth, ask whether the new component is really different or just tuned by a different person.
Use the tokens everywhere
import { motion } from "motion/react";
import { spring } from "@/tokens/tokens";
<motion.div layout transition={spring.snappy} />import { withSpring } from "react-native-reanimated";
import { nativeSpring } from "@/tokens/tokens";
offset.value = withSpring(0, nativeSpring.soft);For a whole subtree, Motion's MotionConfig sets a default transition so children inherit the token without repeating it.
Tokens beyond springs
A complete motion layer usually also defines:
- Distances: how far things travel on enter (for example 8px for small elements, 24px for sections). Small distances read as polish; large ones read as drama.
- Stagger: the delay between items in a list reveal, typically tens of milliseconds.
- Reduced-motion behaviour: what each token becomes when the user prefers reduced motion. A sensible default is "no travel, short or no fade".
Document intent, not just values
Each token in your docs should say when to use it, with a live example. "Use soft for anything that follows the pointer" is more useful than the numbers themselves. Moveset keeps the presets in one token file, so new components can match the existing feel.
Rolling it out
- Inventory existing animations and group them by feel.
- Pick the three springs that cover the groups.
- Replace hard-coded transitions component by component, starting with the most used.
- Add a lint rule or code review check for inline spring numbers.
Pairing with the rest of the system
Motion tokens sit alongside colour, type, spacing and radius tokens. Moveset's token file exports all of them, with one accent colour used sparingly for focus states, Pro tags and the spring curve itself, which keeps animated surfaces calm.