When to Use
- Use when: adding transitions, hover states, or interactive feedback
- Use when: animations feel mechanical, too slow, or jarring
- Use when: implementing skeleton loading choreography
- Do NOT use for: Three.js / GSAP / Lottie — those are animation library decisions
- Do NOT use for: page-level routing transitions — use
aesthetic-anchor motion tokens there
Duration Scale
| Name |
Range |
Use |
| Instant |
0ms |
Hover color, cursor change — never animate |
| Micro |
100–150ms |
Button press, checkbox toggle, ripple |
| Small |
150–250ms |
Tooltip appear, dropdown open, fade |
| Medium |
250–400ms |
Panel slide, card expand |
| Large |
400–600ms |
Modal + backdrop, page transition |
| Celebration |
600–1000ms |
Onboarding, success milestone — use sparingly |
Rules:
- Under 100ms → no animation needed, instant change
- User-initiated actions: 150–300ms
- System-initiated changes: 200–500ms
- Mobile: 30% shorter than desktop equivalents
- Never exceed 1000ms in a regular workflow
Easing Curves
:root {
/* Entering screen — fast start, decelerate to rest */
--ease-out: cubic-bezier(0.0, 0.0, 0.2, 1.0);
/* Leaving screen — accelerate then exit */
--ease-in: cubic-bezier(0.4, 0.0, 1.0, 1.0);
/* On-screen state change — smooth bidirectional */
--ease-inout: cubic-bezier(0.4, 0.0, 0.2, 1.0);
}
Never use linear for UI motion — it feels mechanical.
Exceptions only: infinite loaders, color interpolation.
Spring physics (JS): stiffness: 170 / damping: 26 / mass: 1 — for playful bounce.
Micro-Interaction Patterns
Button
/* Press */
button:active { transform: scale(0.97); transition: transform 80ms var(--ease-in); }
/* Release */
button { transition: transform 200ms var(--ease-out); }
/* Loading state: swap label for inline spinner — no layout shift */
/* Success: color → green + checkmark, 300ms */
/* Disabled: opacity 0.5, remove all hover states */
Form field
Focus: border-color transition 150ms --ease-inout
Success: green border + checkmark fade-in 200ms
Error: red border + horizontal shake (±3px, 3 cycles, 300ms) + error text slide-down
Toast / Notification
Enter: slide from edge + fade-in, 300ms --ease-out
Persist: success = 3–5s, error = persistent (user must dismiss)
Exit: fade + vertical slide, 200ms --ease-in
Stack: 50ms stagger between simultaneous toasts
Modal
Backdrop: opacity 0 → 0.5, 200ms
Dialog: scale 0.95 → 1.0 + fade-in, 250ms --ease-out
Dismiss: reverse with --ease-in
Emotional Motion Vocabulary
| Mood |
Curve |
Duration |
| Confident |
ease-out |
200–250ms |
| Playful |
spring / slight overshoot |
300–400ms |
| Urgent |
ease-in, sharp |
100–150ms |
| Calm |
ease-in-out, gentle |
300–500ms |
| Celebratory |
particles / radial burst |
600–1000ms, once only |
Reduced Motion (Accessibility Required)
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Replace, don't remove:
- Slide → instant appear or opacity fade
- Bounce/spin → static indicator
- Parallax → fixed background
- Keep: progress bars and functional loaders (simplify only)
Anti-Fake-Pass Rules
Before claiming motion is done, you MUST show:
Reference: gates/anti-fake-pass-gate.md
1---2name: motion-design3description: Design motion and animation for UI — duration timing, easing curves, micro-interaction patterns (button, form, toast, modal), and reduced-motion accessibility. Use when asked to "add animation", "make it feel alive", "micro-interactions", "transition", or "something feels janky/mechanical". Do NOT use for 3D or canvas animation — this covers UI-layer motion only.4license: MIT © phazurlabs5---6
7<!-- Adapted from phazurlabs/ux-ui-mastery (MIT) — Interaction & Motion Design skill.
8 Duration table, easing curves, micro-interaction patterns, reduced-motion section.
9 YAMTAM structure, Anti-Fake-Pass section, and CSS output format are original. -->
10
11## When to Use
12
13- Use when: adding transitions, hover states, or interactive feedback
14- Use when: animations feel mechanical, too slow, or jarring
15- Use when: implementing skeleton loading choreography
16- Do NOT use for: Three.js / GSAP / Lottie — those are animation library decisions
17- Do NOT use for: page-level routing transitions — use `aesthetic-anchor` motion tokens there
18
19---
20
21## Duration Scale
22
23| Name | Range | Use |
24|---|---|---|
25| Instant | 0ms | Hover color, cursor change — never animate |
26| Micro | 100–150ms | Button press, checkbox toggle, ripple |
27| Small | 150–250ms | Tooltip appear, dropdown open, fade |
28| Medium | 250–400ms | Panel slide, card expand |
29| Large | 400–600ms | Modal + backdrop, page transition |
30| Celebration | 600–1000ms | Onboarding, success milestone — use sparingly |
31
32**Rules:**
33- Under 100ms → no animation needed, instant change
34- User-initiated actions: 150–300ms
35- System-initiated changes: 200–500ms
36- Mobile: 30% shorter than desktop equivalents
37- Never exceed 1000ms in a regular workflow
38
39---
40
41## Easing Curves
42
43```css
44:root {
45 /* Entering screen — fast start, decelerate to rest */
46 --ease-out: cubic-bezier(0.0, 0.0, 0.2, 1.0);
47
48 /* Leaving screen — accelerate then exit */
49 --ease-in: cubic-bezier(0.4, 0.0, 1.0, 1.0);
50
51 /* On-screen state change — smooth bidirectional */
52 --ease-inout: cubic-bezier(0.4, 0.0, 0.2, 1.0);
53}
54```
55
56**Never use `linear` for UI motion** — it feels mechanical.
57Exceptions only: infinite loaders, color interpolation.
58
59Spring physics (JS): `stiffness: 170 / damping: 26 / mass: 1` — for playful bounce.
60
61---
62
63## Micro-Interaction Patterns
64
65### Button
66```css
67/* Press */
68button:active { transform: scale(0.97); transition: transform 80ms var(--ease-in); }
69/* Release */
70button { transition: transform 200ms var(--ease-out); }
71/* Loading state: swap label for inline spinner — no layout shift */
72/* Success: color → green + checkmark, 300ms */
73/* Disabled: opacity 0.5, remove all hover states */
74```
75
76### Form field
77```
78Focus: border-color transition 150ms --ease-inout
79Success: green border + checkmark fade-in 200ms
80Error: red border + horizontal shake (±3px, 3 cycles, 300ms) + error text slide-down
81```
82
83### Toast / Notification
84```
85Enter: slide from edge + fade-in, 300ms --ease-out
86Persist: success = 3–5s, error = persistent (user must dismiss)
87Exit: fade + vertical slide, 200ms --ease-in
88Stack: 50ms stagger between simultaneous toasts
89```
90
91### Modal
92```
93Backdrop: opacity 0 → 0.5, 200ms
94Dialog: scale 0.95 → 1.0 + fade-in, 250ms --ease-out
95Dismiss: reverse with --ease-in
96```
97
98---
99
100## Emotional Motion Vocabulary
101
102| Mood | Curve | Duration |
103|---|---|---|
104| Confident | ease-out | 200–250ms |
105| Playful | spring / slight overshoot | 300–400ms |
106| Urgent | ease-in, sharp | 100–150ms |
107| Calm | ease-in-out, gentle | 300–500ms |
108| Celebratory | particles / radial burst | 600–1000ms, once only |
109
110---
111
112## Reduced Motion (Accessibility Required)
113
114```css
115@media (prefers-reduced-motion: reduce) {
116 *, *::before, *::after {
117 animation-duration: 0.01ms !important;
118 animation-iteration-count: 1 !important;
119 transition-duration: 0.01ms !important;
120 scroll-behavior: auto !important;
121 }
122}
123```
124
125**Replace, don't remove:**
126- Slide → instant appear or opacity fade
127- Bounce/spin → static indicator
128- Parallax → fixed background
129- Keep: progress bars and functional loaders (simplify only)
130
131---
132
133## Anti-Fake-Pass Rules
134
135Before claiming motion is done, you MUST show:
136- [ ] Duration specified for every animated element (ms value, not "fast"/"slow")
137- [ ] Easing curve used — no `linear` without justification
138- [ ] `prefers-reduced-motion` override present in CSS
139- [ ] No animation exceeds 1000ms in a regular workflow path
140
141Reference: `gates/anti-fake-pass-gate.md`