Back to list
dylantarre

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

TokenValueUse Case
instant0msImmediate, no animation
fastest50msMicro-feedback (ripples, highlights)
fast100msHover, focus, small changes
normal200msMost transitions, toggles
slow300msModals, drawers, reveals
slower400msPage transitions, complex sequences
slowest500msElaborate animations, onboarding

Quick Reference: Easing Curves

TokenCurveUse Case
linearlinearProgress bars, looping
ease-incubic-bezier(0.4, 0, 1, 1)Exit animations
ease-outcubic-bezier(0, 0, 0.2, 1)Enter animations (most common)
ease-in-outcubic-bezier(0.4, 0, 0.2, 1)Move, resize, continuous
ease-bouncecubic-bezier(0.34, 1.56, 0.64, 1)Playful, attention
ease-elasticcubic-bezier(0.68, -0.55, 0.27, 1.55)Springy, overshoot

The Process

  1. Choose duration scale: How many steps? (5-7 typical)
  2. Define base duration: 200ms is standard baseline
  3. Select ratio: How durations scale (1.5x or 2x)
  4. Pick easing curves: Standard set + any special curves
  5. Create semantic aliases: Map to component behaviors
  6. Add reduced motion: Respect user preferences
  7. 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-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 {
  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

DurationFeelUse For
0-50msInstantRipples, micro-feedback
50-100msSnappyHover states, focus, toggles
100-200msQuickMost UI transitions
200-300msSmoothModals, panels, reveals
300-500msDeliberatePage transitions, onboarding
500ms+SlowComplex 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-motion is 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