mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 09:37:15 +00:00
feat(ui-design): add comprehensive UI/UX design plugin v1.0.0
New plugin covering mobile (iOS, Android, React Native) and web applications with modern design patterns, accessibility, and design systems. Components: - 9 skills: design-system-patterns, accessibility-compliance, responsive-design, mobile-ios-design, mobile-android-design, react-native-design, web-component-design, interaction-design, visual-design-foundations - 4 commands: design-review, create-component, accessibility-audit, design-system-setup - 3 agents: ui-designer, accessibility-expert, design-system-architect Marketplace updated: - Version bumped to 1.3.4 - 102 agents (+3), 116 skills (+9)
This commit is contained in:
297
plugins/ui-design/skills/interaction-design/SKILL.md
Normal file
297
plugins/ui-design/skills/interaction-design/SKILL.md
Normal file
@@ -0,0 +1,297 @@
|
||||
---
|
||||
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
|
||||
|
||||
```css
|
||||
/* 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
|
||||
|
||||
```tsx
|
||||
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
|
||||
```tsx
|
||||
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
|
||||
```tsx
|
||||
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**:
|
||||
```tsx
|
||||
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**:
|
||||
```tsx
|
||||
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**:
|
||||
```tsx
|
||||
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**:
|
||||
```tsx
|
||||
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
|
||||
|
||||
```css
|
||||
@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
|
||||
|
||||
```css
|
||||
.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
|
||||
|
||||
```css
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
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
|
||||
|
||||
1. **Performance First**: Use `transform` and `opacity` for smooth 60fps
|
||||
2. **Reduce Motion Support**: Always respect `prefers-reduced-motion`
|
||||
3. **Consistent Timing**: Use a timing scale across the app
|
||||
4. **Natural Physics**: Prefer spring animations over linear
|
||||
5. **Interruptible**: Allow users to cancel long animations
|
||||
6. **Progressive Enhancement**: Work without JS animations
|
||||
7. **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-change` sparingly for optimization
|
||||
|
||||
## Resources
|
||||
|
||||
- [Framer Motion Documentation](https://www.framer.com/motion/)
|
||||
- [CSS Animation Guide](https://web.dev/animations-guide/)
|
||||
- [Material Design Motion](https://m3.material.io/styles/motion/overview)
|
||||
- [GSAP Animation Library](https://greensock.com/gsap/)
|
||||
Reference in New Issue
Block a user