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.
@@ -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. Keep components, styles, tests, and types in the same directory:
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`, `DashboardHeader`)
37
- - Files: kebab-case (`user-card.tsx`, `dashboard-header.tsx`)
38
- - Hooks: camelCase with `use` prefix (`useAuth`, `useMediaQuery`)
39
- - Event handlers: `handle` + event (`handleClick`, `handleSubmit`)
40
- - Boolean props: `is`/`has`/`should` prefix (`isLoading`, `hasError`)
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 components and layout components
44
- - **Named export**: everything else (utilities, hooks, shared components)
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 and loops unlike other hooks:
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 the parent form's pending state from within child components:
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 an async action completes:
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
- React 19 ships with an opt-in compiler that auto-memoizes components, hooks, and expressions. When the compiler is enabled, remove manual `React.memo`, `useMemo`, and `useCallback` calls -- the compiler handles it. Check your project's `react-compiler` config before deciding whether to memoize manually.
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 (use `searchParams`)
108
- 2. **Server state**: data from APIs (use server components or React Query/SWR for client)
109
- 3. **Local state**: form inputs, UI toggles, hover/focus state (use `useState`)
110
- 4. **Shared local state**: state needed by siblings (lift to parent, or use context)
111
- 5. **Global state**: rarely needed (auth user, theme preference, feature flags)
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 it during render.
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` over `useState` when the next state depends on the previous state or when managing more than 3 related state variables.
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 enabled: skip manual `React.memo`, `useMemo`, `useCallback`
130
- - Without the compiler: use `React.memo` only for components that re-render often with the same props; use `useMemo` for expensive computations only; use `useCallback` for callbacks passed to memoized children
131
- - Use `key` props correctly (stable, unique identifiers, never array indices for reorderable lists)
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 (works in Next.js App Router and plain React):
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, use `@tanstack/virtual` or `react-window`.
80
+ For lists with 100+ items: `@tanstack/virtual` or `react-window`.
143
81
 
144
82
  ### Image Optimization
145
- Use `next/image` in Next.js or `loading="lazy"` with explicit `width`/`height`. Always set `aspect-ratio` to prevent layout shift.
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
- Components that share implicit state through context (e.g., `<Select>`, `<Select.Trigger>`, `<Select.Item>` pattern). Parent holds state, children consume via context.
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 component trees and enables flexible layouts without render props.
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
- ```tsx
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 actions in a `'use server'` file and call from client components via form `action`:
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
- Server Actions handle serialization, error boundaries, and progressive enhancement. Prefer them over API route handlers for mutations.
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
- Tailwind v4 uses a CSS-first configuration model. Key differences from v3:
207
-
208
- - **No `tailwind.config.js`**: configuration lives in CSS using the `@theme` directive
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
- You can reference these tokens anywhere in CSS or JS via `var(--color-brand)` without any build-step workaround.
226
-
227
- ### Tailwind Best Practices
228
- - Use Tailwind's core utility classes (pre-defined classes only in Claude artifacts)
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>` for navigation, `<main>` for primary content, `<section>` for thematic grouping, `<article>` for self-contained content, `<button>` for actions, `<a>` for navigation. Do not use bare `div` where semantic elements apply.
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
- Complete dark mode implementation with localStorage persistence, system preference detection, and flash prevention.
248
-
249
- ### Blocking Script (prevents flash of wrong theme)
250
- Place inline in `<head>` before stylesheets (in Next.js, use `<script dangerouslySetInnerHTML>` in `app/layout.tsx`):
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 instead)
308
- - Putting everything in global state (most state should be local or server-derived)
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 the React Compiler handle this)
311
- - Using `any` in TypeScript (defeats the purpose of type safety)
312
- - Fetching data in `useEffect` when a server component would suffice
313
- - Not using Suspense boundaries (the whole page flashes instead of parts loading independently)
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
- - Using API route handlers for mutations when Server Actions are simpler
316
- - Not using `useOptimistic` for actions that need instant visual feedback
317
- - Manual memoization when the React Compiler is enabled (causes unnecessary code noise)
318
- - Using `@tailwind` directives or `tailwind.config.js` in Tailwind v4 projects (use `@import "tailwindcss"` and `@theme` instead)
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`)