← Back to list

motion-scale
by dylantarre
Design system skills for agentic coding
⭐ 3🍴 0📅 Jan 6, 2026
SKILL.md
name: motion-scale description: Generates animation duration, easing curves, and delay tokens with prefers-reduced-motion support. Use when creating transition timing, animation speed, or motion systems. Outputs CSS, Tailwind, or JSON.
Motion Scale Generator
Overview
Generate consistent animation and transition tokens for duration, easing, and delay. Creates a motion system that feels cohesive and respects user preferences for reduced motion.
When to Use
- Setting up animation tokens for a new project
- Standardizing transition timing across components
- Replacing hardcoded animation values
- Implementing reduced motion support
- Building micro-interaction patterns
Quick Reference: Duration Scale
| Token | Value | Use Case |
|---|---|---|
instant | 0ms | Immediate, no animation |
fastest | 50ms | Micro-feedback (ripples, highlights) |
fast | 100ms | Hover, focus, small changes |
normal | 200ms | Most transitions, toggles |
slow | 300ms | Modals, drawers, reveals |
slower | 400ms | Page transitions, complex sequences |
slowest | 500ms | Elaborate animations, onboarding |
Quick Reference: Easing Curves
| Token | Curve | Use Case |
|---|---|---|
linear | linear | Progress bars, looping |
ease-in | cubic-bezier(0.4, 0, 1, 1) | Exit animations |
ease-out | cubic-bezier(0, 0, 0.2, 1) | Enter animations (most common) |
ease-in-out | cubic-bezier(0.4, 0, 0.2, 1) | Move, resize, continuous |
ease-bounce | cubic-bezier(0.34, 1.56, 0.64, 1) | Playful, attention |
ease-elastic | cubic-bezier(0.68, -0.55, 0.27, 1.55) | Springy, overshoot |
The Process
- Choose duration scale: How many steps? (5-7 typical)
- Define base duration: 200ms is standard baseline
- Select ratio: How durations scale (1.5x or 2x)
- Pick easing curves: Standard set + any special curves
- Create semantic aliases: Map to component behaviors
- Add reduced motion: Respect user preferences
- Choose format: CSS, Tailwind, or JSON
Output Formats
CSS Custom Properties:
:root {
/* ===== Durations ===== */
--duration-0: 0ms;
--duration-50: 50ms;
--duration-100: 100ms;
--duration-150: 150ms;
--duration-200: 200ms;
--duration-300: 300ms;
--duration-400: 400ms;
--duration-500: 500ms;
--duration-700: 700ms;
--duration-1000: 1000ms;
/* Named aliases */
--duration-instant: var(--duration-0);
--duration-fastest: var(--duration-50);
--duration-fast: var(--duration-100);
--duration-normal: var(--duration-200);
--duration-slow: var(--duration-300);
--duration-slower: var(--duration-500);
--duration-slowest: var(--duration-700);
/* ===== Easings ===== */
--ease-linear: linear;
--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);
/* Expressive easings */
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-elastic: cubic-bezier(0.68, -0.55, 0.27, 1.55);
--ease-snap: cubic-bezier(0.2, 0, 0, 1);
/* Deceleration/Acceleration (Material Design style) */
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
/* ===== Delays ===== */
--delay-none: 0ms;
--delay-short: 50ms;
--delay-medium: 100ms;
--delay-long: 200ms;
/* Stagger delays for sequential animations */
--delay-stagger-1: 0ms;
--delay-stagger-2: 50ms;
--delay-stagger-3: 100ms;
--delay-stagger-4: 150ms;
--delay-stagger-5: 200ms;
/* ===== Semantic Transitions ===== */
--transition-colors: color, background-color, border-color, fill, stroke;
--transition-opacity: opacity;
--transition-transform: transform;
--transition-shadow: box-shadow;
--transition-all: all;
/* Component transitions */
--transition-hover: var(--duration-fast) var(--ease-out);
--transition-focus: var(--duration-fast) var(--ease-out);
--transition-active: var(--duration-fastest) var(--ease-out);
--transition-enter: var(--duration-normal) var(--ease-out);
--transition-exit: var(--duration-fast) var(--ease-in);
--transition-move: var(--duration-normal) var(--ease-in-out);
--transition-expand: var(--duration-slow) var(--ease-out);
--transition-collapse: var(--duration-normal) var(--ease-in);
}
/* ===== Reduced Motion ===== */
@media (prefers-reduced-motion: reduce) {
:root {
--duration-50: 0ms;
--duration-100: 0ms;
--duration-150: 0ms;
--duration-200: 0ms;
--duration-300: 0ms;
--duration-400: 0ms;
--duration-500: 0ms;
--duration-700: 0ms;
--duration-1000: 0ms;
}
}
/* Alternative: Keep minimal motion */
@media (prefers-reduced-motion: reduce) {
:root {
--duration-fastest: 0ms;
--duration-fast: 0ms;
--duration-normal: 50ms; /* Minimal feedback */
--duration-slow: 50ms;
--duration-slower: 100ms;
--duration-slowest: 100ms;
/* Disable expressive easings */
--ease-bounce: var(--ease-out);
--ease-elastic: var(--ease-out);
}
}
Tailwind Config:
module.exports = {
theme: {
extend: {
transitionDuration: {
'0': '0ms',
'50': '50ms',
'100': '100ms',
'150': '150ms',
'200': '200ms',
'300': '300ms',
'400': '400ms',
'500': '500ms',
'700': '700ms',
'1000': '1000ms',
},
transitionTimingFunction: {
'in': 'cubic-bezier(0.4, 0, 1, 1)',
'out': 'cubic-bezier(0, 0, 0.2, 1)',
'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
'bounce': 'cubic-bezier(0.34, 1.56, 0.64, 1)',
'elastic': 'cubic-bezier(0.68, -0.55, 0.27, 1.55)',
'snap': 'cubic-bezier(0.2, 0, 0, 1)',
},
transitionDelay: {
'0': '0ms',
'50': '50ms',
'100': '100ms',
'150': '150ms',
'200': '200ms',
},
animation: {
'fade-in': 'fade-in 200ms ease-out',
'fade-out': 'fade-out 150ms ease-in',
'slide-up': 'slide-up 200ms ease-out',
'slide-down': 'slide-down 200ms ease-out',
'scale-in': 'scale-in 200ms ease-out',
'spin-slow': 'spin 2s linear infinite',
},
keyframes: {
'fade-in': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
'fade-out': {
'0%': { opacity: '1' },
'100%': { opacity: '0' },
},
'slide-up': {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
'slide-down': {
'0%': { transform: 'translateY(-10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
'scale-in': {
'0%': { transform: 'scale(0.95)', opacity: '0' },
'100%': { transform: 'scale(1)', opacity: '1' },
},
},
},
},
};
JSON Tokens:
{
"motion": {
"duration": {
"0": { "value": "0ms", "type": "duration" },
"50": { "value": "50ms", "type": "duration" },
"100": { "value": "100ms", "type": "duration" },
"200": { "value": "200ms", "type": "duration" },
"300": { "value": "300ms", "type": "duration" },
"500": { "value": "500ms", "type": "duration" }
},
"easing": {
"linear": { "value": "linear", "type": "cubicBezier" },
"ease-in": { "value": "cubic-bezier(0.4, 0, 1, 1)", "type": "cubicBezier" },
"ease-out": { "value": "cubic-bezier(0, 0, 0.2, 1)", "type": "cubicBezier" },
"ease-in-out": { "value": "cubic-bezier(0.4, 0, 0.2, 1)", "type": "cubicBezier" },
"bounce": { "value": "cubic-bezier(0.34, 1.56, 0.64, 1)", "type": "cubicBezier" }
},
"delay": {
"none": { "value": "0ms", "type": "duration" },
"short": { "value": "50ms", "type": "duration" },
"medium": { "value": "100ms", "type": "duration" }
}
}
}
Easing Curve Reference
Standard Curves
ease-out (enter/appear):
●━━━━━━━━━━━━━━━━━━●
Fast start, gentle stop
Best for: Elements entering view
ease-in (exit/leave):
●━━━━━━━━━━━━━━━━━━●
Gentle start, fast end
Best for: Elements leaving view
ease-in-out (move/resize):
●━━━━━━━━━━━━━━━━━━●
Gentle start and end
Best for: Elements moving position
linear (progress):
●━━━━━━━━━━━━━━━━━━●
Constant speed
Best for: Progress bars, spinners
Expressive Curves
bounce (playful):
●━━━━━━━━╮
╰━━━●
Overshoots then settles
Best for: Notifications, confirmations
elastic (springy):
●━━━╮ ╭━╮
╰━━━╯ ╰●
Oscillates before settling
Best for: Attention, drag-and-drop
snap (decisive):
●━━━━━━━━━━━━━━━━●
Very fast deceleration
Best for: Snapping, selections
Custom Curve Creation
/*
cubic-bezier(x1, y1, x2, y2)
x1, y1 = First control point (affects start)
x2, y2 = Second control point (affects end)
y values > 1 = overshoot
y values < 0 = anticipation
*/
/* Dramatic entrance */
--ease-dramatic: cubic-bezier(0.2, 0.8, 0.2, 1);
/* Snappy with slight overshoot */
--ease-snappy: cubic-bezier(0.2, 1.2, 0.3, 1);
/* Gentle float */
--ease-float: cubic-bezier(0.4, 0.2, 0.2, 1);
Component Patterns
Button Hover
.button {
transition:
background-color var(--transition-hover),
transform var(--transition-active);
}
.button:hover {
background-color: var(--color-primary-600);
}
.button:active {
transform: scale(0.98);
}
Modal Enter/Exit
.modal-backdrop {
transition: opacity var(--duration-normal) var(--ease-out);
}
.modal-content {
transition:
opacity var(--duration-normal) var(--ease-out),
transform var(--duration-normal) var(--ease-out);
}
/* Enter */
.modal-backdrop.entering { opacity: 0; }
.modal-backdrop.entered { opacity: 1; }
.modal-content.entering { opacity: 0; transform: scale(0.95) translateY(10px); }
.modal-content.entered { opacity: 1; transform: scale(1) translateY(0); }
/* Exit (faster) */
.modal-backdrop.exiting {
opacity: 0;
transition-duration: var(--duration-fast);
}
.modal-content.exiting {
opacity: 0;
transform: scale(0.95);
transition-duration: var(--duration-fast);
}
Dropdown Menu
.dropdown-menu {
transform-origin: top;
transition:
opacity var(--duration-fast) var(--ease-out),
transform var(--duration-fast) var(--ease-out);
}
.dropdown-menu[data-state="closed"] {
opacity: 0;
transform: scaleY(0.9);
pointer-events: none;
}
.dropdown-menu[data-state="open"] {
opacity: 1;
transform: scaleY(1);
}
Staggered List Animation
.list-item {
opacity: 0;
transform: translateY(10px);
animation: slide-up var(--duration-normal) var(--ease-out) forwards;
}
.list-item:nth-child(1) { animation-delay: var(--delay-stagger-1); }
.list-item:nth-child(2) { animation-delay: var(--delay-stagger-2); }
.list-item:nth-child(3) { animation-delay: var(--delay-stagger-3); }
.list-item:nth-child(4) { animation-delay: var(--delay-stagger-4); }
.list-item:nth-child(5) { animation-delay: var(--delay-stagger-5); }
@keyframes slide-up {
to {
opacity: 1;
transform: translateY(0);
}
}
Loading Spinner
.spinner {
animation: spin var(--duration-1000) var(--ease-linear) infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Pulsing loader */
.pulse {
animation: pulse var(--duration-700) var(--ease-in-out) infinite alternate;
}
@keyframes pulse {
0% { opacity: 0.4; transform: scale(0.95); }
100% { opacity: 1; transform: scale(1); }
}
Skeleton Loading
.skeleton {
background: linear-gradient(
90deg,
var(--color-gray-200) 0%,
var(--color-gray-100) 50%,
var(--color-gray-200) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s var(--ease-in-out) infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Reduced Motion
Strategy 1: Remove All Motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Strategy 2: Reduce, Don't Remove
@media (prefers-reduced-motion: reduce) {
:root {
/* Keep brief transitions for feedback */
--duration-fast: 0ms;
--duration-normal: 50ms;
--duration-slow: 100ms;
/* Simplify easings */
--ease-bounce: var(--ease-out);
--ease-elastic: var(--ease-out);
}
/* Disable parallax, auto-play, infinite loops */
.parallax { transform: none !important; }
.auto-animate { animation: none !important; }
}
Strategy 3: Provide Alternatives
/* Full motion */
.card-enter {
animation: slide-up var(--duration-normal) var(--ease-out);
}
/* Reduced: fade only, no movement */
@media (prefers-reduced-motion: reduce) {
.card-enter {
animation: fade-in var(--duration-fast) var(--ease-out);
}
}
Duration Guidelines
| Duration | Feel | Use For |
|---|---|---|
| 0-50ms | Instant | Ripples, micro-feedback |
| 50-100ms | Snappy | Hover states, focus, toggles |
| 100-200ms | Quick | Most UI transitions |
| 200-300ms | Smooth | Modals, panels, reveals |
| 300-500ms | Deliberate | Page transitions, onboarding |
| 500ms+ | Slow | Complex sequences, emphasis |
Rule of thumb:
- Smaller elements = faster (50-150ms)
- Larger elements = slower (200-400ms)
- User-triggered = fast (100-200ms)
- System-triggered = can be slower (200-400ms)
Testing Checklist
- Transitions feel smooth, not jarring
- Hover/focus feedback is immediate (<150ms)
- Modals/drawers don't feel sluggish
- Animations enhance, don't block interaction
-
prefers-reduced-motionis respected - No layout shift during animations
- Animations work at 60fps
- Consistent timing across similar interactions
Score
Total Score
60/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon