picasso-skill 1.5.1 → 2.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.
- package/agents/picasso.md +14 -2
- package/checklists/pre-ship.md +83 -0
- package/commands/backlog.md +34 -0
- package/commands/variants.md +18 -0
- package/package.json +3 -1
- package/references/accessibility-wcag.md +245 -0
- package/references/anti-patterns.md +184 -0
- package/references/color-and-contrast.md +477 -0
- package/references/component-patterns.md +113 -0
- package/references/conversion-design.md +193 -0
- package/references/data-visualization.md +226 -0
- package/references/depth-and-elevation.md +211 -0
- package/references/design-system.md +176 -0
- package/references/generative-art.md +648 -0
- package/references/interaction-design.md +162 -0
- package/references/modern-css-performance.md +361 -0
- package/references/motion-and-animation.md +267 -0
- package/references/performance-optimization.md +746 -0
- package/references/react-patterns.md +318 -0
- package/references/responsive-design.md +452 -0
- package/references/sensory-design.md +369 -0
- package/references/spatial-design.md +176 -0
- package/references/style-presets.md +502 -0
- package/references/tools-catalog.md +103 -0
- package/references/typography.md +415 -0
- package/references/ux-psychology.md +235 -0
- package/references/ux-writing.md +513 -0
- package/skills/picasso/SKILL.md +58 -2
- package/skills/picasso/references/animation-performance.md +244 -0
- package/skills/picasso/references/brand-and-identity.md +136 -0
- package/skills/picasso/references/code-typography.md +222 -0
- package/skills/picasso/references/color-and-contrast.md +56 -2
- package/skills/picasso/references/dark-mode.md +199 -0
- package/skills/picasso/references/depth-and-elevation.md +211 -0
- package/skills/picasso/references/i18n-visual-patterns.md +177 -0
- package/skills/picasso/references/images-and-media.md +222 -0
- package/skills/picasso/references/loading-and-states.md +258 -0
- package/skills/picasso/references/micro-interactions.md +291 -0
- package/skills/picasso/references/motion-and-animation.md +9 -2
- package/skills/picasso/references/navigation-patterns.md +247 -0
- package/skills/picasso/references/style-presets.md +1 -1
- package/skills/picasso/references/tables-and-forms.md +227 -0
- package/skills/picasso/references/tools-catalog.md +103 -0
- package/skills/picasso/references/typography.md +45 -2
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# React Patterns Reference
|
|
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
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Component Architecture
|
|
17
|
+
|
|
18
|
+
### Server vs. Client Components
|
|
19
|
+
Default to Server Components. Add `'use client'` only when the component needs:
|
|
20
|
+
- Event handlers (onClick, onChange, etc.)
|
|
21
|
+
- useState, useEffect, useRef, or other hooks
|
|
22
|
+
- Browser APIs (window, document, navigator)
|
|
23
|
+
- Third-party libraries that use hooks or browser APIs
|
|
24
|
+
|
|
25
|
+
### File Organization
|
|
26
|
+
Colocate related files. Keep components, styles, tests, and types in the same directory:
|
|
27
|
+
```
|
|
28
|
+
components/
|
|
29
|
+
user-card/
|
|
30
|
+
user-card.tsx
|
|
31
|
+
user-card.test.tsx
|
|
32
|
+
types.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 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`)
|
|
41
|
+
|
|
42
|
+
### Export Patterns
|
|
43
|
+
- **Default export**: page/route components and layout components
|
|
44
|
+
- **Named export**: everything else (utilities, hooks, shared components)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. React 19 Features
|
|
49
|
+
|
|
50
|
+
### 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
|
+
```
|
|
58
|
+
|
|
59
|
+
### `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
|
+
```
|
|
76
|
+
|
|
77
|
+
### `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
|
+
```
|
|
88
|
+
|
|
89
|
+
### `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
|
+
```
|
|
98
|
+
|
|
99
|
+
### 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.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 3. State Management
|
|
105
|
+
|
|
106
|
+
### 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)
|
|
112
|
+
|
|
113
|
+
### Rules
|
|
114
|
+
- Do not store derived state. Compute it during render.
|
|
115
|
+
- 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
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 4. Performance
|
|
127
|
+
|
|
128
|
+
### 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)
|
|
132
|
+
|
|
133
|
+
### 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`.
|
|
140
|
+
|
|
141
|
+
### Virtualization
|
|
142
|
+
For lists with 100+ items, use `@tanstack/virtual` or `react-window`.
|
|
143
|
+
|
|
144
|
+
### Image Optimization
|
|
145
|
+
Use `next/image` in Next.js or `loading="lazy"` with explicit `width`/`height`. Always set `aspect-ratio` to prevent layout shift.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 5. Composition Patterns
|
|
150
|
+
|
|
151
|
+
### 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.
|
|
153
|
+
|
|
154
|
+
### Slot Pattern
|
|
155
|
+
Flexible composition through named children (`header`, `children`, `footer` as props). Avoids rigid component trees and enables flexible layouts without render props.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 6. Data Fetching & Mutations
|
|
160
|
+
|
|
161
|
+
### 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
|
+
```
|
|
168
|
+
|
|
169
|
+
### 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
|
+
```
|
|
188
|
+
|
|
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
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 7. Styling & Tailwind v4
|
|
204
|
+
|
|
205
|
+
### 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.)
|
|
211
|
+
|
|
212
|
+
```css
|
|
213
|
+
/* app/globals.css */
|
|
214
|
+
@import "tailwindcss";
|
|
215
|
+
|
|
216
|
+
@theme {
|
|
217
|
+
--color-brand: #6366f1;
|
|
218
|
+
--color-surface: #ffffff;
|
|
219
|
+
--color-surface-dark: #0f172a;
|
|
220
|
+
--font-display: "Inter", sans-serif;
|
|
221
|
+
--breakpoint-3xl: 1920px;
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
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
|
+
```
|
|
239
|
+
|
|
240
|
+
### 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.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 8. Dark Mode Toggle
|
|
246
|
+
|
|
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
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## 9. Common Mistakes
|
|
306
|
+
|
|
307
|
+
- Using `useEffect` for derived state (compute during render instead)
|
|
308
|
+
- Putting everything in global state (most state should be local or server-derived)
|
|
309
|
+
- 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)
|
|
314
|
+
- 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)
|