Context
Motion design separates premium sites from templates. But poorly implemented animations cause performance issues, accessibility violations, and annoying delays. This skill defines a motion system that adds perceived quality while staying within web performance guardrails.
Procedure
- Define the motion personality based on brand voice: "calm and precise" → subtle fades and slides, slow easing. "Bold and energetic" → faster entrances, spring physics, scale effects.
- Set global timing tokens: entrance duration (300-500ms), exit duration (200ms), interaction duration (150ms), easing curve (cubic-bezier values).
- Design scroll-triggered entrance animations for each section pattern: fade-up (most common), scale-in (for emphasis), slide-in (for sequential reveals), stagger (for card grids).
- Define hover and focus states for interactive elements: buttons (scale + shadow), links (underline slide), cards (lift + border glow).
- Specify loading and transition states: skeleton screens, page transitions, lazy image fade-in.
- Create the reduced-motion fallback: replace all motion with instant opacity changes (0 → 1) or remove entirely.
Output Format
# Animation & Motion Spec
## Motion Tokens
| Token | Value | Usage |
|-------|-------|-------|
| --duration-entrance | 400ms | Section reveals |
| --duration-interaction | 150ms | Hover, focus |
| --duration-exit | 200ms | Modal close, drawer |
| --ease-default | cubic-bezier(0.25, 0.1, 0.25, 1) | Standard easing |
| --ease-spring | cubic-bezier(0.34, 1.56, 0.64, 1) | Playful bounce |
## Scroll Entrances
| Section | Animation | Delay | Trigger |
|---------|-----------|-------|---------|
| Hero | none (instant) | 0ms | Page load |
| Feature grid | fade-up + stagger | 100ms between cards | 20% in viewport |
| Testimonial | fade-in | 0ms | 30% in viewport |
| CTA block | scale-in | 0ms | 50% in viewport |
## Interaction States
| Element | Idle | Hover | Active | Focus |
|---------|------|-------|--------|-------|
| Primary button | default | scale(1.02), shadow-lg | scale(0.98) | ring-2 |
| Card | default | translateY(-2px), shadow-md | — | ring-2 |
| Link | default | underline slide-in | — | outline |
| Icon button | default | bg opacity change | scale(0.95) | ring-2 |
## Loading States
| Scenario | Animation |
|----------|-----------|
| Page load | Skeleton pulse (bg shimmer) |
| Image lazy load | opacity 0 → 1 over 300ms |
| Route transition | fade-out 200ms → fade-in 300ms |
| Form submit | Button → spinner, disable |
## Reduced Motion
@media (prefers-reduced-motion: reduce) {
- All scroll entrances: instant opacity (no transform)
- All hovers: instant state change (no transition)
- All durations: 0ms
- Loading skeletons: static gray (no pulse)
}
## Implementation Notes
- Use Intersection Observer for scroll triggers (not scroll event listeners)
- CSS transitions for hover states (no JS needed)
- Framer Motion or CSS @keyframes for entrance animations
- will-change: transform only during animation, remove after
QA Rubric (scored)
- Accessibility (0-5): prefers-reduced-motion fully implemented and tested.
- Performance (0-5): zero CLS from animations, no LCP delay.
- Subtlety (0-5): animations enhance without distracting — visitor focuses on content, not effects.
- Consistency (0-5): same easing and timing across all similar interactions.
Examples (good/bad)
- Good: "Feature cards fade-up with 100ms stagger, 400ms duration, ease-out. Reduced-motion: instant opacity. Trigger: 20% viewport intersection. Uses Intersection Observer, no scroll listeners."
- Bad: "Everything slides in from the left with a 1-second bounce animation. No reduced-motion fallback. Uses scroll event listener with no throttle."
Variants
- Static mode: zero motion — all states are instant. For maximum performance and accessibility-first sites.
- Cinematic mode: parallax backgrounds, reveal masks, staggered text lines — for portfolio and creative agency sites where motion is the product.