Motion Guidelines
Motion and animation guidelines, easing curves, and transition patterns for design systems
You are an expert in motion and animation guidelines for building and maintaining design systems. ## Key Points 1. **Purposeful** — every animation should answer "what is this helping the user understand?" If there is no answer, remove it. 2. **Quick** — UI transitions should feel instantaneous to snappy; most component animations fall between 100ms and 300ms. 3. **Natural** — use easing curves that mimic real-world physics; linear motion feels robotic. 4. **Accessible** — respect `prefers-reduced-motion` and never rely on animation as the sole means of conveying information. - Always wrap motion in `prefers-reduced-motion` checks — disable or drastically shorten animations for users who have enabled reduced motion at the OS level. - Use the duration scale tokens consistently; do not hard-code arbitrary durations in component CSS. - Pair enter and exit animations so components do not appear instantly but disappear with a transition, or vice versa. - Using `transition: all` which animates unintended properties (like width or height), causing janky layout recalculations and poor performance. - Making animations too slow in an effort to be "polished" — anything over 300ms for a simple component transition feels sluggish and hurts perceived performance.
skilldb get design-systems-skills/Motion GuidelinesFull skill: 217 linesMotion Guidelines — Design Systems
You are an expert in motion and animation guidelines for building and maintaining design systems.
Overview
Motion in a design system serves functional purposes — it communicates state changes, guides attention, establishes spatial relationships, and provides feedback. A motion guideline defines shared duration scales, easing curves, and reusable transition patterns so animations feel cohesive across every component and page.
Core Philosophy
Motion in a design system is functional communication, not decoration. Every animation should answer a specific question: "What just changed?" (a toast appearing), "Where did this come from?" (a modal sliding in from the element that triggered it), or "What should I look at?" (a pulsing notification badge). If an animation does not serve one of these purposes, it is visual noise that slows users down.
Speed is the most important property of UI animation. Research consistently shows that transitions between 100ms and 300ms feel responsive, while anything above 400ms feels sluggish. The design system should encode these durations as tokens so teams do not independently decide that their dropdown should take 600ms. A shared duration scale creates temporal consistency — interactions across the entire product feel like they have the same rhythm and pace.
Accessibility is a hard requirement, not a nice-to-have. Users who enable prefers-reduced-motion at the OS level are communicating a medical need, not a preference. The system must respect this by disabling or drastically simplifying all motion when that media query matches. This means motion tokens should resolve to 0ms under reduced motion, and components should remain fully functional without any animation.
Core Concepts
Motion Principles
- Purposeful — every animation should answer "what is this helping the user understand?" If there is no answer, remove it.
- Quick — UI transitions should feel instantaneous to snappy; most component animations fall between 100ms and 300ms.
- Natural — use easing curves that mimic real-world physics; linear motion feels robotic.
- Accessible — respect
prefers-reduced-motionand never rely on animation as the sole means of conveying information.
Duration Scale
| Token | Value | Use case |
|---|---|---|
duration-instant | 100ms | Micro-interactions: checkbox, toggle |
duration-fast | 150ms | Tooltips, hover states, ripples |
duration-normal | 250ms | Modals, dropdowns, accordions |
duration-slow | 400ms | Page transitions, complex reveals |
duration-slower | 600ms | Orchestrated sequences, onboarding |
Easing Curves
| Token | Value | Use case |
|---|---|---|
ease-default | cubic-bezier(0.2, 0, 0, 1) | General-purpose transitions |
ease-in | cubic-bezier(0.4, 0, 1, 1) | Elements leaving the screen |
ease-out | cubic-bezier(0, 0, 0.2, 1) | Elements entering the screen |
ease-in-out | cubic-bezier(0.4, 0, 0.2, 1) | Elements moving on-screen |
ease-spring | cubic-bezier(0.175, 0.885, 0.32, 1.275) | Playful bounces |
Implementation Patterns
Motion Tokens as CSS Custom Properties
:root {
--duration-instant: 100ms;
--duration-fast: 150ms;
--duration-normal: 250ms;
--duration-slow: 400ms;
--ease-default: cubic-bezier(0.2, 0, 0, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
}
/* Reduced motion override */
@media (prefers-reduced-motion: reduce) {
:root {
--duration-instant: 0ms;
--duration-fast: 0ms;
--duration-normal: 0ms;
--duration-slow: 0ms;
}
}
Reusable Transition Classes
.transition-fade {
transition: opacity var(--duration-normal) var(--ease-default);
}
.transition-slide-up {
transition:
opacity var(--duration-normal) var(--ease-out),
transform var(--duration-normal) var(--ease-out);
}
.transition-slide-up[data-state="open"] {
opacity: 1;
transform: translateY(0);
}
.transition-slide-up[data-state="closed"] {
opacity: 0;
transform: translateY(8px);
}
.transition-scale {
transition:
opacity var(--duration-fast) var(--ease-out),
transform var(--duration-fast) var(--ease-out);
transform-origin: var(--origin, center);
}
.transition-scale[data-state="open"] {
opacity: 1;
transform: scale(1);
}
.transition-scale[data-state="closed"] {
opacity: 0;
transform: scale(0.95);
}
Animated Presence with Exit Animations
import { useRef, useState, useEffect } from 'react';
interface AnimatePresenceProps {
show: boolean;
children: React.ReactNode;
className?: string;
duration?: number;
}
export function AnimatePresence({
show,
children,
className = 'transition-fade',
duration = 250,
}: AnimatePresenceProps) {
const [mounted, setMounted] = useState(show);
const [state, setState] = useState<'open' | 'closed'>(show ? 'open' : 'closed');
useEffect(() => {
if (show) {
setMounted(true);
// Wait one frame for the element to mount in the "closed" state
requestAnimationFrame(() => setState('open'));
} else {
setState('closed');
const timer = setTimeout(() => setMounted(false), duration);
return () => clearTimeout(timer);
}
}, [show, duration]);
if (!mounted) return null;
return (
<div className={className} data-state={state}>
{children}
</div>
);
}
Staggered List Animation
.stagger-list > * {
opacity: 0;
transform: translateY(8px);
animation: stagger-in var(--duration-normal) var(--ease-out) forwards;
}
.stagger-list > *:nth-child(1) { animation-delay: 0ms; }
.stagger-list > *:nth-child(2) { animation-delay: 50ms; }
.stagger-list > *:nth-child(3) { animation-delay: 100ms; }
.stagger-list > *:nth-child(4) { animation-delay: 150ms; }
.stagger-list > *:nth-child(5) { animation-delay: 200ms; }
@keyframes stagger-in {
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.stagger-list > * {
animation: none;
opacity: 1;
transform: none;
}
}
Best Practices
- Always wrap motion in
prefers-reduced-motionchecks — disable or drastically shorten animations for users who have enabled reduced motion at the OS level. - Use the duration scale tokens consistently; do not hard-code arbitrary durations in component CSS.
- Pair enter and exit animations so components do not appear instantly but disappear with a transition, or vice versa.
Common Pitfalls
- Using
transition: allwhich animates unintended properties (like width or height), causing janky layout recalculations and poor performance. - Making animations too slow in an effort to be "polished" — anything over 300ms for a simple component transition feels sluggish and hurts perceived performance.
Anti-Patterns
-
Animation for its own sake. Adding bounce effects, elastic easings, and multi-step transitions to basic UI elements like buttons or checkboxes because they look fun in isolation. In a production interface used hundreds of times a day, clever animations become irritating.
-
Inconsistent timing across components. A dropdown that opens in 150ms, a modal that fades in over 500ms, and a tooltip that appears in 50ms create a disjointed experience. Use the shared duration tokens consistently so all similar interactions feel like they belong to the same product.
-
Enter animations without exit animations. A modal that fades in smoothly but disappears instantly, or a drawer that slides in from the right but vanishes without sliding back, feels half-finished. Always pair entrance and exit animations for a complete interaction loop.
-
Ignoring
prefers-reduced-motion. Shipping animations without checking this media query is an accessibility violation. Users with vestibular disorders, seizure conditions, or motion sensitivity rely on this setting, and design systems are in the best position to enforce compliance. -
Animating layout properties. Transitioning
width,height,top,left, ormargintriggers expensive browser layout recalculations on every frame. Stick totransformandopacity, which are GPU-composited and do not affect the document flow.
Install this skill directly: skilldb add design-systems-skills
Related Skills
Component Architecture
Component API design, composition patterns, and architecture for scalable design system components
Design System Governance
Versioning, contribution processes, and governance models for design system teams
Design Tokens
Design tokens for colors, spacing, typography, and other visual primitives in design systems
Icon Systems
Icon systems, SVG management, and scalable icon delivery pipelines for design systems
Responsive Patterns
Responsive design patterns, fluid layouts, and adaptive component strategies for design systems
Storybook Docs
Storybook documentation, visual testing, and interactive component cataloging for design systems