picasso-skill 1.0.0 → 1.2.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/README.md +70 -45
- package/agents/picasso.md +326 -0
- package/bin/install.mjs +54 -24
- package/package.json +5 -3
- package/skills/picasso/references/accessibility.md +172 -0
- package/skills/picasso/references/design-system.md +14 -14
- package/skills/picasso/references/generative-art.md +626 -32
- package/skills/picasso/references/motion-and-animation.md +2 -2
- package/skills/picasso/references/react-patterns.md +193 -91
- package/skills/picasso/references/responsive-design.md +349 -15
- package/skills/picasso/references/sensory-design.md +294 -50
- package/SKILL.md +0 -202
- package/references/anti-patterns.md +0 -95
- package/references/color-and-contrast.md +0 -174
- package/references/component-patterns.md +0 -113
- package/references/design-system.md +0 -176
- package/references/generative-art.md +0 -54
- package/references/interaction-design.md +0 -162
- package/references/motion-and-animation.md +0 -260
- package/references/react-patterns.md +0 -216
- package/references/responsive-design.md +0 -118
- package/references/sensory-design.md +0 -125
- package/references/spatial-design.md +0 -176
- package/references/typography.md +0 -168
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# Interaction Design Reference
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
1. Form Design
|
|
5
|
-
2. Focus Management
|
|
6
|
-
3. Loading Patterns
|
|
7
|
-
4. Empty States
|
|
8
|
-
5. Error Handling
|
|
9
|
-
6. UX Writing
|
|
10
|
-
7. Common Mistakes
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 1. Form Design
|
|
15
|
-
|
|
16
|
-
### Input Fields
|
|
17
|
-
- Use visible labels above inputs, not placeholder-only labels (placeholders disappear on focus)
|
|
18
|
-
- Input height: 40-48px for desktop, 48px minimum for touch
|
|
19
|
-
- Group related fields visually (name + email, street + city + zip)
|
|
20
|
-
- Show validation inline, not in an alert after submission
|
|
21
|
-
- Use `inputmode` attribute for mobile keyboards: `inputmode="email"`, `inputmode="numeric"`, `inputmode="tel"`
|
|
22
|
-
- Auto-focus the first field on page load when the form is the primary task
|
|
23
|
-
|
|
24
|
-
### Field States
|
|
25
|
-
Every input needs four visible states:
|
|
26
|
-
1. **Default**: subtle border, neutral background
|
|
27
|
-
2. **Focus**: accent border (2px), subtle glow or shadow
|
|
28
|
-
3. **Error**: error-colored border, inline error message below the field
|
|
29
|
-
4. **Disabled**: reduced opacity (0.5), `cursor: not-allowed`
|
|
30
|
-
|
|
31
|
-
### Buttons
|
|
32
|
-
- Primary action: filled, high contrast (accent color)
|
|
33
|
-
- Secondary action: outlined or ghost (border only)
|
|
34
|
-
- Destructive action: red/error color, requires confirmation for irreversible actions
|
|
35
|
-
- Button text: verb-first ("Save changes", "Create project"), never "Submit" or "Click here"
|
|
36
|
-
- Loading state: replace text with spinner or use `aria-busy="true"`, disable the button to prevent double-submission
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## 2. Focus Management
|
|
41
|
-
|
|
42
|
-
### Focus Indicators
|
|
43
|
-
Never remove focus outlines without replacement. Use a visible, high-contrast focus ring:
|
|
44
|
-
```css
|
|
45
|
-
:focus-visible {
|
|
46
|
-
outline: 2px solid var(--accent);
|
|
47
|
-
outline-offset: 2px;
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Use `:focus-visible` (not `:focus`) to show focus rings only for keyboard navigation, not mouse clicks.
|
|
52
|
-
|
|
53
|
-
### Focus Trapping
|
|
54
|
-
Modal dialogs must trap focus within them. When a modal opens:
|
|
55
|
-
1. Move focus to the first focusable element inside
|
|
56
|
-
2. Tab cycles within the modal
|
|
57
|
-
3. Escape closes the modal
|
|
58
|
-
4. Focus returns to the trigger element on close
|
|
59
|
-
|
|
60
|
-
### Skip Links
|
|
61
|
-
Add a skip-to-content link as the first focusable element:
|
|
62
|
-
```html
|
|
63
|
-
<a href="#main-content" class="skip-link">Skip to content</a>
|
|
64
|
-
```
|
|
65
|
-
```css
|
|
66
|
-
.skip-link {
|
|
67
|
-
position: absolute;
|
|
68
|
-
top: -100%;
|
|
69
|
-
left: 0;
|
|
70
|
-
}
|
|
71
|
-
.skip-link:focus {
|
|
72
|
-
top: 0;
|
|
73
|
-
z-index: 1000;
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 3. Loading Patterns
|
|
80
|
-
|
|
81
|
-
### Skeleton Screens
|
|
82
|
-
Replace content with gray shapes that match the expected layout. This feels faster than a spinner because the user can see the structure forming.
|
|
83
|
-
|
|
84
|
-
### Progressive Loading
|
|
85
|
-
Show content as it arrives. Do not wait for everything to load before showing anything. Use `Suspense` boundaries in React to show parts of the page while others load.
|
|
86
|
-
|
|
87
|
-
### Optimistic Updates
|
|
88
|
-
For user-initiated actions (like, save, delete), update the UI immediately and reconcile with the server response. Show a subtle undo option if the server rejects the action.
|
|
89
|
-
|
|
90
|
-
### Spinner vs. Skeleton
|
|
91
|
-
- **Spinner**: Use for actions that take 1-3 seconds (button submissions, API calls). Place inside the triggering element.
|
|
92
|
-
- **Skeleton**: Use for content areas that take 0.5-5 seconds to load. Match the shape of the expected content.
|
|
93
|
-
- **Progress bar**: Use for operations that take 5+ seconds with measurable progress (file uploads, multi-step processes).
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## 4. Empty States
|
|
98
|
-
|
|
99
|
-
Empty states are an opportunity, not a placeholder. They should:
|
|
100
|
-
1. Explain what this area will contain once populated
|
|
101
|
-
2. Provide a clear action to get started
|
|
102
|
-
3. Optionally include an illustration or icon for warmth
|
|
103
|
-
|
|
104
|
-
```
|
|
105
|
-
No projects yet.
|
|
106
|
-
Create your first project to get started.
|
|
107
|
-
[+ Create Project]
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Never show a blank page, an error message, or raw "null" / "undefined" in place of empty content.
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## 5. Error Handling
|
|
115
|
-
|
|
116
|
-
### Inline Errors
|
|
117
|
-
Show errors next to the element that caused them, not in a modal or toast. Use the error color for the field border and display the message directly below.
|
|
118
|
-
|
|
119
|
-
### Error Messages
|
|
120
|
-
- Be specific: "Email address must include an @ symbol" not "Invalid input"
|
|
121
|
-
- Be helpful: suggest the fix, not just the problem
|
|
122
|
-
- Be human: "We couldn't find that page" not "404 Not Found"
|
|
123
|
-
|
|
124
|
-
### Network Errors
|
|
125
|
-
Show a non-blocking banner or inline message with a retry action. Never show a raw error object or stack trace.
|
|
126
|
-
|
|
127
|
-
### Form Validation
|
|
128
|
-
Validate on blur (when the user leaves a field), not on every keystroke. Show success indicators for valid fields (subtle checkmark or green border).
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## 6. UX Writing
|
|
133
|
-
|
|
134
|
-
### Buttons
|
|
135
|
-
- Use action verbs: "Save", "Send", "Create", "Delete"
|
|
136
|
-
- Be specific: "Save changes" > "Save", "Send message" > "Send"
|
|
137
|
-
- Match the action to the context: "Place order" not "Submit"
|
|
138
|
-
|
|
139
|
-
### Labels
|
|
140
|
-
- Clear and concise: "Full name" not "Please enter your full name"
|
|
141
|
-
- Avoid jargon: "Phone number" not "Primary contact number"
|
|
142
|
-
|
|
143
|
-
### Confirmations
|
|
144
|
-
- State what happened: "Project created successfully"
|
|
145
|
-
- Provide next step if relevant: "Project created. Add your first task?"
|
|
146
|
-
|
|
147
|
-
### Destructive Actions
|
|
148
|
-
- State what will happen: "This will permanently delete 3 files"
|
|
149
|
-
- Require explicit confirmation: "Type DELETE to confirm"
|
|
150
|
-
- Provide an out: "Cancel" should be more prominent than "Delete"
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## 7. Common Mistakes
|
|
155
|
-
|
|
156
|
-
- Placeholder text as the only label (disappears on focus, inaccessible)
|
|
157
|
-
- Disabling the submit button before all fields are filled (users do not know which field is missing)
|
|
158
|
-
- Using toast notifications for errors (the user may not see them, they disappear)
|
|
159
|
-
- No loading feedback after clicking a button (user clicks again, causing duplicate submissions)
|
|
160
|
-
- Custom scrollbars that break native scroll behavior
|
|
161
|
-
- Hover-only interactions with no keyboard or touch equivalent
|
|
162
|
-
- Alert/confirm dialogs that block the entire page for minor confirmations
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
# Motion and Animation Reference
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
1. Principles
|
|
5
|
-
2. Easing Functions
|
|
6
|
-
3. Duration Guidelines
|
|
7
|
-
4. Staggered Reveals
|
|
8
|
-
5. Scroll-Triggered Animation
|
|
9
|
-
6. Text Morphing
|
|
10
|
-
7. Micro-Interactions
|
|
11
|
-
8. Reduced Motion
|
|
12
|
-
9. Common Mistakes
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 1. Principles
|
|
17
|
-
|
|
18
|
-
Motion serves three purposes: feedback (confirming an action), orientation (showing where something went), and delight (making the interface feel alive). If an animation does not serve one of these, remove it.
|
|
19
|
-
|
|
20
|
-
### Priority of Animation Investment
|
|
21
|
-
1. Page load choreography (highest impact, seen by everyone)
|
|
22
|
-
2. State transitions (tabs, modals, accordions)
|
|
23
|
-
3. Hover/focus states
|
|
24
|
-
4. Scroll-triggered reveals
|
|
25
|
-
5. Loading states and skeletons
|
|
26
|
-
6. Micro-interactions (button press effects, toggle animations)
|
|
27
|
-
|
|
28
|
-
Invest time in this order. A well-choreographed page load does more than fifty micro-interactions.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## 2. Easing Functions
|
|
33
|
-
|
|
34
|
-
### Use These
|
|
35
|
-
```css
|
|
36
|
-
/* Standard ease-out: elements arriving */
|
|
37
|
-
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
38
|
-
|
|
39
|
-
/* Standard ease-in: elements departing */
|
|
40
|
-
--ease-in: cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
|
41
|
-
|
|
42
|
-
/* Standard ease-in-out: elements transforming in place */
|
|
43
|
-
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
44
|
-
|
|
45
|
-
/* Spring-like (subtle): natural deceleration */
|
|
46
|
-
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Never Use
|
|
50
|
-
- `linear` for UI animations (looks mechanical)
|
|
51
|
-
- `ease` (the CSS default is mediocre)
|
|
52
|
-
- `bounce` / elastic easing (looks dated and gimmicky)
|
|
53
|
-
- Spring animations with visible oscillation (too playful for most UIs)
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## 3. Duration Guidelines
|
|
58
|
-
|
|
59
|
-
| Type | Duration | Why |
|
|
60
|
-
|---|---|---|
|
|
61
|
-
| Hover state change | 100-150ms | Must feel instant |
|
|
62
|
-
| Button press | 80-120ms | Tactile response |
|
|
63
|
-
| Tooltip appear | 150-200ms | Quick but not jarring |
|
|
64
|
-
| Fade in/out | 150-250ms | Smooth perception |
|
|
65
|
-
| Slide/expand | 200-350ms | Visible movement |
|
|
66
|
-
| Page transition | 300-500ms | Substantial change |
|
|
67
|
-
| Complex choreography | 400-800ms total | Entrance sequence |
|
|
68
|
-
|
|
69
|
-
Rule of thumb: if the user is waiting for it, it should be fast (under 200ms). If the user is watching it, it can be slower (200-500ms).
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## 4. Staggered Reveals
|
|
74
|
-
|
|
75
|
-
The most impactful animation pattern. Elements enter one after another with increasing delay.
|
|
76
|
-
|
|
77
|
-
### CSS-Only Pattern
|
|
78
|
-
```css
|
|
79
|
-
.reveal-item {
|
|
80
|
-
opacity: 0;
|
|
81
|
-
transform: translateY(12px);
|
|
82
|
-
animation: reveal 0.5s var(--ease-out) forwards;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@keyframes reveal {
|
|
86
|
-
to {
|
|
87
|
-
opacity: 1;
|
|
88
|
-
transform: translateY(0);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.reveal-item:nth-child(1) { animation-delay: 0ms; }
|
|
93
|
-
.reveal-item:nth-child(2) { animation-delay: 60ms; }
|
|
94
|
-
.reveal-item:nth-child(3) { animation-delay: 120ms; }
|
|
95
|
-
.reveal-item:nth-child(4) { animation-delay: 180ms; }
|
|
96
|
-
.reveal-item:nth-child(5) { animation-delay: 240ms; }
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Key Parameters
|
|
100
|
-
- **Stagger interval**: 40-80ms between items (shorter for many items, longer for few)
|
|
101
|
-
- **Translate distance**: 8-16px (subtle is better)
|
|
102
|
-
- **Do not stagger more than 6-8 items**. After that, group them.
|
|
103
|
-
|
|
104
|
-
### React with Motion Library
|
|
105
|
-
```jsx
|
|
106
|
-
import { motion } from "framer-motion";
|
|
107
|
-
|
|
108
|
-
const container = {
|
|
109
|
-
hidden: {},
|
|
110
|
-
show: { transition: { staggerChildren: 0.06 } }
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const item = {
|
|
114
|
-
hidden: { opacity: 0, y: 12 },
|
|
115
|
-
show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: [0.16, 1, 0.3, 1] } }
|
|
116
|
-
};
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 5. Scroll-Triggered Animation
|
|
122
|
-
|
|
123
|
-
### CSS-Only with Scroll-Driven Animations
|
|
124
|
-
```css
|
|
125
|
-
@keyframes fade-in {
|
|
126
|
-
from { opacity: 0; transform: translateY(20px); }
|
|
127
|
-
to { opacity: 1; transform: translateY(0); }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.scroll-reveal {
|
|
131
|
-
animation: fade-in linear both;
|
|
132
|
-
animation-timeline: view();
|
|
133
|
-
animation-range: entry 0% entry 30%;
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Intersection Observer Pattern
|
|
138
|
-
```js
|
|
139
|
-
const observer = new IntersectionObserver(
|
|
140
|
-
(entries) => {
|
|
141
|
-
entries.forEach(entry => {
|
|
142
|
-
if (entry.isIntersecting) {
|
|
143
|
-
entry.target.classList.add('visible');
|
|
144
|
-
observer.unobserve(entry.target);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
},
|
|
148
|
-
{ threshold: 0.15, rootMargin: '0px 0px -50px 0px' }
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
document.querySelectorAll('.animate-on-scroll').forEach(el => observer.observe(el));
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## 6. Text Morphing
|
|
157
|
-
|
|
158
|
-
For animated text transitions (changing labels, counters, status updates), use **Torph** (dependency-free).
|
|
159
|
-
|
|
160
|
-
### Installation
|
|
161
|
-
```
|
|
162
|
-
npm i torph
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Usage
|
|
166
|
-
```jsx
|
|
167
|
-
import { TextMorph } from 'torph/react';
|
|
168
|
-
|
|
169
|
-
// Automatically animates between text values
|
|
170
|
-
<TextMorph>{status}</TextMorph>
|
|
171
|
-
|
|
172
|
-
// Works with any dynamic text
|
|
173
|
-
<button>
|
|
174
|
-
<TextMorph>{isLoading ? "Processing..." : `Buy for $${price}`}</TextMorph>
|
|
175
|
-
</button>
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Torph morphs individual characters with smooth enter/exit animations. It works with React, Vue, Svelte, and vanilla TypeScript.
|
|
179
|
-
|
|
180
|
-
### When to Use
|
|
181
|
-
- Tab labels that change on selection
|
|
182
|
-
- Button text that updates (Add to Cart -> Added!)
|
|
183
|
-
- Counter values that increment
|
|
184
|
-
- Status indicators that cycle through states
|
|
185
|
-
- Any text that changes in response to user action
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## 7. Micro-Interactions
|
|
190
|
-
|
|
191
|
-
### Button Press
|
|
192
|
-
```css
|
|
193
|
-
button:active {
|
|
194
|
-
transform: scale(0.97);
|
|
195
|
-
transition: transform 80ms var(--ease-in);
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Toggle Switch
|
|
200
|
-
Animate the knob position and background color simultaneously. The knob should arrive slightly before the color finishes changing.
|
|
201
|
-
|
|
202
|
-
### Checkbox
|
|
203
|
-
Scale the checkmark from 0 to 1 with a slight overshoot:
|
|
204
|
-
```css
|
|
205
|
-
.checkbox-mark {
|
|
206
|
-
transform: scale(0);
|
|
207
|
-
transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
208
|
-
}
|
|
209
|
-
.checkbox:checked .checkbox-mark {
|
|
210
|
-
transform: scale(1);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Skeleton Loading
|
|
215
|
-
Shimmer from left to right using a gradient animation:
|
|
216
|
-
```css
|
|
217
|
-
.skeleton {
|
|
218
|
-
background: linear-gradient(90deg,
|
|
219
|
-
var(--surface-2) 25%,
|
|
220
|
-
var(--surface-3) 50%,
|
|
221
|
-
var(--surface-2) 75%
|
|
222
|
-
);
|
|
223
|
-
background-size: 200% 100%;
|
|
224
|
-
animation: shimmer 1.5s infinite;
|
|
225
|
-
}
|
|
226
|
-
@keyframes shimmer {
|
|
227
|
-
0% { background-position: 200% 0; }
|
|
228
|
-
100% { background-position: -200% 0; }
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## 8. Reduced Motion
|
|
235
|
-
|
|
236
|
-
Always respect the user's motion preference:
|
|
237
|
-
```css
|
|
238
|
-
@media (prefers-reduced-motion: reduce) {
|
|
239
|
-
*, *::before, *::after {
|
|
240
|
-
animation-duration: 0.01ms !important;
|
|
241
|
-
animation-iteration-count: 1 !important;
|
|
242
|
-
transition-duration: 0.01ms !important;
|
|
243
|
-
scroll-behavior: auto !important;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
This does not mean removing all visual feedback. Opacity changes (fades) are still acceptable. Remove translation, scaling, and rotation animations.
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## 9. Common Mistakes
|
|
253
|
-
|
|
254
|
-
- Animating everything on the page (creates visual noise, reduces perceived performance)
|
|
255
|
-
- Using `animation-duration: 0` for reduced motion (some browsers behave unexpectedly; use 0.01ms)
|
|
256
|
-
- Bounce/elastic easing on UI elements (acceptable only in game-like or toy-like contexts)
|
|
257
|
-
- Animating layout properties (width, height, top, left) instead of transforms (causes layout thrashing)
|
|
258
|
-
- Forgetting `will-change` on frequently animated elements (or overusing it on everything)
|
|
259
|
-
- Staggering 20+ items with visible delays (group them or animate the container)
|
|
260
|
-
- Using `transition: all 0.3s` (animates properties you did not intend; be explicit about which properties to transition)
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
# React Patterns Reference
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
1. Component Architecture
|
|
5
|
-
2. State Management
|
|
6
|
-
3. Performance
|
|
7
|
-
4. Composition Patterns
|
|
8
|
-
5. Data Fetching
|
|
9
|
-
6. Styling
|
|
10
|
-
7. Common Mistakes
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 1. Component Architecture
|
|
15
|
-
|
|
16
|
-
### Server vs. Client Components
|
|
17
|
-
Default to Server Components. Add `'use client'` only when the component needs:
|
|
18
|
-
- Event handlers (onClick, onChange, etc.)
|
|
19
|
-
- useState, useEffect, useRef, or other hooks
|
|
20
|
-
- Browser APIs (window, document, navigator)
|
|
21
|
-
- Third-party libraries that use hooks or browser APIs
|
|
22
|
-
|
|
23
|
-
### File Organization
|
|
24
|
-
Colocate related files. Keep components, styles, tests, and types in the same directory:
|
|
25
|
-
```
|
|
26
|
-
components/
|
|
27
|
-
user-card/
|
|
28
|
-
user-card.tsx
|
|
29
|
-
user-card.test.tsx
|
|
30
|
-
user-card.module.css
|
|
31
|
-
types.ts
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Naming
|
|
35
|
-
- Components: PascalCase (`UserCard`, `DashboardHeader`)
|
|
36
|
-
- Files: kebab-case (`user-card.tsx`, `dashboard-header.tsx`)
|
|
37
|
-
- Hooks: camelCase with `use` prefix (`useAuth`, `useMediaQuery`)
|
|
38
|
-
- Event handlers: `handle` + event (`handleClick`, `handleSubmit`)
|
|
39
|
-
- Boolean props: `is`/`has`/`should` prefix (`isLoading`, `hasError`)
|
|
40
|
-
|
|
41
|
-
### Export Patterns
|
|
42
|
-
- **Default export**: page/route components and layout components
|
|
43
|
-
- **Named export**: everything else (utilities, hooks, shared components)
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 2. State Management
|
|
48
|
-
|
|
49
|
-
### Where State Lives
|
|
50
|
-
1. **URL state**: filters, pagination, search queries (use `searchParams`)
|
|
51
|
-
2. **Server state**: data from APIs (use React Query, SWR, or server components)
|
|
52
|
-
3. **Local state**: form inputs, UI toggles, hover/focus state (use `useState`)
|
|
53
|
-
4. **Shared local state**: state needed by siblings (lift to parent, or use context)
|
|
54
|
-
5. **Global state**: rarely needed (auth user, theme preference, feature flags)
|
|
55
|
-
|
|
56
|
-
### Rules
|
|
57
|
-
- Do not store derived state. Compute it during render.
|
|
58
|
-
- Do not sync state between sources. Pick one source of truth.
|
|
59
|
-
- Prefer `useReducer` over `useState` when the next state depends on the previous state or when managing more than 3 related state variables.
|
|
60
|
-
|
|
61
|
-
```tsx
|
|
62
|
-
// Bad: storing derived state
|
|
63
|
-
const [items, setItems] = useState(data);
|
|
64
|
-
const [filteredItems, setFilteredItems] = useState([]);
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
setFilteredItems(items.filter(i => i.active));
|
|
67
|
-
}, [items]);
|
|
68
|
-
|
|
69
|
-
// Good: compute during render
|
|
70
|
-
const [items, setItems] = useState(data);
|
|
71
|
-
const filteredItems = items.filter(i => i.active);
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## 3. Performance
|
|
77
|
-
|
|
78
|
-
### Rendering
|
|
79
|
-
- Use `React.memo` only for components that re-render often with the same props
|
|
80
|
-
- Use `useMemo` for expensive computations, not for every variable
|
|
81
|
-
- Use `useCallback` for callbacks passed to memoized children
|
|
82
|
-
- Use `key` props correctly (stable, unique identifiers, never array indices for reorderable lists)
|
|
83
|
-
|
|
84
|
-
### Code Splitting
|
|
85
|
-
```tsx
|
|
86
|
-
import dynamic from 'next/dynamic';
|
|
87
|
-
|
|
88
|
-
const Chart = dynamic(() => import('./chart'), {
|
|
89
|
-
loading: () => <ChartSkeleton />,
|
|
90
|
-
ssr: false,
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Virtualization
|
|
95
|
-
For lists with 100+ items, use `react-window` or `@tanstack/virtual`:
|
|
96
|
-
```tsx
|
|
97
|
-
import { FixedSizeList } from 'react-window';
|
|
98
|
-
|
|
99
|
-
<FixedSizeList height={600} itemCount={items.length} itemSize={48} width="100%">
|
|
100
|
-
{({ index, style }) => <Row style={style} item={items[index]} />}
|
|
101
|
-
</FixedSizeList>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Image Optimization
|
|
105
|
-
Use `next/image` in Next.js or `loading="lazy"` with explicit `width`/`height` attributes. Always set `aspect-ratio` to prevent layout shift.
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## 4. Composition Patterns
|
|
110
|
-
|
|
111
|
-
### Compound Components
|
|
112
|
-
Components that share implicit state through context:
|
|
113
|
-
```tsx
|
|
114
|
-
<Select value={value} onChange={setValue}>
|
|
115
|
-
<Select.Trigger>Choose a fruit</Select.Trigger>
|
|
116
|
-
<Select.Content>
|
|
117
|
-
<Select.Item value="apple">Apple</Select.Item>
|
|
118
|
-
<Select.Item value="banana">Banana</Select.Item>
|
|
119
|
-
</Select.Content>
|
|
120
|
-
</Select>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Slot Pattern
|
|
124
|
-
Flexible component composition through named children:
|
|
125
|
-
```tsx
|
|
126
|
-
function Card({ header, children, footer }) {
|
|
127
|
-
return (
|
|
128
|
-
<div className="card">
|
|
129
|
-
{header && <div className="card-header">{header}</div>}
|
|
130
|
-
<div className="card-body">{children}</div>
|
|
131
|
-
{footer && <div className="card-footer">{footer}</div>}
|
|
132
|
-
</div>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Render Props (when needed)
|
|
138
|
-
For components that need to share behavior, not UI:
|
|
139
|
-
```tsx
|
|
140
|
-
<DataFetcher url="/api/users">
|
|
141
|
-
{({ data, isLoading }) => isLoading ? <Skeleton /> : <UserList users={data} />}
|
|
142
|
-
</DataFetcher>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## 5. Data Fetching
|
|
148
|
-
|
|
149
|
-
### Server Components (preferred)
|
|
150
|
-
```tsx
|
|
151
|
-
async function UserList() {
|
|
152
|
-
const users = await fetch('/api/users').then(r => r.json());
|
|
153
|
-
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Client-Side with Suspense
|
|
158
|
-
```tsx
|
|
159
|
-
function Dashboard() {
|
|
160
|
-
return (
|
|
161
|
-
<Suspense fallback={<DashboardSkeleton />}>
|
|
162
|
-
<DashboardContent />
|
|
163
|
-
</Suspense>
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Error Boundaries
|
|
169
|
-
Wrap data-fetching components in error boundaries:
|
|
170
|
-
```tsx
|
|
171
|
-
<ErrorBoundary fallback={<ErrorMessage />}>
|
|
172
|
-
<Suspense fallback={<Skeleton />}>
|
|
173
|
-
<DataComponent />
|
|
174
|
-
</Suspense>
|
|
175
|
-
</ErrorBoundary>
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## 6. Styling
|
|
181
|
-
|
|
182
|
-
### Tailwind Best Practices
|
|
183
|
-
- Use Tailwind's core utility classes (pre-defined classes only in Claude artifacts)
|
|
184
|
-
- Extract repeated patterns into component variants, not `@apply` rules
|
|
185
|
-
- Use CSS variables for theme values, Tailwind utilities for everything else
|
|
186
|
-
- Never use more than ~10 utility classes on a single element; extract a component instead
|
|
187
|
-
|
|
188
|
-
### CSS Modules
|
|
189
|
-
For non-Tailwind projects:
|
|
190
|
-
```tsx
|
|
191
|
-
import styles from './button.module.css';
|
|
192
|
-
<button className={styles.primary}>Click</button>
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Semantic HTML
|
|
196
|
-
Use the right element, not just `div` with classes:
|
|
197
|
-
- `<nav>` for navigation
|
|
198
|
-
- `<main>` for primary content
|
|
199
|
-
- `<section>` for thematic grouping
|
|
200
|
-
- `<article>` for self-contained content
|
|
201
|
-
- `<aside>` for tangentially related content
|
|
202
|
-
- `<header>` and `<footer>` for their semantic purpose
|
|
203
|
-
- `<button>` for clickable actions, `<a>` for navigation
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## 7. Common Mistakes
|
|
208
|
-
|
|
209
|
-
- Using `useEffect` for derived state (compute during render instead)
|
|
210
|
-
- Putting everything in global state (most state should be local or server-derived)
|
|
211
|
-
- Using `index` as `key` for dynamic lists
|
|
212
|
-
- Wrapping every component in `React.memo`
|
|
213
|
-
- Using `any` in TypeScript (defeats the purpose of type safety)
|
|
214
|
-
- Fetching data in `useEffect` when a server component would suffice
|
|
215
|
-
- Not using Suspense boundaries (the whole page flashes instead of parts loading independently)
|
|
216
|
-
- Prop drilling through 5+ levels (use composition or context)
|