ux-toolkit 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +113 -7
  2. package/agents/card-reviewer.md +173 -0
  3. package/agents/comparison-reviewer.md +143 -0
  4. package/agents/density-reviewer.md +207 -0
  5. package/agents/detail-page-reviewer.md +143 -0
  6. package/agents/editor-reviewer.md +165 -0
  7. package/agents/form-reviewer.md +156 -0
  8. package/agents/game-ui-reviewer.md +181 -0
  9. package/agents/list-page-reviewer.md +132 -0
  10. package/agents/navigation-reviewer.md +145 -0
  11. package/agents/panel-reviewer.md +182 -0
  12. package/agents/replay-reviewer.md +174 -0
  13. package/agents/settings-reviewer.md +166 -0
  14. package/agents/ux-auditor.md +145 -45
  15. package/agents/ux-engineer.md +211 -38
  16. package/dist/cli.js +172 -5
  17. package/dist/cli.js.map +1 -1
  18. package/dist/index.cjs +172 -5
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +128 -4
  21. package/dist/index.d.ts +128 -4
  22. package/dist/index.js +172 -5
  23. package/dist/index.js.map +1 -1
  24. package/package.json +6 -4
  25. package/skills/canvas-grid-patterns/SKILL.md +367 -0
  26. package/skills/comparison-patterns/SKILL.md +354 -0
  27. package/skills/data-density-patterns/SKILL.md +493 -0
  28. package/skills/detail-page-patterns/SKILL.md +522 -0
  29. package/skills/drag-drop-patterns/SKILL.md +406 -0
  30. package/skills/editor-workspace-patterns/SKILL.md +552 -0
  31. package/skills/event-timeline-patterns/SKILL.md +542 -0
  32. package/skills/form-patterns/SKILL.md +608 -0
  33. package/skills/info-card-patterns/SKILL.md +531 -0
  34. package/skills/keyboard-shortcuts-patterns/SKILL.md +365 -0
  35. package/skills/list-page-patterns/SKILL.md +351 -0
  36. package/skills/modal-patterns/SKILL.md +750 -0
  37. package/skills/navigation-patterns/SKILL.md +476 -0
  38. package/skills/page-structure-patterns/SKILL.md +271 -0
  39. package/skills/playback-replay-patterns/SKILL.md +695 -0
  40. package/skills/react-ux-patterns/SKILL.md +434 -0
  41. package/skills/split-panel-patterns/SKILL.md +609 -0
  42. package/skills/status-visualization-patterns/SKILL.md +635 -0
  43. package/skills/toast-notification-patterns/SKILL.md +207 -0
  44. package/skills/turn-based-ui-patterns/SKILL.md +506 -0
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: toast-notification-patterns
3
+ description: Toast notifications, snackbars, alerts, and system feedback patterns
4
+ license: MIT
5
+ ---
6
+
7
+ # Toast & Notification Patterns
8
+
9
+ ## 1. Toast Types
10
+
11
+ **Success** - Confirmation of completed actions
12
+ **Error** - Critical failures requiring attention
13
+ **Warning** - Important non-critical issues
14
+ **Info** - Neutral system messages
15
+ **Loading** - In-progress operations with optional progress indicator
16
+
17
+ ## 2. Toast Anatomy
18
+
19
+ ```tsx
20
+ interface Toast {
21
+ id: string;
22
+ type: 'success' | 'error' | 'warning' | 'info' | 'loading';
23
+ message: string;
24
+ description?: string;
25
+ icon?: React.ReactNode;
26
+ action?: { label: string; onClick: () => void };
27
+ dismissible?: boolean;
28
+ duration?: number;
29
+ progress?: number; // 0-100 for loading toasts
30
+ }
31
+ ```
32
+
33
+ **Core Elements:**
34
+ - Icon (type-specific, 16-20px)
35
+ - Message (bold, 14px)
36
+ - Description (optional, muted, 13px)
37
+ - Action button (optional, primary action)
38
+ - Dismiss button (X icon, always visible for errors)
39
+ - Progress bar (bottom edge, for timed auto-dismiss)
40
+
41
+ ## 3. Positioning & Stacking
42
+
43
+ **Preferred positions:**
44
+ - `top-right` - Default, least intrusive
45
+ - `bottom-center` - Mobile-friendly, undo actions
46
+ - `top-center` - Critical system alerts
47
+
48
+ **Stacking behavior:**
49
+ ```tsx
50
+ // Max 3 visible, newest on top
51
+ const MAX_VISIBLE = 3;
52
+ const STACK_OFFSET = 8; // px between toasts
53
+ const STACK_DIRECTION = 'down'; // or 'up' for bottom positioning
54
+ ```
55
+
56
+ Toasts stack vertically with 8px gap. When exceeding max, oldest dismissed first (FIFO).
57
+
58
+ ## 4. Timing Rules
59
+
60
+ ```tsx
61
+ const DURATIONS = {
62
+ success: 3000, // 3s - Quick confirmation
63
+ info: 4000, // 4s - More reading time
64
+ warning: 6000, // 6s - Important message
65
+ error: Infinity, // Persist until user dismisses
66
+ loading: Infinity // Persist until operation completes
67
+ };
68
+ ```
69
+
70
+ **Auto-dismiss logic:**
71
+ - Pause timer on hover/focus
72
+ - Resume on mouse leave
73
+ - Reset on content update
74
+ - Manual dismiss always available (except loading)
75
+
76
+ ## 5. Accessibility
77
+
78
+ ```tsx
79
+ <div
80
+ role="alert"
81
+ aria-live="polite" // 'assertive' for errors
82
+ aria-atomic="true"
83
+ className="toast"
84
+ >
85
+ <svg aria-hidden="true">{icon}</svg>
86
+ <div>
87
+ <p className="font-semibold">{message}</p>
88
+ {description && <p className="text-sm text-muted">{description}</p>}
89
+ </div>
90
+ <button aria-label="Dismiss notification">{dismissIcon}</button>
91
+ </div>
92
+ ```
93
+
94
+ **Focus management:**
95
+ - Errors: Move focus to toast on appear (if user not typing)
96
+ - Actions: Button must be keyboard accessible
97
+ - Dismiss: ESC key dismisses focused toast
98
+
99
+ ## 6. Code Examples
100
+
101
+ ```tsx
102
+ // Toast Provider
103
+ export function ToastProvider({ children }: { children: React.ReactNode }) {
104
+ const [toasts, setToasts] = useState<Toast[]>([]);
105
+
106
+ const addToast = useCallback((toast: Omit<Toast, 'id'>) => {
107
+ const id = crypto.randomUUID();
108
+ setToasts(prev => [{ ...toast, id }, ...prev].slice(0, MAX_VISIBLE));
109
+ }, []);
110
+
111
+ const removeToast = useCallback((id: string) => {
112
+ setToasts(prev => prev.filter(t => t.id !== id));
113
+ }, []);
114
+
115
+ return (
116
+ <ToastContext.Provider value={{ addToast, removeToast }}>
117
+ {children}
118
+ <div className="fixed top-4 right-4 z-50 flex flex-col gap-2">
119
+ {toasts.map((toast, idx) => (
120
+ <ToastItem key={toast.id} toast={toast} onDismiss={removeToast} />
121
+ ))}
122
+ </div>
123
+ </ToastContext.Provider>
124
+ );
125
+ }
126
+
127
+ // Hook
128
+ export const useToast = () => useContext(ToastContext);
129
+ ```
130
+
131
+ ## 7. Toast Queue Management
132
+
133
+ **Deduplication strategy:**
134
+ ```tsx
135
+ const addToast = (toast: Omit<Toast, 'id'>) => {
136
+ setToasts(prev => {
137
+ // Remove duplicate messages within 5s window
138
+ const isDuplicate = prev.some(t =>
139
+ t.message === toast.message &&
140
+ Date.now() - t.createdAt < 5000
141
+ );
142
+ if (isDuplicate) return prev;
143
+
144
+ return [{ ...toast, id: uuid(), createdAt: Date.now() }, ...prev]
145
+ .slice(0, MAX_VISIBLE);
146
+ });
147
+ };
148
+ ```
149
+
150
+ **Priority system:**
151
+ Errors > Warnings > Loading > Success > Info
152
+
153
+ ## 8. Action Toasts
154
+
155
+ **Undo pattern:**
156
+ ```tsx
157
+ toast.success('Item deleted', {
158
+ action: {
159
+ label: 'Undo',
160
+ onClick: () => restoreItem(id)
161
+ },
162
+ duration: 8000 // Longer for user to react
163
+ });
164
+ ```
165
+
166
+ **Retry pattern:**
167
+ ```tsx
168
+ toast.error('Upload failed', {
169
+ action: {
170
+ label: 'Retry',
171
+ onClick: () => retryUpload()
172
+ },
173
+ duration: Infinity
174
+ });
175
+ ```
176
+
177
+ ## 9. Persistent vs Transient
178
+
179
+ **Use persistent (no auto-dismiss) for:**
180
+ - Errors requiring user action
181
+ - Loading states
182
+ - Toasts with action buttons (undo, retry)
183
+ - Form validation summaries
184
+
185
+ **Use transient (auto-dismiss) for:**
186
+ - Success confirmations
187
+ - Informational updates
188
+ - Non-critical warnings
189
+ - Progress milestones
190
+
191
+ ## 10. Audit Checklist
192
+
193
+ - [ ] **[CRITICAL]** Errors persist until manually dismissed
194
+ - [ ] **[CRITICAL]** All interactive elements keyboard accessible (Tab, Enter, ESC)
195
+ - [ ] **[CRITICAL]** role="alert" and aria-live present
196
+ - [ ] **[MAJOR]** Icons have aria-hidden="true"
197
+ - [ ] **[MAJOR]** Dismiss buttons have aria-label
198
+ - [ ] **[MAJOR]** Auto-dismiss pauses on hover/focus
199
+ - [ ] **[MAJOR]** Maximum 3 toasts visible simultaneously
200
+ - [ ] **[MAJOR]** Toast width constrained (max 400px)
201
+ - [ ] **[MINOR]** Success toasts auto-dismiss in 3-4s
202
+ - [ ] **[MINOR]** Toast animates in/out smoothly (200-300ms)
203
+ - [ ] **[MINOR]** Duplicate messages within 5s deduplicated
204
+ - [ ] **[MINOR]** Loading toasts show progress indicator
205
+ - [ ] **[MINOR]** Action toasts have 6-8s duration minimum
206
+ - [ ] **[MINOR]** Mobile: Bottom positioning preferred
207
+ - [ ] **[MINOR]** Color contrast meets WCAG AA (4.5:1)
@@ -0,0 +1,506 @@
1
+ # Turn-Based & Phase UI Patterns
2
+
3
+ Patterns for turn-based games, phase-driven workflows, and sequential state machines. Applies to strategy games, board games, approval workflows, and any UI with distinct phases or turns.
4
+
5
+ ## When to Use This Skill
6
+
7
+ - Turn-based strategy games (XCOM, Civilization, BattleTech)
8
+ - Board game implementations
9
+ - Multi-step approval workflows
10
+ - Wizard/stepper with dependent phases
11
+ - Auction/bidding systems
12
+ - Real-time strategy games with pause
13
+ - Collaborative editing with turn-taking
14
+
15
+ ---
16
+
17
+ ## Core Concepts
18
+
19
+ ### Phase vs Turn vs Round
20
+
21
+ | Term | Definition | Example |
22
+ |------|------------|---------|
23
+ | **Phase** | Distinct action type within a turn | Movement Phase, Attack Phase, End Phase |
24
+ | **Turn** | One player's complete action sequence | Player 1's turn, Player 2's turn |
25
+ | **Round** | Complete cycle of all players' turns | Round 1 = All players take one turn |
26
+
27
+ ### Phase Flow Types
28
+
29
+ | Type | Description | Use Case |
30
+ |------|-------------|----------|
31
+ | **Linear** | Phases always in same order | Most board games |
32
+ | **Conditional** | Some phases may be skipped | MTG: Skip combat if no creatures |
33
+ | **Parallel** | Multiple phases can be active | RTS games with simultaneous actions |
34
+ | **Interruptible** | Opponent can interrupt phases | Stack-based card games |
35
+
36
+ ---
37
+
38
+ ## Audit Checklist
39
+
40
+ ### Phase Banner/Indicator
41
+ - [ ] [CRITICAL] Current phase clearly displayed at all times
42
+ - [ ] [CRITICAL] Phase name readable and prominent
43
+ - [ ] [MAJOR] Visual distinction between phases (color/icon)
44
+ - [ ] [MAJOR] Phase transition is obvious (animation/sound)
45
+ - [ ] [MINOR] Phase description/tooltip explains what can be done
46
+ - [ ] [MINOR] Upcoming phase indicated (next phase preview)
47
+
48
+ ### Turn Indicator
49
+ - [ ] [CRITICAL] Whose turn it is clearly shown
50
+ - [ ] [CRITICAL] Active player highlighted in player list
51
+ - [ ] [MAJOR] Turn number displayed
52
+ - [ ] [MAJOR] Different styling for "your turn" vs "opponent's turn"
53
+ - [ ] [MINOR] Turn timer (if applicable)
54
+ - [ ] [MINOR] Turn history accessible
55
+
56
+ ### Action Availability
57
+ - [ ] [CRITICAL] Available actions clearly enabled
58
+ - [ ] [CRITICAL] Unavailable actions clearly disabled (grayed out)
59
+ - [ ] [MAJOR] Reason for unavailability shown (tooltip/message)
60
+ - [ ] [MAJOR] No clickable actions that do nothing
61
+ - [ ] [MINOR] Keyboard shortcuts for common actions
62
+ - [ ] [MINOR] Action cost displayed (if applicable)
63
+
64
+ ### Phase-Specific Actions
65
+ - [ ] [CRITICAL] Action bar changes based on current phase
66
+ - [ ] [CRITICAL] Only phase-appropriate actions shown
67
+ - [ ] [MAJOR] Default/primary action emphasized
68
+ - [ ] [MAJOR] "End Phase" / "Pass" button always accessible
69
+ - [ ] [MINOR] Quick action buttons for common operations
70
+ - [ ] [MINOR] Action confirmation for irreversible actions
71
+
72
+ ### State Feedback
73
+ - [ ] [CRITICAL] Game state updates immediately on action
74
+ - [ ] [CRITICAL] Animation indicates what changed
75
+ - [ ] [MAJOR] Sound feedback for actions (if applicable)
76
+ - [ ] [MAJOR] Undo available within phase (if allowed by rules)
77
+ - [ ] [MINOR] Action log shows what happened
78
+ - [ ] [MINOR] Pending actions queue visible
79
+
80
+ ### Waiting States
81
+ - [ ] [CRITICAL] Clear indication when waiting for opponent
82
+ - [ ] [MAJOR] "Opponent is thinking..." or similar message
83
+ - [ ] [MAJOR] Estimated wait time or activity indicator
84
+ - [ ] [MINOR] Allow reviewing state while waiting
85
+ - [ ] [MINOR] Notification when it's your turn again
86
+
87
+ ### Phase Transitions
88
+ - [ ] [CRITICAL] Clear visual/audio cue when phase changes
89
+ - [ ] [MAJOR] Brief pause between phases for readability
90
+ - [ ] [MAJOR] Auto-advance when phase has no actions
91
+ - [ ] [MINOR] Transition animation (slide, fade, etc.)
92
+ - [ ] [MINOR] Summary of phase results before moving on
93
+
94
+ ### Error Prevention
95
+ - [ ] [CRITICAL] Cannot take actions out of turn
96
+ - [ ] [CRITICAL] Cannot perform invalid actions
97
+ - [ ] [MAJOR] Confirmation for game-ending actions
98
+ - [ ] [MAJOR] Warning for suboptimal moves (optional)
99
+ - [ ] [MINOR] "Are you sure?" for skipping phases with unused resources
100
+
101
+ ---
102
+
103
+ ## Implementation Patterns
104
+
105
+ ### Phase State Machine
106
+
107
+ ```typescript
108
+ enum GamePhase {
109
+ Initiative = 'INITIATIVE',
110
+ Movement = 'MOVEMENT',
111
+ Attack = 'ATTACK',
112
+ PhysicalAttack = 'PHYSICAL_ATTACK',
113
+ Heat = 'HEAT',
114
+ End = 'END',
115
+ }
116
+
117
+ interface PhaseConfig {
118
+ name: string;
119
+ description: string;
120
+ icon: string;
121
+ color: string;
122
+ allowedActions: ActionType[];
123
+ canSkip: boolean;
124
+ autoAdvance: boolean; // Auto-advance when no actions available
125
+ }
126
+
127
+ const PHASE_CONFIG: Record<GamePhase, PhaseConfig> = {
128
+ [GamePhase.Movement]: {
129
+ name: 'Movement Phase',
130
+ description: 'Move your units across the battlefield',
131
+ icon: 'move',
132
+ color: 'blue',
133
+ allowedActions: ['move', 'stand', 'prone', 'sprint'],
134
+ canSkip: true,
135
+ autoAdvance: false,
136
+ },
137
+ [GamePhase.Attack]: {
138
+ name: 'Weapon Attack Phase',
139
+ description: 'Fire weapons at enemy targets',
140
+ icon: 'target',
141
+ color: 'red',
142
+ allowedActions: ['fire', 'aim', 'torso_twist'],
143
+ canSkip: true,
144
+ autoAdvance: false,
145
+ },
146
+ // ... more phases
147
+ };
148
+ ```
149
+
150
+ ### Phase Banner Component
151
+
152
+ ```tsx
153
+ interface PhaseBannerProps {
154
+ phase: GamePhase;
155
+ turn: number;
156
+ activeSide: 'player' | 'opponent';
157
+ isPlayerTurn: boolean;
158
+ }
159
+
160
+ function PhaseBanner({ phase, turn, activeSide, isPlayerTurn }: PhaseBannerProps) {
161
+ const config = PHASE_CONFIG[phase];
162
+
163
+ return (
164
+ <div
165
+ className={`
166
+ flex items-center justify-between px-6 py-3
167
+ ${isPlayerTurn ? 'bg-blue-900' : 'bg-gray-800'}
168
+ border-b-2 ${isPlayerTurn ? 'border-blue-500' : 'border-gray-600'}
169
+ `}
170
+ role="status"
171
+ aria-live="polite"
172
+ >
173
+ {/* Turn Info */}
174
+ <div className="flex items-center gap-4">
175
+ <span className="text-sm text-gray-400">Turn {turn}</span>
176
+ <div className={`
177
+ px-3 py-1 rounded-full text-sm font-medium
178
+ ${isPlayerTurn
179
+ ? 'bg-emerald-500/20 text-emerald-400'
180
+ : 'bg-amber-500/20 text-amber-400'
181
+ }
182
+ `}>
183
+ {isPlayerTurn ? 'Your Turn' : "Opponent's Turn"}
184
+ </div>
185
+ </div>
186
+
187
+ {/* Phase Info */}
188
+ <div className="flex items-center gap-3">
189
+ <PhaseIcon phase={phase} className="w-6 h-6" style={{ color: config.color }} />
190
+ <div>
191
+ <div className="font-semibold text-white">{config.name}</div>
192
+ <div className="text-xs text-gray-400">{config.description}</div>
193
+ </div>
194
+ </div>
195
+
196
+ {/* Phase Progress */}
197
+ <PhaseProgressIndicator currentPhase={phase} />
198
+ </div>
199
+ );
200
+ }
201
+ ```
202
+
203
+ ### Phase Progress Indicator
204
+
205
+ ```tsx
206
+ function PhaseProgressIndicator({ currentPhase }: { currentPhase: GamePhase }) {
207
+ const phases = Object.values(GamePhase);
208
+ const currentIndex = phases.indexOf(currentPhase);
209
+
210
+ return (
211
+ <div className="flex items-center gap-2" aria-label="Phase progress">
212
+ {phases.map((phase, index) => {
213
+ const config = PHASE_CONFIG[phase];
214
+ const status = index < currentIndex ? 'completed'
215
+ : index === currentIndex ? 'current'
216
+ : 'upcoming';
217
+
218
+ return (
219
+ <React.Fragment key={phase}>
220
+ {index > 0 && (
221
+ <div className={`w-8 h-0.5 ${
222
+ status === 'completed' ? 'bg-emerald-500' : 'bg-gray-600'
223
+ }`} />
224
+ )}
225
+ <div
226
+ className={`
227
+ w-8 h-8 rounded-full flex items-center justify-center
228
+ ${status === 'current' ? 'bg-accent ring-2 ring-accent/50' : ''}
229
+ ${status === 'completed' ? 'bg-emerald-500' : ''}
230
+ ${status === 'upcoming' ? 'bg-gray-700' : ''}
231
+ `}
232
+ title={config.name}
233
+ >
234
+ <PhaseIcon phase={phase} className="w-4 h-4" />
235
+ </div>
236
+ </React.Fragment>
237
+ );
238
+ })}
239
+ </div>
240
+ );
241
+ }
242
+ ```
243
+
244
+ ### Action Bar Component
245
+
246
+ ```tsx
247
+ interface ActionBarProps {
248
+ phase: GamePhase;
249
+ canAct: boolean;
250
+ canUndo: boolean;
251
+ onAction: (action: string) => void;
252
+ onEndPhase: () => void;
253
+ onUndo: () => void;
254
+ }
255
+
256
+ function ActionBar({ phase, canAct, canUndo, onAction, onEndPhase, onUndo }: ActionBarProps) {
257
+ const config = PHASE_CONFIG[phase];
258
+ const actions = config.allowedActions;
259
+
260
+ return (
261
+ <div
262
+ className="flex items-center justify-between px-6 py-4 bg-gray-900 border-t border-gray-700"
263
+ role="toolbar"
264
+ aria-label="Phase actions"
265
+ >
266
+ {/* Phase-specific actions */}
267
+ <div className="flex items-center gap-2">
268
+ {actions.map(action => (
269
+ <ActionButton
270
+ key={action}
271
+ action={action}
272
+ disabled={!canAct}
273
+ onClick={() => onAction(action)}
274
+ />
275
+ ))}
276
+ </div>
277
+
278
+ {/* Universal actions */}
279
+ <div className="flex items-center gap-2">
280
+ {canUndo && (
281
+ <Button variant="ghost" onClick={onUndo}>
282
+ <UndoIcon className="w-4 h-4 mr-2" />
283
+ Undo
284
+ </Button>
285
+ )}
286
+
287
+ <Button
288
+ variant="primary"
289
+ onClick={onEndPhase}
290
+ disabled={!canAct}
291
+ >
292
+ {config.canSkip ? 'End Phase' : 'Continue'}
293
+ <ChevronRightIcon className="w-4 h-4 ml-2" />
294
+ </Button>
295
+ </div>
296
+ </div>
297
+ );
298
+ }
299
+ ```
300
+
301
+ ### Waiting State Component
302
+
303
+ ```tsx
304
+ function WaitingForOpponent({ opponentName, lastAction }: WaitingProps) {
305
+ return (
306
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
307
+ <div className="bg-gray-800 rounded-lg p-8 text-center max-w-md">
308
+ <div className="animate-pulse mb-4">
309
+ <ClockIcon className="w-12 h-12 mx-auto text-amber-400" />
310
+ </div>
311
+
312
+ <h2 className="text-xl font-bold text-white mb-2">
313
+ Waiting for {opponentName}
314
+ </h2>
315
+
316
+ <p className="text-gray-400 mb-4">
317
+ {lastAction
318
+ ? `Last action: ${lastAction}`
319
+ : 'Opponent is thinking...'}
320
+ </p>
321
+
322
+ <div className="flex justify-center gap-2">
323
+ <span className="w-2 h-2 bg-amber-400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
324
+ <span className="w-2 h-2 bg-amber-400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
325
+ <span className="w-2 h-2 bg-amber-400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
326
+ </div>
327
+ </div>
328
+ </div>
329
+ );
330
+ }
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Visual Hierarchy
336
+
337
+ ### Phase Colors
338
+
339
+ ```typescript
340
+ const PHASE_COLORS = {
341
+ movement: {
342
+ bg: 'bg-blue-900',
343
+ border: 'border-blue-500',
344
+ text: 'text-blue-400',
345
+ icon: '#3b82f6',
346
+ },
347
+ attack: {
348
+ bg: 'bg-red-900',
349
+ border: 'border-red-500',
350
+ text: 'text-red-400',
351
+ icon: '#ef4444',
352
+ },
353
+ defense: {
354
+ bg: 'bg-emerald-900',
355
+ border: 'border-emerald-500',
356
+ text: 'text-emerald-400',
357
+ icon: '#10b981',
358
+ },
359
+ utility: {
360
+ bg: 'bg-purple-900',
361
+ border: 'border-purple-500',
362
+ text: 'text-purple-400',
363
+ icon: '#8b5cf6',
364
+ },
365
+ end: {
366
+ bg: 'bg-gray-800',
367
+ border: 'border-gray-600',
368
+ text: 'text-gray-400',
369
+ icon: '#6b7280',
370
+ },
371
+ };
372
+ ```
373
+
374
+ ### Turn Indicator States
375
+
376
+ ```typescript
377
+ const TURN_STATES = {
378
+ yourTurn: {
379
+ label: 'Your Turn',
380
+ color: 'emerald',
381
+ pulse: true,
382
+ },
383
+ opponentTurn: {
384
+ label: "Opponent's Turn",
385
+ color: 'amber',
386
+ pulse: false,
387
+ },
388
+ waiting: {
389
+ label: 'Waiting...',
390
+ color: 'gray',
391
+ pulse: true,
392
+ },
393
+ gameOver: {
394
+ label: 'Game Over',
395
+ color: 'slate',
396
+ pulse: false,
397
+ },
398
+ };
399
+ ```
400
+
401
+ ---
402
+
403
+ ## Anti-Patterns
404
+
405
+ ### DON'T: Hidden Phase Information
406
+ ```tsx
407
+ // BAD - Phase only shown in small text
408
+ <span className="text-xs text-gray-500">{phase}</span>
409
+
410
+ // GOOD - Prominent, always-visible phase banner
411
+ <PhaseBanner phase={phase} className="sticky top-0 z-10" />
412
+ ```
413
+
414
+ ### DON'T: Clickable Disabled Actions
415
+ ```tsx
416
+ // BAD - Looks disabled but accepts clicks
417
+ <button className="opacity-50" onClick={handleAction}>Attack</button>
418
+
419
+ // GOOD - Actually disabled with explanation
420
+ <Tooltip content="Cannot attack: No valid targets">
421
+ <button disabled className="opacity-50 cursor-not-allowed">Attack</button>
422
+ </Tooltip>
423
+ ```
424
+
425
+ ### DON'T: Silent Phase Transitions
426
+ ```tsx
427
+ // BAD - Phase changes without notice
428
+ setPhase(nextPhase);
429
+
430
+ // GOOD - Announce transition
431
+ setPhase(nextPhase);
432
+ announceToScreenReader(`Entering ${PHASE_CONFIG[nextPhase].name}`);
433
+ playTransitionSound();
434
+ showTransitionAnimation();
435
+ ```
436
+
437
+ ### DON'T: No Clear Turn Ownership
438
+ ```tsx
439
+ // BAD - Ambiguous whose turn it is
440
+ <div>{currentPlayer} is playing</div>
441
+
442
+ // GOOD - Crystal clear with visual hierarchy
443
+ <div className={isYourTurn ? 'bg-emerald-500/20' : 'bg-gray-800'}>
444
+ {isYourTurn
445
+ ? <span className="text-emerald-400 font-bold">YOUR TURN</span>
446
+ : <span className="text-amber-400">Opponent's Turn</span>
447
+ }
448
+ </div>
449
+ ```
450
+
451
+ ---
452
+
453
+ ## Accessibility
454
+
455
+ ### Screen Reader Announcements
456
+
457
+ ```tsx
458
+ function usePhaseAnnouncer(phase: GamePhase, turn: number, isYourTurn: boolean) {
459
+ const prevPhase = useRef(phase);
460
+
461
+ useEffect(() => {
462
+ if (phase !== prevPhase.current) {
463
+ const message = `${PHASE_CONFIG[phase].name}. ${
464
+ isYourTurn ? 'Your turn to act.' : 'Waiting for opponent.'
465
+ }`;
466
+ announceToScreenReader(message);
467
+ prevPhase.current = phase;
468
+ }
469
+ }, [phase, isYourTurn]);
470
+ }
471
+ ```
472
+
473
+ ### Keyboard Navigation
474
+
475
+ | Key | Action |
476
+ |-----|--------|
477
+ | `Space` / `Enter` | Confirm action / End phase |
478
+ | `Escape` | Cancel current action |
479
+ | `Tab` | Navigate between action buttons |
480
+ | `1-9` | Quick select action by number |
481
+ | `U` | Undo last action |
482
+ | `E` | End current phase |
483
+
484
+ ---
485
+
486
+ ## Testing Checklist
487
+
488
+ - [ ] Phase banner shows correct phase name
489
+ - [ ] Turn indicator shows correct player
490
+ - [ ] Actions disable when not your turn
491
+ - [ ] Phase transition animation plays
492
+ - [ ] End Phase advances to next phase
493
+ - [ ] Undo reverts last action (if allowed)
494
+ - [ ] Cannot take actions out of turn
495
+ - [ ] Phase-specific actions change correctly
496
+ - [ ] Waiting state shows during opponent's turn
497
+ - [ ] Screen reader announces phase changes
498
+
499
+ ---
500
+
501
+ ## Related Skills
502
+
503
+ - `canvas-grid-patterns` - For game board visualization
504
+ - `keyboard-shortcuts-patterns` - For action hotkeys
505
+ - `playback-replay-patterns` - For game replay functionality
506
+ - `event-timeline-patterns` - For turn history/log