mcpField
U
ui-animation

ui-animation

github.com/lukasstricklerDesign & UI453 installsUpdated 35w ago
Repository

About ui-animation

A workflow skill for designing UI animations with guidance on timing, Motion and CSS/Tailwind techniques, accessibility, and performance. It is for developers implementing interface transitions, gestures, and layout changes.

Key features

  1. 01Selects techniques by animation type
  2. 02Provides easing and duration guidance
  3. 03Covers Motion gestures and layout transitions
  4. 04Accounts for reduced-motion preferences
  5. 05Includes performance and focus checks
  6. 06References reusable animation recipes

Use cases

  1. 01Adding modal and dropdown enter/exit effects
  2. 02Animating gesture-driven controls and layout changes
  3. 03Adapting motion for reduced-motion users
  4. 04Checking animation performance and focus behavior

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/lukasstrickler/ai-dev-atelier --skill ui-animation

After installing, type/ui-animationin Claude Code to confirm it loaded.

Bundled filesreferences/recipes.md

Details

From the project README

UI Animation

Tasteful UI animation with proper timing, accessibility, and performance.

Quick Start

Technique Decision:

  • Simple transition? → CSS/Tailwind (transition-*, animate-*)
  • Enter/exit with unmount? → Motion + AnimatePresence
  • Gesture-driven? → Motion springs
  • Layout changes? → Motion layout prop

Default: Start with Easing Decision Tree → Duration Guidelines → Implement → Add a11y → Verify

Core Principles

  1. Natural Motion: Mimic physics. Avoid linear easing - nothing moves at constant speed.
  2. Purposeful: Every animation must add meaning. If you can't explain its benefit, remove it.
  3. Fast: UI animations under 300ms. Hover effects under 150ms. Over 500ms feels sluggish.
  4. Interruptible: Use springs for gesture-driven animations - they handle interruption gracefully.
  5. Accessible: Always respect prefers-reduced-motion. Non-negotiable.

Workflow

Step 1: Classify Animation Type
TypeExamplesTechnique
Micro-interactionButton press, toggle, checkboxCSS/Tailwind
Enter/ExitModal, toast, dropdownMotion + AnimatePresence
Layout changeAccordion, reorder, expandMotion layout prop
Shared elementTab indicator, card expandMotion layoutId
GestureDrag, swipe, pull-to-refreshMotion springs
Step 2: Choose Timing
  1. Use Easing Decision Tree below to select curve
  2. Use Duration Guidelines to select timing
  3. For gestures, use Spring Animations config
Step 3: Implement
  1. Check references/recipes.md for copy-paste patterns
  2. Apply timing from Step 2
  3. Wrap unmounting elements in AnimatePresence

Use ui_to_artifact when starting from a design screenshot or mockup. Use ui_diff_check to compare expected vs implemented UI.

Step 4: Accessibility (Required)
  1. Check prefers-reduced-motion with useReducedMotion() or motion-safe:
  2. Simplify to opacity-only for reduced motion users
  3. Verify focus timing (move focus AFTER animation starts)
Step 5: Verify
  • Exit animations run (not instant unmount)
  • opacity: 0 elements have pointerEvents: none
  • Focus moves after animation starts, not before
  • Only animating transform and opacity

Easing

Decision Tree
What triggers the animation?
│
├─ User action (click, tap, open)?
│  └─ Use: ease-out (fast start, slow end = responsive)
│
├─ Element moving on-screen (tab switch, reorder)?
│  └─ Use: ease-in-out (accelerate then decelerate)
│
├─ Continuous/looping (spinner, marquee)?
│  └─ Use: linear (constant speed appropriate here)
│
├─ Gesture-based (drag, swipe, pull)?
│  └─ Use: Spring animation (physics-based, interruptible)
│
└─ Hover/focus effect?
   └─ Use: CSS ease, 150ms (subtle, immediate)
Quick Reference
PurposeCSSTailwindDuration
Modal/drawer entercubic-bezier(0.32, 0.72, 0, 1)ease-out duration-200200ms
Modal/drawer exitcubic-bezier(0.32, 0.72, 0, 1)ease-out duration-150150ms
On-screen movementcubic-bezier(0.4, 0, 0.2, 1)ease-in-out duration-200200-300ms
Hover effecteaseease duration-150150ms
Button press—active:scale-[0.97]instant
Pro Curves
NameValueUse Case
Vaul (buttery)cubic-bezier(0.32, 0.72, 0, 1)Sheets, drawers, modals
Emphasizedcubic-bezier(0.2, 0, 0, 1)Material Design 3
Snappycubic-bezier(0.25, 1, 0.5, 1)Fast UI transitions

Avoid: Built-in ease-in—starts slow, feels sluggish.

Duration Guidelines

TypeDurationNotes
Micro-feedback100-150msButton press, toggle, checkbox
Small transition150-250msTooltip, icon morph
Medium transition200-300msModal, popover, dropdown
Large transition300-400msPage transition, complex layout
Maximum<500msExceptions: onboarding, data viz

Key Rules:

  • Exit faster than enter: 200ms enter → 150ms exit
  • Hover = fast: Under 150ms
  • High-frequency = instant: Keyboard nav, scrolling—<100ms or none

Spring Animations

Duration-based (Recommended)

Easier to compose with other timed animations. Use visualDuration (time to visually reach target) and bounce (0 = no bounce, 1 = very bouncy).

FeelConfigUse Case
Snappy{ duration: 0.3, bounce: 0.15 }Tabs, buttons, quick feedback
Standard{ duration: 0.4, bounce: 0.2 }Modals, menus, general UI
Gentle{ duration: 0.5, bounce: 0.25 }Smooth, human-like flow
Physics-based (Legacy/Advanced)

Use when integrating with physics libraries or when precise control over spring dynamics is needed.

FeelConfigUse Case
Snappy{ stiffness: 400, damping: 30 }High-frequency interactions
Standard{ stiffness: 300, damping: 20 }Framer Handshake convention
Gentle{ stiffness: 120, damping: 14 }react-motion preset

Gotcha: stiffness/damping/mass overrides duration/bounce. Pick one approach—don't mix.

Layout Animations

The layout Prop

Add layout to animate position/size changes automatically. Use layout="position" for text (prevents distortion).

Prop ValueEffectUse Case
layout={true}Animates position AND sizeDefault for flexible elements
layout="position"Animates only translationText/icons that shouldn't stretch
layout="size"Animates only dimensionsFixed-position expanding panels
Shared Element Transitions (layoutId)

Elements with matching layoutId animate between each other when entering/exiting.

Critical Trap: Duplicate layoutId values cause elements to teleport across the page. Use unique IDs per context or wrap in <LayoutGroup id="...">.

Layout Gotchas
  • Text distortion: Apply layout="position" to text elements
  • Border radius: Can warp during scale—Motion auto-corrects, but test it
  • SVG elements: layout doesn't work on <path>—use manual morphing

Gesture Gotchas

ProblemSolution
Touch scroll conflictsdragPropagation={false}
Element snaps backCheck dragConstraints + dragElastic
Momentum feels wrongdragMomentum={false} for precise UIs
One-direction onlydragElastic={{ top: 0, bottom: 0.5 }}

Swipe dismiss: Check BOTH distance AND velocity—users expect flicks to work.

Accessibility

prefers-reduced-motion (REQUIRED)
import { useReducedMotion } from "motion/react"

const shouldReduce = useReducedMotion()
const variants = shouldReduce 
  ? { opacity: 1 }                    // Fade only
  : { opacity: 1, scale: 1, y: 0 }    // Full animation

Tailwind: motion-safe:animate-pulse / motion-reduce:transition-none

Best practice: Don't disable—simplify. Remove spatial movement, keep opacity.

Focus Management
  • Move focus AFTER animation starts: requestAnimationFrame(() => ref.focus())
  • Restore focus to trigger on close
  • Don't animate inside aria-live regions
Touch Targets
StandardSizeTailwindPhysical
Material Design48×48 dpmin-h-12 min-w-12~9mm (recommended)
Apple HIG44×44 ptmin-h-11 min-w-11~7mm
WCAG 2.2 (AA)24×24 pxmin-h-6 min-w-6~5mm (minimum)

Why? Average adult finger pad is ~9mm. Targets below 7mm cause "fat finger" errors. Use Material's 48dp for cross-platform; Apple's 44pt is iOS-specific minimum.

Performance

Golden Rules
  1. Only animate transform and opacity—GPU-accelerated
  2. Never animate: width, height, top, left, margin, padding
  3. will-change sparingly—only during animation, remove after
  4. Blur thresholds:
    • ≤10px: Safe for animation
    • 11-20px: May cause jank on mobile/4K—test thoroughly
    • 20px: Avoid for real-time effects; use pre-blurred images instead

  5. Prefer CSS over JS for simple transitions
Key Traps
  • Height animation: Use layout prop, not animate={{ height }}
  • Invisible but clickable: opacity: 0 still receives clicks—add pointerEvents: "none"
  • will-change everywhere: Causes layer explosion, mobile crashes

See references/recipes.md for detailed examples.

Examples

Copy-paste patterns organized by category in references/recipes.md:

  • Common UI Patterns: Button press, modal enter/exit, error shake, staggered lists, accordion
  • Touch & Interaction: Accessible touch targets, hover on touch devices, instant tooltips
  • Layout Animations: layout prop, layoutId shared elements, collision fixes
  • Radix UI Integration: forceMount pattern, asChild, origin-aware popovers
  • Accessibility: Focus timing, focus restoration, reduced motion variants
  • Performance: Height animation (use layout), invisible-but-clickable fix, will-change
  • Exit Patterns: popLayout with forwardRef, SSR hydration (initial={false})
  • Gestures: Swipe dismiss with velocity check, elastic drag boundaries

AnimatePresence

ModeBehaviorUse Case
sync (default)Simultaneous enter/exitCrossfades, overlays
waitExit completes before enterPage transitions, tabs
popLayoutExiting elements leave flowList removals (with layout)
Exit Animation Trap

Exit animations require AnimatePresence—without it, unmount is instant:

// ❌ Exit never runs
{isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}

// ✅ Wrap in AnimatePresence
<AnimatePresence>
  {isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}
</AnimatePresence>

SSR: Use <AnimatePresence initial={false}> to prevent animation on page load.

Anti-patterns

Don'tDo InsteadWhy
scale(0) startscale(0.9) or higherAvoids "popping" effect
linear for UIease-out or springsLinear feels robotic
Animations >500msKeep under 300msFeels sluggish
Same tooltip delayFirst: 400ms, subsequent: 0msUser mental model
Skip reduced-motionAlways motion-safe:Accessibility
Animate layout propsUse transform: scale()Performance
Excessive bouncebounce: 0-0.2Unprofessional

tailwindcss-animate

Tailwind v4: Define keyframes via @theme in CSS, not config.

CategoryClasses
Enteranimate-in fade-in zoom-in-95 slide-in-from-top
Exitanimate-out fade-out zoom-out-95 slide-out-to-top
Timingdelay-150 duration-500
Fill Modefill-mode-forwards fill-mode-backwards

Integration with Other Skills

WhenSkillWhy
After implementingcode-qualityEnsure code passes checks
Reusable patternsdocs-writeDocument component API
Before committinggit-commitUse feat(ui): or style:
Integration issuessearchLook up latest patterns

Output

  • Artifacts: Code changes only (no .ada/ outputs)
  • Modifications: Component animations, CSS/Tailwind styles, Motion configs
  • Type: Workflow skill (guidance only, no scripts)

References

Internal
External

Comments

Markdown supported