picasso-skill 1.6.0 → 2.0.1
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 +65 -32
- package/agents/picasso.md +14 -2
- package/commands/backlog.md +34 -0
- package/commands/variants.md +18 -0
- package/package.json +1 -1
- package/references/animation-performance.md +244 -0
- package/references/brand-and-identity.md +136 -0
- package/references/code-typography.md +222 -0
- package/references/dark-mode.md +199 -0
- package/references/i18n-visual-patterns.md +177 -0
- package/references/images-and-media.md +222 -0
- package/references/loading-and-states.md +258 -0
- package/references/micro-interactions.md +291 -0
- package/references/navigation-patterns.md +247 -0
- package/references/tables-and-forms.md +227 -0
- package/skills/picasso/SKILL.md +35 -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/dark-mode.md +199 -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/navigation-patterns.md +247 -0
- package/skills/picasso/references/tables-and-forms.md +227 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Tables and Forms Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. Sortable Tables
|
|
5
|
+
2. Responsive Tables
|
|
6
|
+
3. Inline Editing
|
|
7
|
+
4. Multi-Select Patterns
|
|
8
|
+
5. Form Validation
|
|
9
|
+
6. Multi-Step Forms
|
|
10
|
+
7. Complex Inputs
|
|
11
|
+
8. Common Mistakes
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Sortable Tables
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<table>
|
|
19
|
+
<thead>
|
|
20
|
+
<tr>
|
|
21
|
+
<th aria-sort="ascending">
|
|
22
|
+
<button>Name <span aria-hidden="true">↑</span></button>
|
|
23
|
+
</th>
|
|
24
|
+
<th aria-sort="none">
|
|
25
|
+
<button>Date <span aria-hidden="true">↕</span></button>
|
|
26
|
+
</th>
|
|
27
|
+
</tr>
|
|
28
|
+
</thead>
|
|
29
|
+
</table>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Use `aria-sort` on `<th>`: `ascending`, `descending`, or `none`.
|
|
33
|
+
- Sort icon: `↑` ascending, `↓` descending, `↕` unsorted. Use `aria-hidden="true"` on the icon.
|
|
34
|
+
- Sortable headers must be `<button>` inside `<th>`, not clickable `<th>`.
|
|
35
|
+
- Default sort: most recent first for dates, alphabetical for names.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. Responsive Tables
|
|
40
|
+
|
|
41
|
+
**Option A: Horizontal scroll** (preferred for data tables)
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
.table-container {
|
|
45
|
+
overflow-x: auto;
|
|
46
|
+
-webkit-overflow-scrolling: touch;
|
|
47
|
+
border: 1px solid var(--border);
|
|
48
|
+
border-radius: 8px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Fade edge to hint scrollability */
|
|
52
|
+
.table-container::after {
|
|
53
|
+
content: '';
|
|
54
|
+
position: absolute;
|
|
55
|
+
right: 0;
|
|
56
|
+
top: 0;
|
|
57
|
+
bottom: 0;
|
|
58
|
+
width: 40px;
|
|
59
|
+
background: linear-gradient(to left, var(--surface-1), transparent);
|
|
60
|
+
pointer-events: none;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Option B: Stacked cards** (for simple tables on mobile)
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
@media (max-width: 640px) {
|
|
68
|
+
table, thead, tbody, th, td, tr { display: block; }
|
|
69
|
+
thead { display: none; }
|
|
70
|
+
td { padding: 8px 16px; text-align: right; }
|
|
71
|
+
td::before {
|
|
72
|
+
content: attr(data-label);
|
|
73
|
+
float: left;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
color: var(--text-secondary);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 3. Inline Editing
|
|
83
|
+
|
|
84
|
+
Click to edit pattern: display text, click reveals input, blur/Enter saves.
|
|
85
|
+
|
|
86
|
+
```jsx
|
|
87
|
+
function EditableCell({ value, onSave }) {
|
|
88
|
+
const [editing, setEditing] = useState(false);
|
|
89
|
+
const [draft, setDraft] = useState(value);
|
|
90
|
+
|
|
91
|
+
if (!editing) return (
|
|
92
|
+
<span onClick={() => setEditing(true)} className="cursor-text hover:bg-surface-2 px-2 py-1 rounded">
|
|
93
|
+
{value}
|
|
94
|
+
</span>
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<input
|
|
99
|
+
value={draft}
|
|
100
|
+
onChange={e => setDraft(e.target.value)}
|
|
101
|
+
onBlur={() => { onSave(draft); setEditing(false); }}
|
|
102
|
+
onKeyDown={e => { if (e.key === 'Enter') { onSave(draft); setEditing(false); } if (e.key === 'Escape') setEditing(false); }}
|
|
103
|
+
autoFocus
|
|
104
|
+
className="input-base w-full"
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Show a subtle pencil icon on row hover. Use `opacity-0 group-hover:opacity-100` pattern.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 4. Multi-Select Patterns
|
|
115
|
+
|
|
116
|
+
- **Checkbox column**: leftmost column, always visible.
|
|
117
|
+
- **Shift-click range select**: select from last checked to current.
|
|
118
|
+
- **Select all**: checkbox in header, toggles all visible (filtered) rows.
|
|
119
|
+
- **Bulk action bar**: appears when 1+ rows selected. Shows count + actions.
|
|
120
|
+
|
|
121
|
+
```jsx
|
|
122
|
+
<div className={`fixed bottom-4 left-1/2 -translate-x-1/2 bg-surface-2 rounded-xl px-4 py-2
|
|
123
|
+
flex items-center gap-4 shadow-lg border border-border transition-transform
|
|
124
|
+
${selectedCount > 0 ? 'translate-y-0' : 'translate-y-[200%]'}`}>
|
|
125
|
+
<span className="text-sm font-medium">{selectedCount} selected</span>
|
|
126
|
+
<button className="btn-ghost text-sm">Export</button>
|
|
127
|
+
<button className="btn-ghost text-sm text-red">Delete</button>
|
|
128
|
+
</div>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 5. Form Validation
|
|
134
|
+
|
|
135
|
+
**When to validate:**
|
|
136
|
+
|
|
137
|
+
| Trigger | Use For |
|
|
138
|
+
|---|---|
|
|
139
|
+
| On blur | Email format, required fields, min/max length |
|
|
140
|
+
| On submit | All fields, server-side checks |
|
|
141
|
+
| Real-time (on change) | Password strength, username availability |
|
|
142
|
+
| Never on keystroke | Don't interrupt typing. Wait for blur or 500ms debounce. |
|
|
143
|
+
|
|
144
|
+
```jsx
|
|
145
|
+
<div className="space-y-1.5">
|
|
146
|
+
<label htmlFor="email" className="text-sm font-medium">Email</label>
|
|
147
|
+
<input
|
|
148
|
+
id="email"
|
|
149
|
+
type="email"
|
|
150
|
+
aria-invalid={error ? "true" : undefined}
|
|
151
|
+
aria-describedby={error ? "email-error" : undefined}
|
|
152
|
+
className={`input-base ${error ? 'border-red' : ''}`}
|
|
153
|
+
/>
|
|
154
|
+
{error && (
|
|
155
|
+
<p id="email-error" role="alert" className="text-xs text-red flex items-center gap-1">
|
|
156
|
+
<svg className="w-3.5 h-3.5" aria-hidden="true">...</svg>
|
|
157
|
+
{error}
|
|
158
|
+
</p>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Error messages: specific and helpful. "Enter a valid email" not "Invalid input."
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 6. Multi-Step Forms
|
|
168
|
+
|
|
169
|
+
Show a progress indicator. Allow back navigation. Validate per step, not all at once.
|
|
170
|
+
|
|
171
|
+
```jsx
|
|
172
|
+
<div className="flex items-center gap-2 mb-8">
|
|
173
|
+
{steps.map((step, i) => (
|
|
174
|
+
<Fragment key={i}>
|
|
175
|
+
<div className={`flex items-center justify-center h-8 w-8 rounded-full text-sm font-bold
|
|
176
|
+
${i < currentStep ? 'bg-accent text-white' :
|
|
177
|
+
i === currentStep ? 'border-2 border-accent text-accent' :
|
|
178
|
+
'border border-border text-muted'}`}>
|
|
179
|
+
{i < currentStep ? '✓' : i + 1}
|
|
180
|
+
</div>
|
|
181
|
+
{i < steps.length - 1 && (
|
|
182
|
+
<div className={`flex-1 h-0.5 ${i < currentStep ? 'bg-accent' : 'bg-border'}`} />
|
|
183
|
+
)}
|
|
184
|
+
</Fragment>
|
|
185
|
+
))}
|
|
186
|
+
</div>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Rules:
|
|
190
|
+
- Save progress per step (don't lose data on back).
|
|
191
|
+
- Validate step before advancing (disable Next if invalid).
|
|
192
|
+
- Show step count: "Step 2 of 4".
|
|
193
|
+
- Allow clicking completed steps to go back.
|
|
194
|
+
- Final step: show summary before submit.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 7. Complex Inputs
|
|
199
|
+
|
|
200
|
+
**Date pickers:** Use native `<input type="date">` first. Custom picker only if design requires it. Always allow manual text entry as fallback.
|
|
201
|
+
|
|
202
|
+
**File upload:** Show progress, preview (for images), allow removal.
|
|
203
|
+
|
|
204
|
+
```jsx
|
|
205
|
+
<label className="flex flex-col items-center gap-2 p-8 border-2 border-dashed border-border
|
|
206
|
+
rounded-xl cursor-pointer hover:border-accent hover:bg-accent/5 transition-colors">
|
|
207
|
+
<svg>...</svg>
|
|
208
|
+
<span className="text-sm text-secondary">Drop files or click to upload</span>
|
|
209
|
+
<input type="file" className="hidden" onChange={handleUpload} />
|
|
210
|
+
</label>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Address autocomplete:** Use Google Places API or similar. Show suggestions in a dropdown. Parse into structured fields (street, city, state, zip).
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 8. Common Mistakes
|
|
218
|
+
|
|
219
|
+
- **Sortable `<th>` without `<button>`.** Clicking a `<th>` isn't keyboard accessible.
|
|
220
|
+
- **No `aria-sort` on sortable columns.** Screen readers can't announce sort state.
|
|
221
|
+
- **Validating on every keystroke.** Annoying. Use blur or 500ms debounce.
|
|
222
|
+
- **Error messages without `role="alert"`.** Screen readers won't announce them.
|
|
223
|
+
- **Multi-step form losing data on back.** Save each step's state.
|
|
224
|
+
- **No horizontal scroll hint on tables.** Users don't know content is hidden. Add fade gradient.
|
|
225
|
+
- **Custom date picker without text input fallback.** Some users prefer typing dates.
|
|
226
|
+
- **Select all selecting ALL rows, not just filtered.** Only select what's visible.
|
|
227
|
+
- **Labels as placeholder text.** Labels must be visible above the input, always.
|
package/skills/picasso/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: picasso
|
|
3
|
-
version:
|
|
3
|
+
version: 2.0.0
|
|
4
4
|
description: >
|
|
5
5
|
The ultimate frontend design and UI engineering skill. Use this whenever the user asks to build, design, style, or improve any web interface, component, page, application, dashboard, landing page, artifact, poster, or visual output. Covers typography, color systems, spatial design, motion/animation, interaction design, responsive layouts, sound design, haptic feedback, icon systems, generative art, theming, React best practices, and DESIGN.md system generation. Also use when the user asks to audit, critique, polish, simplify, animate, or normalize a frontend. Triggers on any mention of "make it look good," "fix the design," "UI," "UX," "frontend," "component," "landing page," "dashboard," "artifact," "poster," "design system," "theme," "animation," "responsive," or any request to improve visual quality. Use this skill even when the user does not explicitly ask for design help but the task involves producing a visual interface.
|
|
6
6
|
metadata:
|
|
@@ -31,6 +31,20 @@ When the user says "make it premium" or "luxury feel," drop VISUAL_DENSITY to 2-
|
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
34
|
+
## Quick Start (30 seconds)
|
|
35
|
+
|
|
36
|
+
New to Picasso? Here's the minimum viable workflow:
|
|
37
|
+
|
|
38
|
+
1. **Pick a font** that isn't Inter, Roboto, or Arial. Try: Satoshi, Cabinet Grotesk, Outfit, General Sans, Clash Display.
|
|
39
|
+
2. **Pick a color** in OKLCH. Not Tailwind's default indigo. Try: `oklch(0.65 0.25 25)` (warm red), `oklch(0.55 0.20 160)` (teal), `oklch(0.60 0.22 300)` (violet).
|
|
40
|
+
3. **Tint your grays** toward your accent hue. Never use pure `#808080` or `#000`.
|
|
41
|
+
4. **Break the center.** Left-align content. Use asymmetric grids (2:1, 3:2). Only center heroes and CTAs.
|
|
42
|
+
5. **Read `references/anti-patterns.md`** before writing any code. It's the most important file.
|
|
43
|
+
|
|
44
|
+
Then follow the full workflow below. Skip nothing.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
34
48
|
## Step 0: Read the Right References
|
|
35
49
|
|
|
36
50
|
Before writing any code, read the reference files relevant to the task. Each covers a domain in depth with rules, examples, and anti-patterns. Load only what you need.
|
|
@@ -50,6 +64,25 @@ Before writing any code, read the reference files relevant to the task. Each cov
|
|
|
50
64
|
| `references/design-system.md` | Generating DESIGN.md files, theming, systematic tokens |
|
|
51
65
|
| `references/generative-art.md` | Algorithmic art, p5.js, seeded randomness, flow fields |
|
|
52
66
|
| `references/component-patterns.md` | Standard component naming, taxonomy, and state patterns |
|
|
67
|
+
| `references/navigation-patterns.md` | Breadcrumbs, sidebar, tabs, bottom bar, mega menus, skip links |
|
|
68
|
+
| `references/tables-and-forms.md` | Sortable tables, inline editing, multi-step forms, validation |
|
|
69
|
+
| `references/loading-and-states.md` | Skeletons, spinners, empty states, error boundaries, offline |
|
|
70
|
+
| `references/dark-mode.md` | Preference hierarchy, surface elevation, transitions, testing |
|
|
71
|
+
| `references/images-and-media.md` | Format selection, responsive images, favicons, OG images |
|
|
72
|
+
| `references/micro-interactions.md` | Scroll animations, page transitions, gestures, magnetic effects |
|
|
73
|
+
| `references/i18n-visual-patterns.md` | RTL, logical properties, text expansion, CJK, number formatting |
|
|
74
|
+
| `references/brand-and-identity.md` | Logo sizing, brand color usage, consistency, lockup variants |
|
|
75
|
+
| `references/animation-performance.md` | Compositor-only props, will-change, layout thrashing, contain |
|
|
76
|
+
| `references/code-typography.md` | Monospace fonts, syntax highlighting, code blocks, diff views |
|
|
77
|
+
| `references/accessibility-wcag.md` | WCAG 2.2, ARIA patterns, keyboard nav, screen reader testing |
|
|
78
|
+
| `references/conversion-design.md` | Landing pages, CTAs, pricing tables, friction reduction |
|
|
79
|
+
| `references/data-visualization.md` | Chart selection matrix, dashboard patterns, accessible charts |
|
|
80
|
+
| `references/modern-css-performance.md` | CSS nesting, :has(), View Transitions, Tailwind v4, container queries |
|
|
81
|
+
| `references/performance-optimization.md` | Core Web Vitals, Lighthouse, image optimization, 45 Vercel rules |
|
|
82
|
+
| `references/style-presets.md` | 22 curated design presets with exact OKLCH values and font pairings |
|
|
83
|
+
| `references/tools-catalog.md` | Tool recommendations: torph, soundcn, Lucide, Facehash, better-icons |
|
|
84
|
+
| `references/ux-psychology.md` | Gestalt principles, Fitts's Law, Hick's Law, cognitive load, heuristics |
|
|
85
|
+
| `references/ux-writing.md` | Error messages, microcopy, terminology, voice and tone, CTAs |
|
|
53
86
|
|
|
54
87
|
---
|
|
55
88
|
|
|
@@ -73,7 +106,7 @@ This step is non-negotiable. It takes 30 seconds and prevents hours of rework.
|
|
|
73
106
|
- Same spacing everywhere
|
|
74
107
|
- Three equal-width items in a row
|
|
75
108
|
|
|
76
|
-
If you
|
|
109
|
+
**HALT CONDITION:** If you cannot fill out the commitments above with specific, non-default values, you MUST NOT proceed to Step 1. Go back to the references. Read anti-patterns.md. Try again. There is no "just do it" bypass for this step. The gate exists because without it, every output converges to the same generic AI aesthetic. This is not optional. No code until commitments are written.
|
|
77
110
|
|
|
78
111
|
---
|
|
79
112
|
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Animation Performance Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. Compositor-Only Properties
|
|
5
|
+
2. Will-Change Best Practices
|
|
6
|
+
3. Layout Thrashing
|
|
7
|
+
4. IntersectionObserver vs Scroll Events
|
|
8
|
+
5. Web Animations API
|
|
9
|
+
6. Performance Measurement
|
|
10
|
+
7. Testing on Low-End Devices
|
|
11
|
+
8. Contain Property
|
|
12
|
+
9. Common Mistakes
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Compositor-Only Properties
|
|
17
|
+
|
|
18
|
+
Only two CSS properties can be animated without triggering layout or paint: **transform** and **opacity**. Everything else causes reflow.
|
|
19
|
+
|
|
20
|
+
| Property | Layout | Paint | Composite | Animate? |
|
|
21
|
+
|---|---|---|---|---|
|
|
22
|
+
| `transform` | No | No | Yes | **Yes** |
|
|
23
|
+
| `opacity` | No | No | Yes | **Yes** |
|
|
24
|
+
| `filter` | No | Yes | Yes | Carefully |
|
|
25
|
+
| `background-color` | No | Yes | No | Avoid |
|
|
26
|
+
| `width`, `height` | Yes | Yes | No | **Never** |
|
|
27
|
+
| `top`, `left` | Yes | Yes | No | **Never** |
|
|
28
|
+
| `margin`, `padding` | Yes | Yes | No | **Never** |
|
|
29
|
+
| `border-radius` | No | Yes | No | Avoid |
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
/* Good: compositor-only */
|
|
33
|
+
.slide-in {
|
|
34
|
+
transform: translateX(-100%);
|
|
35
|
+
opacity: 0;
|
|
36
|
+
transition: transform 300ms var(--ease-out), opacity 300ms var(--ease-out);
|
|
37
|
+
}
|
|
38
|
+
.slide-in.active {
|
|
39
|
+
transform: translateX(0);
|
|
40
|
+
opacity: 1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Bad: triggers layout on every frame */
|
|
44
|
+
.slide-in-bad {
|
|
45
|
+
left: -100%;
|
|
46
|
+
transition: left 300ms ease;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. Will-Change Best Practices
|
|
53
|
+
|
|
54
|
+
`will-change` promotes an element to its own compositor layer. This speeds up animation but consumes GPU memory.
|
|
55
|
+
|
|
56
|
+
Rules:
|
|
57
|
+
- Add `will-change` BEFORE the animation starts (e.g., on hover, not in the animation itself).
|
|
58
|
+
- Remove it AFTER the animation completes.
|
|
59
|
+
- Never use `will-change: all` — it promotes everything.
|
|
60
|
+
- Never apply it to more than 10 elements simultaneously.
|
|
61
|
+
- Don't put it in your stylesheet permanently.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
// Good: apply before, remove after
|
|
65
|
+
element.addEventListener('mouseenter', () => {
|
|
66
|
+
element.style.willChange = 'transform';
|
|
67
|
+
});
|
|
68
|
+
element.addEventListener('transitionend', () => {
|
|
69
|
+
element.style.willChange = 'auto';
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
/* Acceptable: for elements that are ALWAYS animated (e.g., loading spinners) */
|
|
75
|
+
.spinner { will-change: transform; }
|
|
76
|
+
|
|
77
|
+
/* Bad: permanent will-change on static elements */
|
|
78
|
+
.card { will-change: transform, opacity; } /* don't do this */
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Layout Thrashing
|
|
84
|
+
|
|
85
|
+
Reading layout properties then writing them in a loop forces the browser to recalculate layout on every iteration.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
// BAD: layout thrashing (read-write-read-write)
|
|
89
|
+
elements.forEach(el => {
|
|
90
|
+
const height = el.offsetHeight; // READ — forces layout
|
|
91
|
+
el.style.height = height + 10 + 'px'; // WRITE — invalidates layout
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// GOOD: batch reads, then batch writes
|
|
95
|
+
const heights = elements.map(el => el.offsetHeight); // all reads first
|
|
96
|
+
elements.forEach((el, i) => {
|
|
97
|
+
el.style.height = heights[i] + 10 + 'px'; // all writes after
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Properties that trigger forced layout when read: `offsetHeight`, `offsetWidth`, `getBoundingClientRect()`, `scrollTop`, `clientHeight`, `getComputedStyle()`.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 4. IntersectionObserver vs Scroll Events
|
|
106
|
+
|
|
107
|
+
| | `scroll` event | IntersectionObserver |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| Performance | Fires every pixel, blocks main thread | Async, fires on threshold crossing |
|
|
110
|
+
| Throttling | Manual (requestAnimationFrame) | Built-in |
|
|
111
|
+
| Use case | Scroll-linked animation (position) | Visibility detection (enter/exit) |
|
|
112
|
+
| Recommendation | Almost never | Almost always |
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
// Good: IntersectionObserver for reveal animations
|
|
116
|
+
const observer = new IntersectionObserver(
|
|
117
|
+
(entries) => {
|
|
118
|
+
entries.forEach(entry => {
|
|
119
|
+
entry.target.classList.toggle('visible', entry.isIntersecting);
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
{ threshold: 0.1 }
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
For scroll-linked animations where you need exact scroll position, use the Scroll-Driven Animations API:
|
|
127
|
+
|
|
128
|
+
```css
|
|
129
|
+
@keyframes fade-in {
|
|
130
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
131
|
+
to { opacity: 1; transform: translateY(0); }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.scroll-reveal {
|
|
135
|
+
animation: fade-in linear;
|
|
136
|
+
animation-timeline: view();
|
|
137
|
+
animation-range: entry 0% entry 100%;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 5. Web Animations API
|
|
144
|
+
|
|
145
|
+
For complex JS-driven animations, use WAAPI instead of manual `requestAnimationFrame`:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
element.animate(
|
|
149
|
+
[
|
|
150
|
+
{ transform: 'translateY(20px)', opacity: 0 },
|
|
151
|
+
{ transform: 'translateY(0)', opacity: 1 }
|
|
152
|
+
],
|
|
153
|
+
{
|
|
154
|
+
duration: 300,
|
|
155
|
+
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
156
|
+
fill: 'forwards'
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Benefits over CSS: programmable, cancellable, can read progress, can reverse.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 6. Performance Measurement
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
// Measure animation frame rate
|
|
169
|
+
let frames = 0;
|
|
170
|
+
let lastTime = performance.now();
|
|
171
|
+
|
|
172
|
+
function countFrames() {
|
|
173
|
+
frames++;
|
|
174
|
+
const now = performance.now();
|
|
175
|
+
if (now - lastTime >= 1000) {
|
|
176
|
+
console.log(`FPS: ${frames}`);
|
|
177
|
+
frames = 0;
|
|
178
|
+
lastTime = now;
|
|
179
|
+
}
|
|
180
|
+
requestAnimationFrame(countFrames);
|
|
181
|
+
}
|
|
182
|
+
requestAnimationFrame(countFrames);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Chrome DevTools:
|
|
186
|
+
1. Performance tab → Record → interact with animations → Stop
|
|
187
|
+
2. Look for long frames (> 16ms) in the flame chart
|
|
188
|
+
3. Rendering tab → Paint flashing (green = repaint, should be minimal)
|
|
189
|
+
4. Rendering tab → Layer borders (orange = composited layers)
|
|
190
|
+
|
|
191
|
+
Target: 60fps = 16.67ms per frame. If ANY frame takes > 33ms, users perceive jank.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 7. Testing on Low-End Devices
|
|
196
|
+
|
|
197
|
+
Your M-series Mac is not representative. Test with:
|
|
198
|
+
- **Chrome DevTools CPU throttling:** Performance tab → CPU → 6x slowdown
|
|
199
|
+
- **Network throttling:** Slow 3G preset to test loading animations
|
|
200
|
+
- **Real device:** Test on a 3-year-old Android phone if possible
|
|
201
|
+
|
|
202
|
+
If an animation stutters at 6x CPU throttle, reduce:
|
|
203
|
+
1. Number of concurrent animations
|
|
204
|
+
2. Element count being animated
|
|
205
|
+
3. Complexity of each animation
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 8. Contain Property
|
|
210
|
+
|
|
211
|
+
`contain` tells the browser what NOT to recalculate when an element changes.
|
|
212
|
+
|
|
213
|
+
```css
|
|
214
|
+
/* Isolate layout/paint to this element */
|
|
215
|
+
.card {
|
|
216
|
+
contain: layout paint;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Full isolation — best for off-screen or independent components */
|
|
220
|
+
.widget {
|
|
221
|
+
contain: strict; /* = size + layout + paint + style */
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Content containment — layout + paint + style (most common) */
|
|
225
|
+
.list-item {
|
|
226
|
+
contain: content;
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Use `contain: content` on repeated elements (list items, cards) to prevent layout changes from propagating to siblings.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 9. Common Mistakes
|
|
235
|
+
|
|
236
|
+
- **Animating `width`/`height`/`top`/`left`.** Use `transform: translate/scale` instead.
|
|
237
|
+
- **`will-change` on everything.** Max 10 elements. Remove after animation completes.
|
|
238
|
+
- **`addEventListener('scroll')` for visibility.** Use IntersectionObserver.
|
|
239
|
+
- **Reading layout properties in animation loops.** Batch reads before writes.
|
|
240
|
+
- **Testing only on fast hardware.** Use Chrome CPU throttling at 6x.
|
|
241
|
+
- **No frame budget awareness.** 16ms per frame. If your JS takes 20ms, you drop frames.
|
|
242
|
+
- **CSS `transition: all`.** Transitions every property including layout triggers.
|
|
243
|
+
- **Forgetting `contain` on repeated elements.** One card's layout change recalculates the entire list.
|
|
244
|
+
- **`requestAnimationFrame` without cancellation.** Always store the ID and `cancelAnimationFrame` on cleanup.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Brand and Identity Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. Logo Sizing Rules
|
|
5
|
+
2. Logo Placement
|
|
6
|
+
3. Logo Lockup Variants
|
|
7
|
+
4. Brand Color Usage
|
|
8
|
+
5. Brand Typography
|
|
9
|
+
6. Consistency Across Pages
|
|
10
|
+
7. When to Bend Brand Rules
|
|
11
|
+
8. Common Mistakes
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Logo Sizing Rules
|
|
16
|
+
|
|
17
|
+
- **Minimum size:** 24px height for icon-only, 32px height for full logo. Below this it's illegible.
|
|
18
|
+
- **Clear space:** Minimum clear space around logo = the height of the logo's icon element. No text or elements may intrude.
|
|
19
|
+
- **Typical sizes by context:**
|
|
20
|
+
|
|
21
|
+
| Context | Height |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Favicon | 32px |
|
|
24
|
+
| Mobile header | 28-32px |
|
|
25
|
+
| Desktop header | 32-40px |
|
|
26
|
+
| Footer | 24-28px |
|
|
27
|
+
| Hero/marketing | 48-64px |
|
|
28
|
+
| App splash | 80-120px |
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
.logo { height: 32px; width: auto; } /* maintain aspect ratio */
|
|
32
|
+
.logo-icon { height: 28px; width: 28px; }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Logo Placement
|
|
38
|
+
|
|
39
|
+
- **Header:** top-left for LTR, top-right for RTL. Always links to homepage.
|
|
40
|
+
- **Footer:** bottom-left or bottom-center. Smaller than header.
|
|
41
|
+
- **Marketing pages:** centered for hero sections, left-aligned for navigation.
|
|
42
|
+
- **Loading/splash:** centered vertically and horizontally.
|
|
43
|
+
|
|
44
|
+
The header logo is the most important. It should be the first visual element the user sees, but not dominate the page. Keep it slim.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Logo Lockup Variants
|
|
49
|
+
|
|
50
|
+
Every brand needs at least 3 logo variants:
|
|
51
|
+
|
|
52
|
+
1. **Full lockup:** icon + wordmark (for headers, marketing)
|
|
53
|
+
2. **Icon only:** for favicons, app icons, small spaces, mobile headers
|
|
54
|
+
3. **Wordmark only:** for editorial/text-heavy contexts
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
// Responsive logo: icon on mobile, full on desktop
|
|
58
|
+
<Link href="/" className="flex items-center gap-2">
|
|
59
|
+
<img src="/logo-icon.svg" alt="" className="h-7 w-7" />
|
|
60
|
+
<span className="hidden sm:inline text-sm font-bold tracking-tight">Brand Name</span>
|
|
61
|
+
</Link>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 4. Brand Color Usage
|
|
67
|
+
|
|
68
|
+
Follow the 60-30-10 rule with brand colors:
|
|
69
|
+
- **60%** — neutral surfaces (backgrounds, cards). Brand color should NOT be the 60%.
|
|
70
|
+
- **30%** — supporting colors (borders, secondary text, section backgrounds).
|
|
71
|
+
- **10%** — brand/accent color (CTAs, active states, highlights). This is where brand color goes.
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
:root {
|
|
75
|
+
--brand: oklch(0.55 0.25 250); /* Primary brand color — use at 10% */
|
|
76
|
+
--brand-subtle: oklch(0.95 0.03 250); /* Tinted background — use sparingly */
|
|
77
|
+
--brand-on-dark: oklch(0.70 0.18 250); /* Lighter variant for dark backgrounds */
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Never use brand color as background for large areas (hero sections, full-width bars) unless the brand is specifically known for it (like Spotify's green). Large saturated surfaces are fatiguing.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 5. Brand Typography
|
|
86
|
+
|
|
87
|
+
If the brand has a custom/licensed font, use it for headings only. Body text should be a readable, web-optimized font.
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
/* Brand font for headings */
|
|
91
|
+
h1, h2, h3 { font-family: 'Brand Display', var(--font-body); }
|
|
92
|
+
|
|
93
|
+
/* Readable font for body */
|
|
94
|
+
body { font-family: 'DM Sans', system-ui, sans-serif; }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
If the brand font isn't available for web, find the closest web-safe alternative:
|
|
98
|
+
- Futura → use Outfit or Jost
|
|
99
|
+
- Helvetica Neue → use Inter (only acceptable here) or Geist
|
|
100
|
+
- Garamond → use EB Garamond or Cormorant
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 6. Consistency Across Pages
|
|
105
|
+
|
|
106
|
+
Every page should share:
|
|
107
|
+
- Same header and footer (identical, not "similar")
|
|
108
|
+
- Same color palette (no page-specific colors)
|
|
109
|
+
- Same typography scale
|
|
110
|
+
- Same border-radius philosophy
|
|
111
|
+
- Same spacing scale
|
|
112
|
+
|
|
113
|
+
Variation should come from layout and content, not from inconsistent styling. A dashboard page and a marketing page can look different through layout while sharing the same design tokens.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 7. When to Bend Brand Rules
|
|
118
|
+
|
|
119
|
+
Strict brand adherence isn't always right:
|
|
120
|
+
- **Data-dense dashboards:** Brand color as accent only. Don't fight with data colors.
|
|
121
|
+
- **Error states:** Use standard red for errors, not brand color. Users need instant recognition.
|
|
122
|
+
- **Third-party embeds:** Payment forms, maps, chat widgets have their own styling. Don't fight it.
|
|
123
|
+
- **Dark mode:** Brand color may need lightness/chroma adjustment to maintain contrast.
|
|
124
|
+
- **Accessibility:** If brand colors don't meet WCAG contrast, accessibility wins.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 8. Common Mistakes
|
|
129
|
+
|
|
130
|
+
- **Logo too small in the header.** Minimum 28px height. Users need to identify the brand instantly.
|
|
131
|
+
- **Brand color as full-width background.** Fatiguing. Use at 10% ratio maximum.
|
|
132
|
+
- **Different fonts on every page.** Pick 2 fonts and use them everywhere.
|
|
133
|
+
- **Logo without clear space.** Crowded logos look unprofessional. Enforce minimum padding.
|
|
134
|
+
- **No icon-only variant.** Mobile needs a compact logo. Don't just shrink the full lockup.
|
|
135
|
+
- **Brand color unchanged in dark mode.** Adjust lightness and chroma for dark backgrounds.
|
|
136
|
+
- **Inconsistent border radius between pages.** One sharp page and one rounded page = two brands.
|