Command Palette

Search for a command to run...

Design Tokens for Motion: How to Standardize Animation Durations, Easing, and Keyframes Across a Design System

Design Tokens for Motion: How to Standardize Animation Durations, Easing, and Keyframes Across a Design System

Create motion tokens for duration, easing, springs, and keyframes. Export to CSS variables, map in Tailwind, wire to Framer Motion/React Spring, and respect reduced motion.

4 min read·Motion

Motion should be predictable and purposeful—not an ad‑hoc flourish. Standardizing durations, easing, and keyframes as design tokens makes your UI feel coherent, accessible, and easier to maintain. In this guide, you’ll build a motion token pipeline and integrate it with Tailwind, Framer Motion, and React Spring.

You’ll get: a token schema, a build script to generate CSS variables, Tailwind mappings, FM/Spring adapters, and a11y rules for reduced motion.


Table of Contents

  1. Principles & naming
  2. Token schema (JSON)
  3. Build tokens → CSS variables
  4. Tailwind mapping
  5. Using tokens in Framer Motion & React Spring
  6. Keyframe tokens & utilities
  7. Reduced motion & accessibility
  8. Documentation & testing
  9. Reference structure
  10. FAQ

Principles & naming

  • Semantic over numeric (motion.duration.fast not 200ms).
  • Consistent tiers: instant, fast, base, slow.
  • Families: duration, easing, spring, keyframe.
  • Use cases: emphasize feedback (short), layout (base), entrances (slow).

Token schema (JSON)

{
  "motion": {
    "duration": {
      "instant": 100,
      "fast": 160,
      "base": 240,
      "slow": 360
    },
    "easing": {
      "standard": "cubic-bezier(0.2, 0, 0, 1)",
      "emphasized": "cubic-bezier(0.2, 0, 0, 1.2)",
      "in": "cubic-bezier(0.4, 0, 1, 1)",
      "out": "cubic-bezier(0, 0, 0.2, 1)"
    },
    "spring": {
      "fast": { "stiffness": 520, "damping": 32 },
      "base": { "stiffness": 400, "damping": 36 },
      "gentle": { "stiffness": 300, "damping": 28 }
    },
    "keyframe": {
      "fade-in": [{ "opacity": 0 }, { "opacity": 1 }],
      "slide-up": [
        { "transform": "translateY(8px)", "opacity": 0 },
        { "transform": "translateY(0)", "opacity": 1 }
      ]
    }
  }
}

Build tokens → CSS variables

// scripts/build-motion.ts (sketch)
import fs from "node:fs";
const tokens = JSON.parse(fs.readFileSync("tokens.json", "utf8")).motion;
 
const lines: string[] = [":root{"];
Object.entries(tokens.duration).forEach(([k, v]) =>
  lines.push(`--motion-duration-${k}:${v}ms;`),
);
Object.entries(tokens.easing).forEach(([k, v]) =>
  lines.push(`--motion-easing-${k}:${v};`),
);
lines.push("}");
 
lines.push("@keyframes fade-in{from{opacity:0}to{opacity:1}}");
lines.push(
  "@keyframes slide-up{from{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}",
);
 
fs.writeFileSync("src/styles/motion.css", lines.join(""));

Import in your app’s global CSS before components.


Tailwind mapping

// tailwind.config.ts (excerpt)
export default {
  theme: {
    extend: {
      transitionDuration: {
        instant: "var(--motion-duration-instant)",
        fast: "var(--motion-duration-fast)",
        base: "var(--motion-duration-base)",
        slow: "var(--motion-duration-slow)",
      },
      animation: {
        "fade-in":
          "fade-in var(--motion-duration-base) var(--motion-easing-standard) both",
        "slide-up":
          "slide-up var(--motion-duration-fast) var(--motion-easing-out) both",
      },
    },
  },
};

Use utilities:

<div className="animate-fade-in" />

Using tokens in Framer Motion & React Spring

Framer Motion

import { motion } from "framer-motion";
 
const dur = getComputedStyle(document.documentElement)
  .getPropertyValue("--motion-duration-fast")
  .trim();
 
export function Toast({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      initial={{ y: 12, opacity: 0 }}
      animate={{ y: 0, opacity: 1 }}
      exit={{ y: 12, opacity: 0 }}
      transition={{ duration: Number(dur.replace("ms", "")) / 1000 }}
      className="rounded bg-black px-3 py-2 text-white"
    >
      {children}
    </motion.div>
  );
}

React Spring

import { useSpring, animated } from "@react-spring/web";
 
export function Expand({
  open,
  children,
}: {
  open: boolean;
  children: React.ReactNode;
}) {
  const styles = useSpring({
    height: open ? "auto" : 0,
    opacity: open ? 1 : 0,
    config: { tension: 400, friction: 36 }, // from tokens.spring.base
  });
  return <animated.div style={styles}>{children}</animated.div>;
}

A small helper can read CSS vars once and expose them to JS.


Keyframe tokens & utilities

Expose canonical keyframes for common effects—fade, slide, scale, shimmer. Reference them via classes to avoid bespoke one‑offs.

/* src/styles/motion.css */
@keyframes shimmer {
  0% {
    background-position: -200% 0;
  }
  100% {
    background-position: 200% 0;
  }
}
.skeleton {
  background: linear-gradient(90deg, #eee 25%, #f6f6f6 37%, #eee 63%);
  background-size: 400% 100%;
  animation: shimmer var(--motion-duration-slow) linear infinite;
}

Reduced motion & accessibility

Respect prefers-reduced-motion across JS and CSS.

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

Guidelines

  • Use motion to clarify (feedback, spatial change), not to decorate.
  • Keep routine UI at 160–240ms; entrance/exit 240–360ms.
  • Avoid parallax and heavy blurs; prefer transforms/opacity.

Documentation & testing

  • Document tokens in Storybook with knobs to adjust durations/easing.
  • Add jest‑axe to ensure focus styles remain visible with/without motion.
  • Visual tests at reduced motion to prevent regressions.

Reference structure

design-system/
├─ tokens.json
├─ scripts/
│  └─ build-motion.ts
├─ src/styles/
│  └─ motion.css
└─ tailwind.config.ts

FAQ

Why not hard‑code durations in components?
Tokenizing motion keeps timing consistent and easy to adjust globally.

Do motion tokens work with RSC?
Yes—tokens are CSS variables; client islands read them at runtime.

How many tokens should we have?
Keep it small: instant/fast/base/slow plus a few keyframes. Expand only when real use‑cases demand it.


Next steps

  • Define motion tokens and generate CSS variables.
  • Map to Tailwind & wire Framer Motion/Spring adapters.
  • Add a reduced‑motion Storybook page and run visual tests.

Read more like this

Design Tokens for Motion: How to Standardize Animation Durations, Easing, and Keyframes Across a Design System | Ruixen UI