picasso-skill 2.4.0 → 2.6.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.
- package/agents/picasso.md +26 -542
- package/commands/godmode.md +3 -13
- package/commands/roast.md +3 -14
- package/commands/score.md +3 -18
- package/commands/steal.md +3 -31
- package/package.json +1 -1
- package/references/accessibility-wcag.md +3 -0
- package/references/code-typography.md +36 -166
- package/references/color-and-contrast.md +78 -345
- package/references/generative-art.md +49 -561
- package/references/modern-css-performance.md +46 -258
- package/references/motion-and-animation.md +225 -88
- package/references/navigation-patterns.md +29 -186
- package/references/performance-optimization.md +42 -678
- package/references/react-patterns.md +56 -216
- package/references/responsive-design.md +77 -379
- package/references/sensory-design.md +62 -263
- package/references/ux-writing.md +64 -354
- package/references/animation-performance.md +0 -244
- package/references/interaction-design.md +0 -162
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
# React Patterns Reference
|
|
2
2
|
|
|
3
|
-
## Table of Contents
|
|
4
|
-
1. Component Architecture
|
|
5
|
-
2. React 19 Features
|
|
6
|
-
3. State Management
|
|
7
|
-
4. Performance
|
|
8
|
-
5. Composition Patterns
|
|
9
|
-
6. Data Fetching & Mutations
|
|
10
|
-
7. Styling & Tailwind v4
|
|
11
|
-
8. Dark Mode Toggle
|
|
12
|
-
9. Common Mistakes
|
|
13
|
-
|
|
14
3
|
---
|
|
15
4
|
|
|
16
5
|
## 1. Component Architecture
|
|
@@ -23,296 +12,147 @@ Default to Server Components. Add `'use client'` only when the component needs:
|
|
|
23
12
|
- Third-party libraries that use hooks or browser APIs
|
|
24
13
|
|
|
25
14
|
### File Organization
|
|
26
|
-
Colocate related files.
|
|
15
|
+
Colocate related files. Components, styles, tests, and types in the same directory:
|
|
27
16
|
```
|
|
28
|
-
components/
|
|
29
|
-
user-card
|
|
30
|
-
user-card.tsx
|
|
31
|
-
user-card.test.tsx
|
|
32
|
-
types.ts
|
|
17
|
+
components/user-card/
|
|
18
|
+
user-card.tsx, user-card.test.tsx, types.ts
|
|
33
19
|
```
|
|
34
20
|
|
|
35
21
|
### Naming
|
|
36
|
-
- Components: PascalCase (`UserCard
|
|
37
|
-
- Files: kebab-case (`user-card.tsx
|
|
38
|
-
- Hooks: camelCase with `use` prefix (`useAuth
|
|
39
|
-
- Event handlers: `handle` + event (`
|
|
40
|
-
- Boolean props: `is`/`has`/`should` prefix (`isLoading
|
|
22
|
+
- Components: PascalCase (`UserCard`)
|
|
23
|
+
- Files: kebab-case (`user-card.tsx`)
|
|
24
|
+
- Hooks: camelCase with `use` prefix (`useAuth`)
|
|
25
|
+
- Event handlers: `handle` + event (`handleSubmit`)
|
|
26
|
+
- Boolean props: `is`/`has`/`should` prefix (`isLoading`)
|
|
41
27
|
|
|
42
28
|
### Export Patterns
|
|
43
|
-
- **Default export**: page/route
|
|
44
|
-
- **Named export**: everything else
|
|
29
|
+
- **Default export**: page/route and layout components
|
|
30
|
+
- **Named export**: everything else
|
|
45
31
|
|
|
46
32
|
---
|
|
47
33
|
|
|
48
34
|
## 2. React 19 Features
|
|
49
35
|
|
|
50
36
|
### The `use` Hook
|
|
51
|
-
Read promises and context directly in render. Works inside conditionals
|
|
52
|
-
```tsx
|
|
53
|
-
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
|
|
54
|
-
const user = use(userPromise); // suspends until resolved
|
|
55
|
-
return <h1>{user.name}</h1>;
|
|
56
|
-
}
|
|
57
|
-
```
|
|
37
|
+
Read promises and context directly in render. Works inside conditionals/loops unlike other hooks.
|
|
58
38
|
|
|
59
39
|
### `useActionState` for Form Actions
|
|
60
|
-
Manages form state, pending status, and server action results in one hook
|
|
61
|
-
```tsx
|
|
62
|
-
'use client';
|
|
63
|
-
import { useActionState } from 'react';
|
|
64
|
-
import { submitForm } from './actions';
|
|
65
|
-
|
|
66
|
-
function ContactForm() {
|
|
67
|
-
const [state, formAction, isPending] = useActionState(submitForm, { message: '' });
|
|
68
|
-
return (
|
|
69
|
-
<form action={formAction}>
|
|
70
|
-
<input name="email" type="email" required />
|
|
71
|
-
<button disabled={isPending}>{isPending ? 'Sending...' : 'Submit'}</button>
|
|
72
|
-
</form>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
```
|
|
40
|
+
Manages form state, pending status, and server action results in one hook. Pair with `<form action={formAction}>`.
|
|
76
41
|
|
|
77
42
|
### `useFormStatus` for Submission State
|
|
78
|
-
Access
|
|
79
|
-
```tsx
|
|
80
|
-
'use client';
|
|
81
|
-
import { useFormStatus } from 'react-dom';
|
|
82
|
-
|
|
83
|
-
function SubmitButton() {
|
|
84
|
-
const { pending } = useFormStatus();
|
|
85
|
-
return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>;
|
|
86
|
-
}
|
|
87
|
-
```
|
|
43
|
+
Access parent form's pending state from child components (e.g., a submit button).
|
|
88
44
|
|
|
89
45
|
### `useOptimistic` for Instant Feedback
|
|
90
|
-
Show optimistic state while
|
|
91
|
-
```tsx
|
|
92
|
-
const [optimistic, addOptimistic] = useOptimistic(
|
|
93
|
-
messages,
|
|
94
|
-
(current, newMsg: string) => [...current, { text: newMsg, pending: true }]
|
|
95
|
-
);
|
|
96
|
-
// Call addOptimistic(value) before await -- renders instantly, reverts on error
|
|
97
|
-
```
|
|
46
|
+
Show optimistic state while async action completes. Call `addOptimistic(value)` before `await`; renders instantly, reverts on error.
|
|
98
47
|
|
|
99
48
|
### React Compiler
|
|
100
|
-
|
|
49
|
+
When enabled, auto-memoizes components, hooks, and expressions. Remove manual `React.memo`, `useMemo`, `useCallback`. Check project's `react-compiler` config first.
|
|
101
50
|
|
|
102
51
|
---
|
|
103
52
|
|
|
104
53
|
## 3. State Management
|
|
105
54
|
|
|
106
55
|
### Where State Lives
|
|
107
|
-
1. **URL state**: filters, pagination, search queries (
|
|
108
|
-
2. **Server state**: data
|
|
109
|
-
3. **Local state**: form inputs, UI toggles
|
|
110
|
-
4. **Shared local state**:
|
|
111
|
-
5. **Global state**: rarely needed (auth
|
|
56
|
+
1. **URL state**: filters, pagination, search queries (`searchParams`)
|
|
57
|
+
2. **Server state**: API data (server components or React Query/SWR)
|
|
58
|
+
3. **Local state**: form inputs, UI toggles (`useState`)
|
|
59
|
+
4. **Shared local state**: needed by siblings (lift to parent or context)
|
|
60
|
+
5. **Global state**: rarely needed (auth, theme, feature flags)
|
|
112
61
|
|
|
113
62
|
### Rules
|
|
114
|
-
- Do not store derived state. Compute
|
|
63
|
+
- Do not store derived state. Compute during render.
|
|
115
64
|
- Do not sync state between sources. Pick one source of truth.
|
|
116
|
-
- Prefer `useReducer`
|
|
117
|
-
|
|
118
|
-
```tsx
|
|
119
|
-
// Bad: derived state in useEffect // Good: compute during render
|
|
120
|
-
const [filtered, setFiltered] = useState([]); const filtered = items.filter(i => i.active);
|
|
121
|
-
useEffect(() => setFiltered(items.filter(i => i.active)), [items]);
|
|
122
|
-
```
|
|
65
|
+
- Prefer `useReducer` when next state depends on previous state or 3+ related variables.
|
|
123
66
|
|
|
124
67
|
---
|
|
125
68
|
|
|
126
69
|
## 4. Performance
|
|
127
70
|
|
|
128
71
|
### Rendering
|
|
129
|
-
- With React Compiler
|
|
130
|
-
- Without
|
|
131
|
-
- Use `key` props
|
|
72
|
+
- With React Compiler: skip manual memoization
|
|
73
|
+
- Without: `React.memo` for frequently re-rendered components with stable props; `useMemo` for expensive computations; `useCallback` for callbacks to memoized children
|
|
74
|
+
- Use stable, unique `key` props (never array indices for reorderable lists)
|
|
132
75
|
|
|
133
76
|
### Code Splitting
|
|
134
|
-
Use `React.lazy` + `Suspense` for client components
|
|
135
|
-
```tsx
|
|
136
|
-
const Chart = lazy(() => import('./chart'));
|
|
137
|
-
// Wrap in <Suspense fallback={<ChartSkeleton />}><Chart /></Suspense>
|
|
138
|
-
```
|
|
139
|
-
Next.js App Router also provides automatic route-level splitting per `page.tsx`/`layout.tsx`.
|
|
77
|
+
Use `React.lazy` + `Suspense` for client components. Next.js App Router also provides automatic route-level splitting.
|
|
140
78
|
|
|
141
79
|
### Virtualization
|
|
142
|
-
For lists with 100+ items
|
|
80
|
+
For lists with 100+ items: `@tanstack/virtual` or `react-window`.
|
|
143
81
|
|
|
144
82
|
### Image Optimization
|
|
145
|
-
|
|
83
|
+
`next/image` or `loading="lazy"` with explicit `width`/`height`. Always set `aspect-ratio`.
|
|
146
84
|
|
|
147
85
|
---
|
|
148
86
|
|
|
149
87
|
## 5. Composition Patterns
|
|
150
88
|
|
|
151
89
|
### Compound Components
|
|
152
|
-
|
|
90
|
+
Share implicit state through context (`<Select>`, `<Select.Trigger>`, `<Select.Item>`). Parent holds state, children consume via context.
|
|
153
91
|
|
|
154
92
|
### Slot Pattern
|
|
155
|
-
Flexible composition through named children (`header`, `children`, `footer` as props). Avoids rigid
|
|
93
|
+
Flexible composition through named children (`header`, `children`, `footer` as props). Avoids rigid trees.
|
|
156
94
|
|
|
157
95
|
---
|
|
158
96
|
|
|
159
97
|
## 6. Data Fetching & Mutations
|
|
160
98
|
|
|
161
99
|
### Server Components (preferred for reads)
|
|
162
|
-
|
|
163
|
-
async function UserList() {
|
|
164
|
-
const users = await fetch('/api/users').then(r => r.json());
|
|
165
|
-
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
|
|
166
|
-
}
|
|
167
|
-
```
|
|
100
|
+
`async` component, `await fetch()` directly. No hooks needed.
|
|
168
101
|
|
|
169
102
|
### Server Actions (preferred for mutations)
|
|
170
|
-
Define
|
|
171
|
-
```tsx
|
|
172
|
-
// actions.ts
|
|
173
|
-
'use server';
|
|
174
|
-
import { revalidatePath } from 'next/cache';
|
|
175
|
-
|
|
176
|
-
export async function createTodo(formData: FormData) {
|
|
177
|
-
await db.todo.create({ data: { title: formData.get('title') as string } });
|
|
178
|
-
revalidatePath('/todos');
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
```tsx
|
|
182
|
-
// todo-form.tsx -- 'use client'
|
|
183
|
-
import { createTodo } from './actions';
|
|
184
|
-
export default function TodoForm() {
|
|
185
|
-
return <form action={createTodo}><input name="title" required /><button>Add</button></form>;
|
|
186
|
-
}
|
|
187
|
-
```
|
|
103
|
+
Define in `'use server'` file, call from client via form `action`. Handles serialization, error boundaries, progressive enhancement. Prefer over API route handlers.
|
|
188
104
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
### Client-Side with Suspense + Error Boundaries
|
|
192
|
-
Always wrap data-fetching components:
|
|
193
|
-
```tsx
|
|
194
|
-
<ErrorBoundary fallback={<ErrorMessage />}>
|
|
195
|
-
<Suspense fallback={<Skeleton />}>
|
|
196
|
-
<DataComponent />
|
|
197
|
-
</Suspense>
|
|
198
|
-
</ErrorBoundary>
|
|
199
|
-
```
|
|
105
|
+
### Client-Side
|
|
106
|
+
Always wrap data-fetching components in `<ErrorBoundary>` + `<Suspense>`.
|
|
200
107
|
|
|
201
108
|
---
|
|
202
109
|
|
|
203
110
|
## 7. Styling & Tailwind v4
|
|
204
111
|
|
|
205
112
|
### Tailwind v4 Changes
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
- **
|
|
209
|
-
- **New import**: use `@import "tailwindcss"` instead of `@tailwind base/components/utilities` directives
|
|
210
|
-
- **CSS variables for all tokens**: every design token is automatically exposed as a CSS custom property (`--color-blue-500`, `--spacing-4`, etc.)
|
|
113
|
+
- **No `tailwind.config.js`**: configuration in CSS via `@theme` directive
|
|
114
|
+
- **New import**: `@import "tailwindcss"` instead of `@tailwind base/components/utilities`
|
|
115
|
+
- **CSS variables for all tokens**: auto-exposed as `--color-blue-500`, `--spacing-4`, etc.
|
|
211
116
|
|
|
212
117
|
```css
|
|
213
|
-
/* app/globals.css */
|
|
214
118
|
@import "tailwindcss";
|
|
215
|
-
|
|
216
119
|
@theme {
|
|
217
120
|
--color-brand: #6366f1;
|
|
218
|
-
--color-surface: #ffffff;
|
|
219
|
-
--color-surface-dark: #0f172a;
|
|
220
121
|
--font-display: "Inter", sans-serif;
|
|
221
122
|
--breakpoint-3xl: 1920px;
|
|
222
123
|
}
|
|
223
124
|
```
|
|
224
125
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
- Extract repeated patterns into component variants, not `@apply` rules
|
|
230
|
-
- Use CSS variables for theme values, Tailwind utilities for everything else
|
|
231
|
-
- Never use more than ~10 utility classes on a single element; extract a component instead
|
|
232
|
-
|
|
233
|
-
### CSS Modules
|
|
234
|
-
For non-Tailwind projects:
|
|
235
|
-
```tsx
|
|
236
|
-
import styles from './button.module.css';
|
|
237
|
-
<button className={styles.primary}>Click</button>
|
|
238
|
-
```
|
|
126
|
+
### Best Practices
|
|
127
|
+
- Use core utility classes; extract repeated patterns into component variants, not `@apply`
|
|
128
|
+
- CSS variables for theme values, Tailwind utilities for everything else
|
|
129
|
+
- Never exceed ~10 utility classes on a single element; extract a component
|
|
239
130
|
|
|
240
131
|
### Semantic HTML
|
|
241
|
-
Use the right element: `<nav
|
|
132
|
+
Use the right element: `<nav>`, `<main>`, `<section>`, `<article>`, `<button>`, `<a>`.
|
|
242
133
|
|
|
243
134
|
---
|
|
244
135
|
|
|
245
136
|
## 8. Dark Mode Toggle
|
|
246
137
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
```html
|
|
252
|
-
<script>
|
|
253
|
-
(function() {
|
|
254
|
-
var s = localStorage.getItem('theme');
|
|
255
|
-
var theme = s || (matchMedia('(prefers-color-scheme:dark)').matches ? 'dark' : 'light');
|
|
256
|
-
document.documentElement.setAttribute('data-theme', theme);
|
|
257
|
-
})();
|
|
258
|
-
</script>
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Theme Toggle Hook
|
|
262
|
-
```tsx
|
|
263
|
-
'use client';
|
|
264
|
-
function useTheme() {
|
|
265
|
-
const [theme, setThemeState] = useState<'light' | 'dark'>(() =>
|
|
266
|
-
typeof window === 'undefined' ? 'light'
|
|
267
|
-
: (document.documentElement.getAttribute('data-theme') as 'light' | 'dark') || 'light'
|
|
268
|
-
);
|
|
269
|
-
const setTheme = useCallback((next: 'light' | 'dark') => {
|
|
270
|
-
document.documentElement.setAttribute('data-theme', next);
|
|
271
|
-
localStorage.setItem('theme', next);
|
|
272
|
-
setThemeState(next);
|
|
273
|
-
}, []);
|
|
274
|
-
const toggle = useCallback(() => setTheme(theme === 'dark' ? 'light' : 'dark'), [theme, setTheme]);
|
|
275
|
-
// Listen for system preference changes when no explicit choice is stored
|
|
276
|
-
useEffect(() => {
|
|
277
|
-
const mq = matchMedia('(prefers-color-scheme: dark)');
|
|
278
|
-
const h = (e: MediaQueryListEvent) => { if (!localStorage.getItem('theme')) setTheme(e.matches ? 'dark' : 'light'); };
|
|
279
|
-
mq.addEventListener('change', h);
|
|
280
|
-
return () => mq.removeEventListener('change', h);
|
|
281
|
-
}, [setTheme]);
|
|
282
|
-
return { theme, setTheme, toggle };
|
|
283
|
-
}
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### CSS Setup with Tailwind v4
|
|
287
|
-
```css
|
|
288
|
-
@import "tailwindcss";
|
|
289
|
-
|
|
290
|
-
@theme {
|
|
291
|
-
--color-bg: #ffffff;
|
|
292
|
-
--color-text: #0f172a;
|
|
293
|
-
--color-surface: #f8fafc;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
[data-theme="dark"] {
|
|
297
|
-
--color-bg: #0f172a;
|
|
298
|
-
--color-text: #f8fafc;
|
|
299
|
-
--color-surface: #1e293b;
|
|
300
|
-
}
|
|
301
|
-
```
|
|
138
|
+
Three parts:
|
|
139
|
+
1. **Blocking script** in `<head>` to read localStorage/system preference and set `data-theme` before paint (prevents flash)
|
|
140
|
+
2. **`useTheme` hook** with `useState`, `useCallback`, and system preference listener
|
|
141
|
+
3. **CSS variables** that flip under `[data-theme="dark"]`
|
|
302
142
|
|
|
303
143
|
---
|
|
304
144
|
|
|
305
145
|
## 9. Common Mistakes
|
|
306
146
|
|
|
307
|
-
- Using `useEffect` for derived state (compute during render
|
|
308
|
-
- Putting everything in global state (most state
|
|
147
|
+
- Using `useEffect` for derived state (compute during render)
|
|
148
|
+
- Putting everything in global state (most state is local or server-derived)
|
|
309
149
|
- Using `index` as `key` for dynamic lists
|
|
310
|
-
- Wrapping every component in `React.memo` (let
|
|
311
|
-
- Using `any` in TypeScript
|
|
312
|
-
- Fetching
|
|
313
|
-
- Not using Suspense boundaries (
|
|
150
|
+
- Wrapping every component in `React.memo` (let React Compiler handle it)
|
|
151
|
+
- Using `any` in TypeScript
|
|
152
|
+
- Fetching in `useEffect` when a server component would suffice
|
|
153
|
+
- Not using Suspense boundaries (whole page flashes instead of parts)
|
|
314
154
|
- Prop drilling through 5+ levels (use composition or context)
|
|
315
|
-
-
|
|
316
|
-
- Not using `useOptimistic` for actions
|
|
317
|
-
- Manual memoization when
|
|
318
|
-
- Using `@tailwind` directives or `tailwind.config.js` in Tailwind v4
|
|
155
|
+
- API route handlers for mutations when Server Actions are simpler
|
|
156
|
+
- Not using `useOptimistic` for actions needing instant feedback
|
|
157
|
+
- Manual memoization when React Compiler is enabled
|
|
158
|
+
- Using `@tailwind` directives or `tailwind.config.js` in Tailwind v4 (use `@import "tailwindcss"` and `@theme`)
|