generic-react-ux-designer · diff
git:20251217.3f83b43 to git:20251217.1edc6e2
277 added, 197 removed. Audit A to A.
---
name: generic-react-ux-designer
description: Professional UI/UX design expertise for React applications. Covers design thinking, user psychology (Hick's/Fitts's/Jakob's Law), visual hierarchy, interaction patterns, accessibility, performance-driven design, and design critique. Use when designing features, improving UX, solving user problems, or conducting design reviews.
---
# React UX Designer
- Professional UX expertise for React/TypeScript applications. Apply design thinking to solve real user problems.
-
- **Foundational Reference:** See [UX Principles](./../_shared/UX_PRINCIPLES.md) for core concepts.
-
- ## Design Philosophy
-
- **Your Role as UX Designer:**
- 1. **Critique** - Evaluate designs against professional standards
- 2. **Improve** - Suggest specific, actionable improvements
- 3. **Innovate** - Propose solutions based on UX best practices
- 4. **Educate** - Explain the "why" behind decisions
-
- **Project Constraints (Define in CLAUDE.md):**
- - Brand colors and design tokens
- - Performance budgets (load time, bundle size, animation fps)
- - Privacy requirements
- - Accessibility level (AA minimum)
-
- ## Design Thinking for React
-
- ### Jobs-to-be-Done Framework
-
- Every feature has a "job" the user is hiring it to do:
-
- ```
- When I [situation]
- I want to [motivation]
- So I can [expected outcome]
- ```
-
- **Example:**
- ```
- When I'm quickly capturing a task
- I want to add it without leaving my current view
- So I can stay focused on what I'm doing
- ```
-
- **Design Implications:** Inline quick-add, keyboard shortcuts, minimal modal usage.
-
- ### Progressive Disclosure
-
- Show only what's necessary, when necessary:
-
- | Level | Visibility | Examples |
- |-------|------------|----------|
- | Primary | Always visible | Core functionality |
- | Secondary | On hover/focus | Supporting actions |
- | Tertiary | In menus/modals | Advanced features |
+ Professional UX expertise for React/TypeScript applications.
- ### Cognitive Load Reduction
+ **Extends:** [Generic UX Designer](../generic-ux-designer/SKILL.md) - Read base skill for design thinking process, user psychology, heuristic evaluation, and research methods.
- - **Chunking**: Group related items (max 7 per group)
- - **Recognition > Recall**: Show options, don't require memorization
- - **Smart Defaults**: Pre-fill with intelligent defaults
- - **Consistency**: Same patterns across the app
+ ## React Interaction Patterns
- ## Visual Design for React
+ ### Micro-interactions with Framer Motion
- ### Visual Hierarchy
+ ```tsx
+ // Checkbox animation
+ <motion.div
+ animate={{ scale: checked ? 1 : 0 }}
+ transition={{ type: "spring", stiffness: 500 }}
+ >
+ <Check className="w-4 h-4" />
+ </motion.div>
- **Typography Scale:**
- ```
- H1: 36-48px - Page titles
- H2: 24-30px - Section titles
- Body: 16px - Default text
- Small: 14px - Captions, metadata
- ```
+ // Button press feedback
+ <motion.button
+ whileTap={{ scale: 0.98 }}
+ whileHover={{ scale: 1.02 }}
+ transition={{ type: "spring", stiffness: 400 }}
+ >
+ Click me
+ </motion.button>
- **Weight Hierarchy:**
- ```
- Bold (700) - Primary actions, headings
- Semibold (600) - Subheadings
- Regular (400) - Body text
+ // Toast notification
+ <motion.div
+ initial={{ opacity: 0, y: 50 }}
+ animate={{ opacity: 1, y: 0 }}
+ exit={{ opacity: 0, y: 50 }}
+ >
+ <Toast message={message} />
+ </motion.div>
```
- ### Gestalt Principles in Components
+ ### Loading States
- **Proximity** - Related items close together:
```tsx
- // Group related form fields
- <div className="space-y-8">
- <div className="space-y-2">
- <h2>Account</h2>
- <Input label="Email" />
- <Input label="Password" />
- </div>
- <div className="space-y-2">
- <h2>Profile</h2>
- <Input label="Name" />
- </div>
+ // Skeleton (preferred over spinners)
+ <div className="animate-pulse space-y-4">
+ <div className="h-8 bg-slate-200 rounded w-3/4" />
+ <div className="h-4 bg-slate-200 rounded" />
</div>
- ```
- **Similarity** - Same styling for related elements:
- ```tsx
- <div className="flex gap-2">
- <button className="btn-secondary">Edit</button>
- <button className="btn-secondary">Share</button>
- <button className="btn-danger">Delete</button> {/* Different = different action type */}
+ // Progress indicator for long operations
+ <div className="relative w-full h-2 bg-slate-200 rounded">
+ <motion.div
+ className="absolute h-full bg-primary rounded"
+ initial={{ width: 0 }}
+ animate={{ width: `${progress}%` }}
+ />
</div>
```
- ## Interaction Design Patterns
-
- ### Affordances
+ ### Optimistic UI Pattern
- Interactive elements must look interactive:
```tsx
- // Clear button affordance
- <button className="px-6 py-3 bg-primary text-white rounded-lg shadow hover:-translate-y-1 transition-all cursor-pointer">
- Click Me
- </button>
+ const handleLike = async () => {
+ // Update UI immediately
+ setLiked(true);
+ setCount(c => c + 1);
- // NOT this (no affordance)
- <div onClick={handler}>Click Me</div>
+ try {
+ await api.like(id);
+ } catch {
+ // Rollback on error
+ setLiked(false);
+ setCount(c => c - 1);
+ toast.error('Failed to save');
+ }
+ };
```
- ### Feedback Timing
-
- | Type | Timing | Example |
- |------|--------|---------|
- | Instant | < 100ms | Button press, hover |
- | Expected | 1-3s | Loading spinner |
- | Long | > 3s | Progress bar with % |
-
- ### Micro-interactions
+ ## React Accessibility Patterns
- **Checkbox animation:**
- ```tsx
- <motion.div
- animate={{ scale: checked ? 1 : 0 }}
- transition={{ type: "spring", stiffness: 500 }}
- >
- <Check className="w-4 h-4" />
- </motion.div>
- ```
+ ### Focus Management
- **Loading skeleton (better than spinners):**
```tsx
- <div className="animate-pulse space-y-4">
- <div className="h-8 bg-slate-200 rounded w-3/4" />
- <div className="h-4 bg-slate-200 rounded" />
- </div>
- ```
+ // Modal focus trap
+ function Modal({ isOpen, onClose, children }: ModalProps) {
+ const modalRef = useRef<HTMLDivElement>(null);
- ## Accessibility in React
+ useEffect(() => {
+ if (isOpen) {
+ const firstFocusable = modalRef.current?.querySelector(
+ 'button, [href], input, select, textarea'
+ ) as HTMLElement;
+ firstFocusable?.focus();
+ }
+ }, [isOpen]);
- ### Keyboard Navigation
+ // Trap focus within modal
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === 'Tab') {
+ const focusables = modalRef.current?.querySelectorAll(
+ 'button, [href], input, select, textarea'
+ );
+ // Handle tab cycling...
+ }
+ if (e.key === 'Escape') onClose();
+ };
- **Required patterns:**
- ```
- Tab - Move through interactive elements
- Shift+Tab - Move backward
- Enter/Space - Activate buttons
- Escape - Close modals
- Arrow keys - Navigate lists/menus
+ return (
+ <div
+ ref={modalRef}
+ role="dialog"
+ aria-modal="true"
+ onKeyDown={handleKeyDown}
+ >
+ {children}
+ </div>
+ );
+ }
```
- **Custom shortcuts:**
+ ### Keyboard Navigation
+
```tsx
+ // Custom keyboard shortcut
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
openCommandPalette();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
+
+ // Arrow key navigation in list
+ const handleKeyDown = (e: KeyboardEvent, index: number) => {
+ switch (e.key) {
+ case 'ArrowDown':
+ e.preventDefault();
+ focusItem(index + 1);
+ break;
+ case 'ArrowUp':
+ e.preventDefault();
+ focusItem(index - 1);
+ break;
+ }
+ };
```
- ### Screen Reader Support
+ ### Motion Accessibility
- **Semantic HTML first:**
```tsx
- // Good
- <nav><ul><li><a href="/dashboard">Dashboard</a></li></ul></nav>
+ // Respect prefers-reduced-motion
+ const prefersReducedMotion = window.matchMedia(
+ '(prefers-reduced-motion: reduce)'
+ ).matches;
- // Bad
- <div><div onClick={go}>Dashboard</div></div>
+ <motion.div
+ initial={{ opacity: 0, y: prefersReducedMotion ? 0 : 20 }}
+ animate={{ opacity: 1, y: 0 }}
+ transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
+ />
```
- **ARIA when necessary:**
+ ## Form UX Patterns
+
+ ### React Hook Form Integration
+
```tsx
- <div role="dialog" aria-modal="true" aria-labelledby="title">
- <h2 id="title">Confirm</h2>
- </div>
+ function ContactForm() {
+ const { register, handleSubmit, formState: { errors } } = useForm();
+
+ return (
+ <form onSubmit={handleSubmit(onSubmit)}>
+ <div className="space-y-1">
+ <label htmlFor="email" className="text-sm font-medium">
+ Email
+ </label>
+ <input
+ id="email"
+ {...register('email', { required: 'Email is required' })}
+ aria-invalid={errors.email ? 'true' : 'false'}
+ aria-describedby={errors.email ? 'email-error' : undefined}
+ className={cn(
+ 'input',
+ errors.email && 'border-red-500'
+ )}
+ />
+ {errors.email && (
+ <p id="email-error" className="text-sm text-red-500">
+ {errors.email.message}
+ </p>
+ )}
+ </div>
+ </form>
+ );
+ }
```
- ### Motion Accessibility
+ ### Inline Validation
- Respect `prefers-reduced-motion`:
```tsx
- const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+ // Validate on blur, show on focus
+ const [touched, setTouched] = useState(false);
+ const [error, setError] = useState('');
- <motion.div
- initial={{ opacity: 0, y: prefersReducedMotion ? 0 : 20 }}
- animate={{ opacity: 1, y: 0 }}
- transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
+ <input
+ onBlur={() => {
+ setTouched(true);
+ setError(validate(value));
+ }}
+ onFocus={() => setError('')} // Clear while editing
+ className={touched && error ? 'border-red-500' : ''}
/>
```
- ## Performance-Driven Design
+ ## Modal/Dialog Patterns
- ### Perceived Performance
+ ### Confirmation Dialog
- **Optimistic UI:**
```tsx
- const handleLike = async () => {
- setLiked(true); // Update immediately
- try {
- await api.like(id);
- } catch {
- setLiked(false); // Rollback on error
- toast.error('Failed');
- }
- };
+ function ConfirmDialog({ isOpen, onConfirm, onCancel, title, message }: Props) {
+ return (
+ <motion.div
+ initial={{ opacity: 0 }}
+ animate={{ opacity: 1 }}
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
+ >
+ <motion.div
+ initial={{ scale: 0.95 }}
+ animate={{ scale: 1 }}
+ role="alertdialog"
+ aria-modal="true"
+ aria-labelledby="dialog-title"
+ aria-describedby="dialog-desc"
+ className="bg-white rounded-xl p-6 max-w-md"
+ >
+ <h2 id="dialog-title" className="text-lg font-semibold">
+ {title}
+ </h2>
+ <p id="dialog-desc" className="mt-2 text-muted">
+ {message}
+ </p>
+ <div className="mt-4 flex gap-3 justify-end">
+ <button onClick={onCancel} className="btn-secondary">
+ Cancel
+ </button>
+ <button onClick={onConfirm} className="btn-primary">
+ Confirm
+ </button>
+ </div>
+ </motion.div>
+ </motion.div>
+ );
+ }
```
- ### Animation Performance
+ ### Command Palette (⌘K Pattern)
- **GPU-accelerated only:**
- ```css
- /* DO animate */
- transform: translateY(-4px);
- opacity: 0.5;
+ ```tsx
+ function CommandPalette({ isOpen, onClose }: Props) {
+ const [query, setQuery] = useState('');
+ const inputRef = useRef<HTMLInputElement>(null);
- /* AVOID animating */
- margin-top: -4px; /* triggers layout */
- width: 100px; /* triggers layout */
+ useEffect(() => {
+ if (isOpen) inputRef.current?.focus();
+ }, [isOpen]);
+
+ const filtered = useMemo(() =>
+ commands.filter(c =>
+ c.label.toLowerCase().includes(query.toLowerCase())
+ ),
+ [query]
+ );
+
+ return (
+ <div role="dialog" aria-label="Command palette">
+ <input
+ ref={inputRef}
+ value={query}
+ onChange={e => setQuery(e.target.value)}
+ placeholder="Type a command..."
+ aria-autocomplete="list"
+ />
+ <ul role="listbox">
+ {filtered.map(cmd => (
+ <li key={cmd.id} role="option">
+ {cmd.label}
+ </li>
+ ))}
+ </ul>
+ </div>
+ );
+ }
```
- ## Design Critique Framework
+ ## Empty & Error States
- ### Usability Heuristics (Nielsen's 10)
+ ```tsx
+ // Empty state with action
+ function EmptyState({ title, description, action }: Props) {
+ return (
+ <div className="text-center py-12">
+ <Icon className="mx-auto h-12 w-12 text-muted" />
+ <h3 className="mt-4 text-lg font-medium">{title}</h3>
+ <p className="mt-2 text-muted">{description}</p>
+ <button onClick={action.onClick} className="mt-4 btn-primary">
+ {action.label}
+ </button>
+ </div>
+ );
+ }
- 1. **System Status** - User always knows what's happening
- 2. **Real World Match** - Familiar language/concepts
- 3. **User Control** - Can undo mistakes easily
- 4. **Consistency** - Same patterns across app
- 5. **Error Prevention** - Design prevents errors
- 6. **Recognition > Recall** - Options visible
- 7. **Flexibility** - Power users can work faster
- 8. **Minimalist** - Every element necessary
- 9. **Error Recovery** - Helpful error messages
- 10. **Help** - Contextual and searchable
+ // Error state with retry
+ function ErrorState({ error, onRetry }: Props) {
+ return (
+ <div className="text-center py-12" role="alert">
+ <AlertCircle className="mx-auto h-12 w-12 text-red-500" />
+ <h3 className="mt-4 text-lg font-medium">Something went wrong</h3>
+ <p className="mt-2 text-muted">{error.message}</p>
+ <button onClick={onRetry} className="mt-4 btn-primary">
+ Try again
+ </button>
+ </div>
+ );
+ }
+ ```
- ### Quick Decision Matrix
+ ## React UX Checklist
- | Situation | Design Decision | Why |
- |-----------|----------------|-----|
- | Primary CTA | Large, prominent | Fitts's Law |
- | Destructive action | Red, confirm dialog | Error prevention |
- | 10+ form fields | Multi-step wizard | Reduce cognitive load |
- | Loading < 1s | Skeleton | Better perception |
- | Loading > 3s | Progress bar | Manage expectations |
+ **Interaction Quality:**
+ - [ ] Immediate feedback on user actions
+ - [ ] Loading states for async operations
+ - [ ] Optimistic updates where appropriate
+ - [ ] Smooth animations (60fps)
- ### Red Flags to Push Back On
+ **Accessibility:**
+ - [ ] Keyboard navigation complete
+ - [ ] Focus management in modals
+ - [ ] Motion respects prefers-reduced-motion
+ - [ ] ARIA labels on interactive elements
- - **Dark Patterns**: Hidden unsubscribe, confusing negatives, fake urgency
- - **Accessibility Violations**: No keyboard nav, low contrast, missing ARIA
- - **Performance Issues**: Layout-triggering animations, blocking main thread
- - **Poor Usability**: >7 choices without grouping, >3 clicks for critical actions
+ **Forms:**
+ - [ ] Inline validation
+ - [ ] Clear error messages
+ - [ ] Smart defaults
+ - [ ] Progress indication for multi-step
## See Also
- - [UX Principles](./../_shared/UX_PRINCIPLES.md) - Core psychology and patterns
- - [Design Patterns](./../_shared/DESIGN_PATTERNS.md) - Visual design reference
- - [Code Review Standards](./../_shared/CODE_REVIEW_STANDARDS.md) - Accessibility requirements
+ - [Generic UX Designer](../generic-ux-designer/SKILL.md) - Design thinking, psychology
+ - [UX Principles](../_shared/UX_PRINCIPLES.md) - Research methods, heuristics
+ - [Design Patterns](../_shared/DESIGN_PATTERNS.md) - Visual patterns