--- name: crafting-ui-animations description: Designs and implements purposeful UI motion — micro-interactions, page-load and scroll-reveal sequences, transitions, and keyframe or Web Animations API animations that stay smooth and respect reduced-motion preferences. Use when the user asks to add an animation, transition, hover or press effect, loading indicator, scroll reveal, or page-load sequence, or to fix janky or excessive motion. Do not use for accessibility audits (ensuring-accessibility) or loading-performance work (optimizing-web-performance). --- # Crafting UI Animations ## When to use / when NOT to use - **Use for:** adding or refining motion — micro-interactions, enter/exit transitions, scroll reveals, load sequences, loading states — and fixing jank or motion overload. - **Do NOT use for:** WCAG audits (ensuring-accessibility), bundle/loading speed (optimizing-web-performance), or pure layout work. ## Core rules 1. **Motion serves purpose.** Each animation must communicate state, direct attention, or express identity — one orchestrated moment beats scattered effects. If you can't name its purpose, delete it. 2. **Animate only `transform` and `opacity`** (compositor-friendly); never animate `width`, `height`, `top`, `left`, or `margin`. - ✅ `transform: translateY(8px) → none` - ❌ `top: 8px → 0` (forces layout every frame) 3. **Never move layout after load** — reserve space; animate overlays and transforms so CLS stays ≤0.1. - ✅ banner slides over content, or space is pre-reserved - ❌ banner insertion pushes the page down 4. **Duration/easing tokens:** UI feedback 150–300ms; enter `ease-out`, exit `ease-in`; larger scene changes ≤500ms. Define once, reuse everywhere. 5. **Choose the lightest tool that works:** two states → CSS `transition`; multi-step/looping → CSS `@keyframes`; runtime-computed values, sequencing, or interruption → Web Animations API. No animation library for what these three cover. 6. **Always honor `prefers-reduced-motion`** — gate every non-essential animation, swap movement for opacity or nothing. 7. **Hierarchy of restraint:** micro-interactions everywhere are fine (≤200ms, subtle); attention-seeking motion (bounce, pulse) at most one element per view. ## Workflow 1. Name the purpose of each requested animation (state feedback / attention / identity). Cut anything purposeless. 2. Define or reuse motion tokens (durations, easings) as CSS custom properties. 3. Implement with the lightest tool per rule 5, animating only transform/opacity. 4. Add the `prefers-reduced-motion` fallback for every animation you wrote. 5. **Validate:** trigger each animation — no layout shift (DevTools → Performance → Layout Shift regions), steady 60fps (no long purple layout bars), interruption behaves (rapid hover on/off doesn't stutter). 6. **Reduced-motion check:** enable "Emulate CSS prefers-reduced-motion" in DevTools Rendering panel and re-run the flow — nothing essential may be lost. ## Edge cases & failure modes - **Animating `display: none` → visible:** `display` can't transition; use `@starting-style` + `transition-behavior: allow-discrete`, or WAAPI with a visibility swap. - **Scroll reveals below the fold:** use `IntersectionObserver`, reveal once, and ensure content is visible without JS (progressive enhancement — never leave `opacity: 0` as the no-JS state). - **Infinite/looping animation** (spinners excepted) → must pause when not visible (`animation-play-state`, or stop the WAAPI animation) and under reduced motion. - **Jank persists despite transform/opacity** → check for unintentionally huge paint areas; promote with `will-change: transform` sparingly and remove it after the animation. - **Autoplaying motion >5s** must have a pause control (WCAG 2.2.2) — flag this instead of shipping it silently. ## References Copy-paste patterns (motion tokens, enter/exit, scroll reveal, WAAPI sequencing, reduced-motion): see [references/patterns.md](references/patterns.md)