interaction-design
wshobson/agents
Design microinteractions, motion, and transitions that enhance UI polish and user delight.
What is interaction-design?
This skill provides patterns and principles for creating engaging interactions through purposeful motion, feedback, and state transitions. Use it when adding microinteractions, loading states, page transitions, gesture-based interactions, or any UI polish that requires smooth, accessible animations.
- Implement microinteractions with spring physics and easing functions
- Create loading states with skeleton screens and progress indicators
- Design smooth page and component transitions with AnimatePresence
- Build gesture-based interactions like swipe-to-dismiss and drag-and-drop
- Develop feedback patterns including ripple effects and state toggles
- Apply CSS keyframe animations and transitions for performant motion
How to install interaction-design
npx skills add https://github.com/wshobson/agents --skill interaction-design- Framer Motion library (for React examples) or CSS animation knowledge
- Understanding of CSS transforms and opacity for performance
- Basic React hooks knowledge (useState, useEffect) for interactive components
How to use interaction-design
- 1.Choose your animation approach: Framer Motion for React or CSS animations for vanilla JS
- 2.Select appropriate timing durations based on interaction type (100-500ms range)
- 3.Apply easing functions (ease-out for entering, ease-in for exiting, spring for playful)
- 4.Implement the pattern (button microinteraction, loading state, page transition, etc.)
- 5.Test animations on actual devices to verify 60fps performance
- 6.Add prefers-reduced-motion support for accessibility compliance
- 7.Iterate based on user feedback and performance metrics
Use cases
- Adding hover and click feedback to buttons and interactive elements
- Implementing skeleton screens and loading indicators during data fetches
- Creating smooth page transitions in single-page applications
- Building swipe-to-dismiss cards or gesture-based interfaces
- Designing notification and toast notification systems with animations
- Frontend developers building polished user interfaces
- Product designers implementing interaction specifications
- Full-stack developers enhancing application UX
- Teams prioritizing accessibility and motion preferences
interaction-design FAQ
Framer Motion provides declarative React components with gesture support and layout animations, ideal for complex interactive components. CSS animations are lighter-weight, work without JavaScript, and are better for simple transitions. Use Framer Motion for gesture interactions and complex choreography; use CSS for basic hover states and transitions.
Only animate `transform` and `opacity` properties, which don't trigger layout recalculations. Avoid animating `width`, `height`, `top`, `left`, or other layout properties. Use `will-change` sparingly, and test on actual devices since performance varies significantly.
No. Over-animation causes fatigue and can harm usability. Animate purposefully to provide feedback, show orientation, direct attention, or maintain continuity. Keep animations brief (100-500ms) and interruptible so users can proceed with their task.
Use the `prefers-reduced-motion` media query in CSS or check `window.matchMedia('(prefers-reduced-motion: reduce)').matches` in JavaScript. When detected, either disable animations entirely or reduce their duration to near-zero (0.01ms).
Use ease-out (decelerate) for elements entering the screen, ease-in (accelerate) for exiting, ease-in-out for moving between states, and spring curves for playful, natural-feeling interactions. Avoid linear easing as it feels mechanical.
Full instructions (SKILL.md)
Source of truth, from wshobson/agents.
name: interaction-design description: Design and implement microinteractions, motion design, transitions, and user feedback patterns. Use when adding polish to UI interactions, implementing loading states, or creating delightful user experiences.
Interaction Design
Create engaging, intuitive interactions through motion, feedback, and thoughtful state transitions that enhance usability and delight users.
When to Use This Skill
- Adding microinteractions to enhance user feedback
- Implementing smooth page and component transitions
- Designing loading states and skeleton screens
- Creating gesture-based interactions
- Building notification and toast systems
- Implementing drag-and-drop interfaces
- Adding scroll-triggered animations
- Designing hover and focus states
Core Principles
1. Purposeful Motion
Motion should communicate, not decorate:
- Feedback: Confirm user actions occurred
- Orientation: Show where elements come from/go to
- Focus: Direct attention to important changes
- Continuity: Maintain context during transitions
2. Timing Guidelines
| Duration | Use Case |
|---|---|
| 100-150ms | Micro-feedback (hovers, clicks) |
| 200-300ms | Small transitions (toggles, dropdowns) |
| 300-500ms | Medium transitions (modals, page changes) |
| 500ms+ | Complex choreographed animations |
3. Easing Functions
/* Common easings */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* Decelerate - entering */
--ease-in: cubic-bezier(0.55, 0, 1, 0.45); /* Accelerate - exiting */
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* Both - moving between */
--spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* Overshoot - playful */
Quick Start: Button Microinteraction
import { motion } from "framer-motion";
export function InteractiveButton({ children, onClick }) {
return (
<motion.button
onClick={onClick}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
className="px-4 py-2 bg-blue-600 text-white rounded-lg"
>
{children}
</motion.button>
);
}
Interaction Patterns
1. Loading States
Skeleton Screens: Preserve layout while loading
function CardSkeleton() {
return (
<div className="animate-pulse">
<div className="h-48 bg-gray-200 rounded-lg" />
<div className="mt-4 h-4 bg-gray-200 rounded w-3/4" />
<div className="mt-2 h-4 bg-gray-200 rounded w-1/2" />
</div>
);
}
Progress Indicators: Show determinate progress
function ProgressBar({ progress }: { progress: number }) {
return (
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<motion.div
className="h-full bg-blue-600"
initial={{ width: 0 }}
animate={{ width: `${progress}%` }}
transition={{ ease: "easeOut" }}
/>
</div>
);
}
2. State Transitions
Toggle with smooth transition:
function Toggle({ checked, onChange }) {
return (
<button
role="switch"
aria-checked={checked}
onClick={() => onChange(!checked)}
className={`
relative w-12 h-6 rounded-full transition-colors duration-200
${checked ? "bg-blue-600" : "bg-gray-300"}
`}
>
<motion.span
className="absolute top-1 left-1 w-4 h-4 bg-white rounded-full shadow"
animate={{ x: checked ? 24 : 0 }}
transition={{ type: "spring", stiffness: 500, damping: 30 }}
/>
</button>
);
}
3. Page Transitions
Framer Motion layout animations:
import { AnimatePresence, motion } from "framer-motion";
function PageTransition({ children, key }) {
return (
<AnimatePresence mode="wait">
<motion.div
key={key}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
</AnimatePresence>
);
}
4. Feedback Patterns
Ripple effect on click:
function RippleButton({ children, onClick }) {
const [ripples, setRipples] = useState([]);
const handleClick = (e) => {
const rect = e.currentTarget.getBoundingClientRect();
const ripple = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
id: Date.now(),
};
setRipples((prev) => [...prev, ripple]);
setTimeout(() => {
setRipples((prev) => prev.filter((r) => r.id !== ripple.id));
}, 600);
onClick?.(e);
};
return (
<button onClick={handleClick} className="relative overflow-hidden">
{children}
{ripples.map((ripple) => (
<span
key={ripple.id}
className="absolute bg-white/30 rounded-full animate-ripple"
style={{ left: ripple.x, top: ripple.y }}
/>
))}
</button>
);
}
5. Gesture Interactions
Swipe to dismiss:
function SwipeCard({ children, onDismiss }) {
return (
<motion.div
drag="x"
dragConstraints={{ left: 0, right: 0 }}
onDragEnd={(_, info) => {
if (Math.abs(info.offset.x) > 100) {
onDismiss();
}
}}
className="cursor-grab active:cursor-grabbing"
>
{children}
</motion.div>
);
}
CSS Animation Patterns
Keyframe Animations
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.animate-fadeIn {
animation: fadeIn 0.3s ease-out;
}
.animate-pulse {
animation: pulse 2s ease-in-out infinite;
}
.animate-spin {
animation: spin 1s linear infinite;
}
CSS Transitions
.card {
transition:
transform 0.2s ease-out,
box-shadow 0.2s ease-out;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}
Accessibility Considerations
/* Respect user motion preferences */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
function AnimatedComponent() {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
return (
<motion.div
animate={{ opacity: 1 }}
transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
/>
);
}
Best Practices
- Performance First: Use
transformandopacityfor smooth 60fps - Reduce Motion Support: Always respect
prefers-reduced-motion - Consistent Timing: Use a timing scale across the app
- Natural Physics: Prefer spring animations over linear
- Interruptible: Allow users to cancel long animations
- Progressive Enhancement: Work without JS animations
- Test on Devices: Performance varies significantly
Common Issues
- Janky Animations: Avoid animating
width,height,top,left - Over-animation: Too much motion causes fatigue
- Blocking Interactions: Never prevent user input during animations
- Memory Leaks: Clean up animation listeners on unmount
- Flash of Content: Use
will-changesparingly for optimization
Related skills
More from wshobson/agents and the wider catalog.
tailwind-design-system
Build production-ready design systems with Tailwind CSS v4, design tokens, and component libraries.
typescript-advanced-types
Master TypeScript's advanced type system: generics, conditional types, mapped types, and utility types for type-safe applications.
nodejs-backend-patterns
Build production-ready Node.js backends with Express/Fastify, middleware patterns, auth, and database integration.
python-performance-optimization
Profile and optimize Python code using cProfile, memory profilers, and performance best practices.
brand-landingpage
Brand-first landing page designer with guided interviews and Stitch-powered iteration.
python-testing-patterns
Implement comprehensive testing strategies with pytest, fixtures, mocking, and test-driven development.