design-protocol 1.0.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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +225 -0
  3. package/agents/dp-researcher.md +239 -0
  4. package/agents/dp-verifier.md +207 -0
  5. package/bin/install.js +464 -0
  6. package/commands/dp-back.md +221 -0
  7. package/commands/dp-discuss.md +257 -0
  8. package/commands/dp-execute.md +513 -0
  9. package/commands/dp-journey.md +85 -0
  10. package/commands/dp-progress.md +178 -0
  11. package/commands/dp-roadmap.md +83 -0
  12. package/commands/dp-skip.md +186 -0
  13. package/commands/dp-start.md +510 -0
  14. package/commands/dp-storytell.md +94 -0
  15. package/commands/dp-verify.md +207 -0
  16. package/package.json +59 -0
  17. package/skills/dp-color/SKILL.md +214 -0
  18. package/skills/dp-color/export_tokens.py +297 -0
  19. package/skills/dp-color/references/apca-contrast.md +87 -0
  20. package/skills/dp-color/references/hue-emotions.md +109 -0
  21. package/skills/dp-color/references/oklch-gamut.md +79 -0
  22. package/skills/dp-color/references/pitfalls.md +171 -0
  23. package/skills/dp-color/references/scale-patterns.md +206 -0
  24. package/skills/dp-color/references/tool-workflows.md +200 -0
  25. package/skills/dp-discovery/SKILL.md +480 -0
  26. package/skills/dp-eng_review/SKILL.md +471 -0
  27. package/skills/dp-eng_review/references/code-review-checklist.md +385 -0
  28. package/skills/dp-eng_review/references/react-patterns.md +512 -0
  29. package/skills/dp-eng_review/references/shadcn-patterns.md +510 -0
  30. package/skills/dp-eng_review/references/tailwind-conventions.md +351 -0
  31. package/skills/dp-journey/SKILL.md +682 -0
  32. package/skills/dp-journey/references/journey-types.md +97 -0
  33. package/skills/dp-journey/references/map-structures.md +177 -0
  34. package/skills/dp-journey/references/omnichannel-patterns.md +208 -0
  35. package/skills/dp-journey/references/research-methods.md +125 -0
  36. package/skills/dp-prd/SKILL.md +201 -0
  37. package/skills/dp-prd/references/claude-code-spec.md +107 -0
  38. package/skills/dp-prd/references/interview-questions.md +158 -0
  39. package/skills/dp-prd/references/section-templates.md +231 -0
  40. package/skills/dp-research/SKILL.md +540 -0
  41. package/skills/dp-research/references/facilitation-guide.md +291 -0
  42. package/skills/dp-research/references/interview-guide-template.md +190 -0
  43. package/skills/dp-research/references/method-selection.md +195 -0
  44. package/skills/dp-research/references/question-writing.md +244 -0
  45. package/skills/dp-research/references/research-report-template.md +363 -0
  46. package/skills/dp-research/references/synthesis-methods.md +289 -0
  47. package/skills/dp-research/references/usability-test-template.md +260 -0
  48. package/skills/dp-roadmap/SKILL.md +648 -0
  49. package/skills/dp-roadmap/references/prioritization-frameworks.md +312 -0
  50. package/skills/dp-roadmap/references/roadmap-structures.md +179 -0
  51. package/skills/dp-roadmap/references/roadmap-workshops.md +264 -0
  52. package/skills/dp-roadmap/references/theme-development.md +168 -0
  53. package/skills/dp-storytell/SKILL.md +645 -0
  54. package/skills/dp-storytell/references/audience-playbooks.md +260 -0
  55. package/skills/dp-storytell/references/content-type-templates.md +310 -0
  56. package/skills/dp-storytell/references/delivery-tactics.md +228 -0
  57. package/skills/dp-storytell/references/narrative-frameworks.md +259 -0
  58. package/skills/dp-ui/SKILL.md +503 -0
  59. package/skills/dp-ui/references/b2b-enterprise-patterns.md +319 -0
  60. package/skills/dp-ui/references/data-visualization.md +304 -0
  61. package/skills/dp-ui/references/visual-design-principles.md +237 -0
  62. package/skills/dp-ux/SKILL.md +414 -0
  63. package/skills/dp-ux/references/accessibility-checklist.md +128 -0
  64. package/skills/dp-ux/references/product-excellence.md +149 -0
  65. package/skills/dp-ux/references/usability-principles.md +140 -0
  66. package/skills/dp-ux/references/ux-patterns.md +221 -0
  67. package/templates/config.json +55 -0
  68. package/templates/context.md +96 -0
  69. package/templates/project.md +83 -0
  70. package/templates/requirements.md +137 -0
  71. package/templates/roadmap.md +168 -0
  72. package/templates/state.md +107 -0
@@ -0,0 +1,512 @@
1
+ # React 19 Patterns
2
+
3
+ Best practices and anti-patterns for React 19 in Next.js 16. Reference this when reviewing component implementations.
4
+
5
+ ---
6
+
7
+ ## Component Structure
8
+
9
+ ### Function Components (Standard)
10
+
11
+ ```tsx
12
+ // GOOD: Standard function component
13
+ interface UserCardProps {
14
+ user: User
15
+ onEdit?: (id: string) => void
16
+ }
17
+
18
+ export function UserCard({ user, onEdit }: UserCardProps) {
19
+ return (
20
+ <Card>
21
+ <CardHeader>
22
+ <CardTitle>{user.name}</CardTitle>
23
+ </CardHeader>
24
+ </Card>
25
+ )
26
+ }
27
+ ```
28
+
29
+ ### Props Typing
30
+
31
+ ```tsx
32
+ // GOOD: Explicit interface
33
+ interface ButtonProps {
34
+ variant?: 'primary' | 'secondary'
35
+ size?: 'sm' | 'md' | 'lg'
36
+ isLoading?: boolean
37
+ children: React.ReactNode
38
+ onClick?: () => void
39
+ }
40
+
41
+ // GOOD: Extending HTML attributes
42
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
43
+ variant?: 'primary' | 'secondary'
44
+ isLoading?: boolean
45
+ }
46
+
47
+ // GOOD: Using ComponentProps for composition
48
+ import { ComponentProps } from 'react'
49
+ type ButtonProps = ComponentProps<typeof Button> & {
50
+ customProp?: string
51
+ }
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Lists and Keys
57
+
58
+ ### Always Provide Stable Keys
59
+
60
+ ```tsx
61
+ // BAD: Using index as key
62
+ {items.map((item, index) => (
63
+ <Item key={index} {...item} /> // Will cause issues with reordering
64
+ ))}
65
+
66
+ // BAD: No key
67
+ {items.map((item) => (
68
+ <Item {...item} /> // React will warn
69
+ ))}
70
+
71
+ // GOOD: Stable unique key
72
+ {items.map((item) => (
73
+ <Item key={item.id} {...item} />
74
+ ))}
75
+
76
+ // GOOD: Compound key when needed
77
+ {items.map((item) => (
78
+ <Item key={`${item.category}-${item.id}`} {...item} />
79
+ ))}
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Event Handlers & Callbacks {#callbacks}
85
+
86
+ ### Avoid Inline Functions in Render
87
+
88
+ ```tsx
89
+ // BAD: Creates new function every render
90
+ <Button onClick={() => handleDelete(item.id)}>Delete</Button>
91
+
92
+ // GOOD: useCallback for stable reference
93
+ const handleItemDelete = useCallback((id: string) => {
94
+ deleteItem(id)
95
+ }, [deleteItem])
96
+
97
+ // Then in render
98
+ <Button onClick={() => handleItemDelete(item.id)}>Delete</Button>
99
+
100
+ // BETTER: Pass data via data attributes
101
+ const handleDelete = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
102
+ const id = e.currentTarget.dataset.itemId
103
+ if (id) deleteItem(id)
104
+ }, [deleteItem])
105
+
106
+ <Button data-item-id={item.id} onClick={handleDelete}>Delete</Button>
107
+ ```
108
+
109
+ ### Event Handler Typing
110
+
111
+ ```tsx
112
+ // GOOD: Properly typed handlers
113
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
114
+ setValue(e.target.value)
115
+ }
116
+
117
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
118
+ e.preventDefault()
119
+ // submit logic
120
+ }
121
+
122
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
123
+ if (e.key === 'Enter') {
124
+ submit()
125
+ }
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ## State Management
132
+
133
+ ### Derived State
134
+
135
+ ```tsx
136
+ // BAD: Duplicating state
137
+ const [items, setItems] = useState<Item[]>([])
138
+ const [filteredItems, setFilteredItems] = useState<Item[]>([]) // Derived!
139
+ const [itemCount, setItemCount] = useState(0) // Derived!
140
+
141
+ useEffect(() => {
142
+ setFilteredItems(items.filter(i => i.active))
143
+ setItemCount(items.length)
144
+ }, [items])
145
+
146
+ // GOOD: Derive values directly
147
+ const [items, setItems] = useState<Item[]>([])
148
+ const filteredItems = items.filter(i => i.active) // Computed
149
+ const itemCount = items.length // Computed
150
+
151
+ // GOOD: useMemo for expensive derivations
152
+ const filteredItems = useMemo(
153
+ () => items.filter(i => expensiveCheck(i)),
154
+ [items]
155
+ )
156
+ ```
157
+
158
+ ### State Initialization
159
+
160
+ ```tsx
161
+ // BAD: Expensive computation on every render
162
+ const [data, setData] = useState(expensiveComputation())
163
+
164
+ // GOOD: Lazy initialization
165
+ const [data, setData] = useState(() => expensiveComputation())
166
+ ```
167
+
168
+ ### State Updates
169
+
170
+ ```tsx
171
+ // BAD: State based on previous without using callback
172
+ const increment = () => {
173
+ setCount(count + 1) // Might be stale
174
+ }
175
+
176
+ // GOOD: Functional update for state based on previous
177
+ const increment = () => {
178
+ setCount(prev => prev + 1)
179
+ }
180
+
181
+ // BAD: Multiple related state updates
182
+ const [firstName, setFirstName] = useState('')
183
+ const [lastName, setLastName] = useState('')
184
+ const [email, setEmail] = useState('')
185
+
186
+ // GOOD: Group related state
187
+ const [formData, setFormData] = useState({
188
+ firstName: '',
189
+ lastName: '',
190
+ email: '',
191
+ })
192
+ ```
193
+
194
+ ---
195
+
196
+ ## useEffect Patterns
197
+
198
+ ### Dependency Arrays
199
+
200
+ ```tsx
201
+ // BAD: Missing dependencies
202
+ useEffect(() => {
203
+ fetchUser(userId) // userId should be in deps
204
+ }, [])
205
+
206
+ // BAD: Object/array in deps (new reference each render)
207
+ useEffect(() => {
208
+ doSomething(options)
209
+ }, [options]) // If options = { foo: 'bar' } in render, runs every time
210
+
211
+ // GOOD: Stable primitive dependencies
212
+ useEffect(() => {
213
+ fetchUser(userId)
214
+ }, [userId])
215
+
216
+ // GOOD: Memoize objects if needed in deps
217
+ const options = useMemo(() => ({ foo: 'bar' }), [])
218
+ useEffect(() => {
219
+ doSomething(options)
220
+ }, [options])
221
+ ```
222
+
223
+ ### Cleanup
224
+
225
+ ```tsx
226
+ // BAD: No cleanup for subscriptions
227
+ useEffect(() => {
228
+ const subscription = subscribe(channel)
229
+ // Memory leak! No cleanup
230
+ }, [channel])
231
+
232
+ // GOOD: Proper cleanup
233
+ useEffect(() => {
234
+ const subscription = subscribe(channel)
235
+ return () => {
236
+ subscription.unsubscribe()
237
+ }
238
+ }, [channel])
239
+
240
+ // GOOD: Abort controller for fetch
241
+ useEffect(() => {
242
+ const controller = new AbortController()
243
+
244
+ fetch(url, { signal: controller.signal })
245
+ .then(res => res.json())
246
+ .then(setData)
247
+ .catch(err => {
248
+ if (err.name !== 'AbortError') {
249
+ setError(err)
250
+ }
251
+ })
252
+
253
+ return () => controller.abort()
254
+ }, [url])
255
+ ```
256
+
257
+ ### Avoid useEffect for Derived Data
258
+
259
+ ```tsx
260
+ // BAD: useEffect for transformation
261
+ const [items, setItems] = useState<Item[]>([])
262
+ const [sortedItems, setSortedItems] = useState<Item[]>([])
263
+
264
+ useEffect(() => {
265
+ setSortedItems([...items].sort((a, b) => a.name.localeCompare(b.name)))
266
+ }, [items])
267
+
268
+ // GOOD: Compute directly or useMemo
269
+ const sortedItems = useMemo(
270
+ () => [...items].sort((a, b) => a.name.localeCompare(b.name)),
271
+ [items]
272
+ )
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Performance Optimization
278
+
279
+ ### When to Use useMemo
280
+
281
+ ```tsx
282
+ // DON'T: Simple operations
283
+ const fullName = useMemo(() => `${first} ${last}`, [first, last]) // Overkill
284
+
285
+ // DO: Expensive computations
286
+ const sortedData = useMemo(
287
+ () => data.sort((a, b) => complexComparison(a, b)),
288
+ [data]
289
+ )
290
+
291
+ // DO: Reference stability for child components
292
+ const chartOptions = useMemo(
293
+ () => ({ responsive: true, scales: { ... } }),
294
+ []
295
+ )
296
+ ```
297
+
298
+ ### When to Use useCallback
299
+
300
+ ```tsx
301
+ // DON'T: Handlers that don't get passed down
302
+ const handleClick = useCallback(() => {
303
+ doSomething()
304
+ }, [])
305
+ // If handleClick isn't passed to memoized children, useCallback is pointless
306
+
307
+ // DO: Handlers passed to memoized children
308
+ const handleDelete = useCallback((id: string) => {
309
+ deleteItem(id)
310
+ }, [deleteItem])
311
+
312
+ <MemoizedList onDelete={handleDelete} />
313
+
314
+ // DO: Handlers in dependency arrays
315
+ const handleFetch = useCallback(() => {
316
+ fetch(url)
317
+ }, [url])
318
+
319
+ useEffect(() => {
320
+ handleFetch()
321
+ }, [handleFetch])
322
+ ```
323
+
324
+ ### React.memo
325
+
326
+ ```tsx
327
+ // Use for components that:
328
+ // 1. Render often
329
+ // 2. Receive the same props usually
330
+ // 3. Are expensive to render
331
+
332
+ const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
333
+ return (
334
+ <ul>
335
+ {items.map(item => (
336
+ <ExpensiveItem key={item.id} {...item} />
337
+ ))}
338
+ </ul>
339
+ )
340
+ })
341
+ ```
342
+
343
+ ---
344
+
345
+ ## React 19 Specific Patterns
346
+
347
+ ### use() Hook
348
+
349
+ ```tsx
350
+ // React 19: use() for promises in render
351
+ import { use } from 'react'
352
+
353
+ function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
354
+ const user = use(userPromise) // Suspends until resolved
355
+ return <div>{user.name}</div>
356
+ }
357
+ ```
358
+
359
+ ### Actions (Server Actions in Next.js)
360
+
361
+ ```tsx
362
+ // Form with Server Action
363
+ async function createItem(formData: FormData) {
364
+ 'use server'
365
+ const name = formData.get('name')
366
+ // Create item...
367
+ }
368
+
369
+ function CreateForm() {
370
+ return (
371
+ <form action={createItem}>
372
+ <Input name="name" />
373
+ <Button type="submit">Create</Button>
374
+ </form>
375
+ )
376
+ }
377
+ ```
378
+
379
+ ### useActionState (React 19)
380
+
381
+ ```tsx
382
+ import { useActionState } from 'react'
383
+
384
+ function Form() {
385
+ const [state, formAction, isPending] = useActionState(
386
+ async (prevState, formData) => {
387
+ const result = await submitForm(formData)
388
+ return result
389
+ },
390
+ null
391
+ )
392
+
393
+ return (
394
+ <form action={formAction}>
395
+ <Input name="email" />
396
+ <Button disabled={isPending}>
397
+ {isPending ? 'Submitting...' : 'Submit'}
398
+ </Button>
399
+ {state?.error && <p className="text-destructive">{state.error}</p>}
400
+ </form>
401
+ )
402
+ }
403
+ ```
404
+
405
+ ### useOptimistic (React 19)
406
+
407
+ ```tsx
408
+ import { useOptimistic } from 'react'
409
+
410
+ function TodoList({ todos }: { todos: Todo[] }) {
411
+ const [optimisticTodos, addOptimisticTodo] = useOptimistic(
412
+ todos,
413
+ (state, newTodo: Todo) => [...state, newTodo]
414
+ )
415
+
416
+ async function addTodo(formData: FormData) {
417
+ const newTodo = { id: crypto.randomUUID(), text: formData.get('text') }
418
+ addOptimisticTodo(newTodo) // Immediately show
419
+ await createTodo(formData) // Actually create
420
+ }
421
+
422
+ return (
423
+ <form action={addTodo}>
424
+ {/* ... */}
425
+ </form>
426
+ )
427
+ }
428
+ ```
429
+
430
+ ---
431
+
432
+ ## Common Anti-Patterns
433
+
434
+ ### Prop Drilling
435
+
436
+ ```tsx
437
+ // BAD: Passing props through many layers
438
+ <App user={user} />
439
+ <Layout user={user} />
440
+ <Sidebar user={user} />
441
+ <UserMenu user={user} />
442
+
443
+ // GOOD: Context for widely-used data
444
+ const UserContext = createContext<User | null>(null)
445
+
446
+ function App() {
447
+ return (
448
+ <UserContext.Provider value={user}>
449
+ <Layout />
450
+ </UserContext.Provider>
451
+ )
452
+ }
453
+
454
+ function UserMenu() {
455
+ const user = useContext(UserContext)
456
+ // ...
457
+ }
458
+ ```
459
+
460
+ ### Unnecessary State
461
+
462
+ ```tsx
463
+ // BAD: State for URL params (Next.js handles this)
464
+ const [searchQuery, setSearchQuery] = useState(
465
+ searchParams.get('q') || ''
466
+ )
467
+
468
+ // GOOD: Use URL directly
469
+ const searchQuery = searchParams.get('q') || ''
470
+ ```
471
+
472
+ ### Conditional Hooks
473
+
474
+ ```tsx
475
+ // BAD: Hooks called conditionally
476
+ if (isLoggedIn) {
477
+ const user = useUser() // BREAKS RULES OF HOOKS
478
+ }
479
+
480
+ // GOOD: Always call, handle null
481
+ const user = useUser() // Always called
482
+ if (!isLoggedIn || !user) return <Login />
483
+ ```
484
+
485
+ ---
486
+
487
+ ## Testing Considerations
488
+
489
+ ### Testable Component Structure
490
+
491
+ ```tsx
492
+ // GOOD: Logic separated from UI
493
+ function useCounter(initial = 0) {
494
+ const [count, setCount] = useState(initial)
495
+ const increment = useCallback(() => setCount(c => c + 1), [])
496
+ const decrement = useCallback(() => setCount(c => c - 1), [])
497
+ return { count, increment, decrement }
498
+ }
499
+
500
+ function Counter() {
501
+ const { count, increment, decrement } = useCounter()
502
+ return (
503
+ <div>
504
+ <Button onClick={decrement}>-</Button>
505
+ <span>{count}</span>
506
+ <Button onClick={increment}>+</Button>
507
+ </div>
508
+ )
509
+ }
510
+
511
+ // Hook can be tested separately from component
512
+ ```