picasso-skill 1.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.
@@ -0,0 +1,260 @@
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)
@@ -0,0 +1,216 @@
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)
@@ -0,0 +1,118 @@
1
+ # Responsive Design Reference
2
+
3
+ ## 1. Breakpoints
4
+
5
+ Use content-driven breakpoints, not device-driven. These are sensible defaults:
6
+
7
+ ```css
8
+ /* Mobile first: no media query = mobile */
9
+ /* Small tablets / large phones */
10
+ @media (min-width: 640px) { }
11
+ /* Tablets / small laptops */
12
+ @media (min-width: 768px) { }
13
+ /* Laptops / desktops */
14
+ @media (min-width: 1024px) { }
15
+ /* Large desktops */
16
+ @media (min-width: 1280px) { }
17
+ /* Ultrawide */
18
+ @media (min-width: 1536px) { }
19
+ ```
20
+
21
+ ## 2. Mobile-First Approach
22
+
23
+ Start with the mobile layout (single column, stacked). Add complexity at wider breakpoints. This ensures the core experience works everywhere before enhancements.
24
+
25
+ ```css
26
+ /* Base: single column */
27
+ .grid { display: grid; gap: 1rem; }
28
+
29
+ /* Tablet: two columns */
30
+ @media (min-width: 768px) {
31
+ .grid { grid-template-columns: repeat(2, 1fr); }
32
+ }
33
+
34
+ /* Desktop: three columns */
35
+ @media (min-width: 1024px) {
36
+ .grid { grid-template-columns: repeat(3, 1fr); }
37
+ }
38
+ ```
39
+
40
+ ## 3. Fluid Design
41
+
42
+ Use `clamp()` for font sizes, padding, and gaps that scale smoothly:
43
+
44
+ ```css
45
+ .container {
46
+ padding: clamp(1rem, 4vw, 3rem);
47
+ max-width: 1200px;
48
+ margin: 0 auto;
49
+ }
50
+ h1 {
51
+ font-size: clamp(1.75rem, 4vw + 0.5rem, 3.5rem);
52
+ }
53
+ ```
54
+
55
+ ## 4. Container Queries
56
+
57
+ For component-level responsiveness (when the component's width, not the viewport, should determine layout):
58
+
59
+ ```css
60
+ .card-container {
61
+ container-type: inline-size;
62
+ }
63
+ @container (min-width: 400px) {
64
+ .card { display: grid; grid-template-columns: 1fr 2fr; }
65
+ }
66
+ ```
67
+
68
+ ## 5. Touch Targets
69
+
70
+ Minimum touch target: 44x44px (WCAG) or 48x48px (Material). Apply to all interactive elements on mobile. If the visual element is smaller, use padding or `::before` pseudo-element to extend the hit area.
71
+
72
+ ```css
73
+ .icon-button {
74
+ position: relative;
75
+ width: 24px;
76
+ height: 24px;
77
+ }
78
+ .icon-button::before {
79
+ content: '';
80
+ position: absolute;
81
+ inset: -12px; /* extends tap area to 48x48 */
82
+ }
83
+ ```
84
+
85
+ ## 6. Responsive Typography
86
+
87
+ Do not just shrink everything on mobile. Adjust the type scale:
88
+ - Mobile: use a smaller ratio (1.2) with a 15-16px base
89
+ - Desktop: use a larger ratio (1.25-1.333) with a 16-18px base
90
+ - Headings should scale more aggressively than body text
91
+
92
+ ## 7. Navigation Patterns
93
+
94
+ - **Mobile (< 768px)**: Hamburger menu, bottom tab bar, or slide-out drawer
95
+ - **Tablet (768-1024px)**: Collapsed sidebar or icon-only navigation
96
+ - **Desktop (> 1024px)**: Full sidebar, top navigation bar, or mega-menu
97
+
98
+ ## 8. Images
99
+
100
+ Use `srcset` and `sizes` for responsive images. Use `loading="lazy"` for below-the-fold images. Set `aspect-ratio` to prevent layout shift:
101
+
102
+ ```css
103
+ img {
104
+ width: 100%;
105
+ height: auto;
106
+ aspect-ratio: 16 / 9;
107
+ object-fit: cover;
108
+ }
109
+ ```
110
+
111
+ ## 9. Common Mistakes
112
+
113
+ - Hiding too much content on mobile (users expect the same information, just reorganized)
114
+ - Using `vw` units for text without a `clamp()` minimum (becomes unreadable on small screens)
115
+ - Horizontal scrolling on mobile (almost never acceptable)
116
+ - Fixed-position elements that cover too much of the mobile viewport
117
+ - Not testing at actual device widths (375px for iPhone SE, 390px for modern iPhones, 360px for Android)
118
+ - Tables that overflow on mobile (use horizontal scroll wrapper or reformat as stacked cards)
@@ -0,0 +1,125 @@
1
+ # Sensory Design Reference
2
+
3
+ ## Table of Contents
4
+ 1. UI Sound Design
5
+ 2. Haptic Feedback
6
+ 3. Multi-Sensory Integration
7
+
8
+ ---
9
+
10
+ ## 1. UI Sound Design
11
+
12
+ ### Why Sound
13
+ Sound provides confirmation that an action occurred, draws attention to important state changes, and adds personality to the interface. It is underused on the web but highly effective when applied with restraint.
14
+
15
+ ### The Soundcn Pattern
16
+ Use inline base64 audio data URIs with the Web Audio API. This avoids external file loading, CORS issues, and runtime fetching. Each sound is a self-contained TypeScript module.
17
+
18
+ ```typescript
19
+ // sounds/click-soft.ts
20
+ export const clickSoftSound = "data:audio/wav;base64,UklGR..."; // base64 WAV
21
+
22
+ // hooks/use-sound.ts
23
+ import { useCallback, useRef } from 'react';
24
+
25
+ export function useSound(src: string) {
26
+ const audioContextRef = useRef<AudioContext | null>(null);
27
+
28
+ const play = useCallback(() => {
29
+ if (!audioContextRef.current) {
30
+ audioContextRef.current = new AudioContext();
31
+ }
32
+ const ctx = audioContextRef.current;
33
+ fetch(src)
34
+ .then(r => r.arrayBuffer())
35
+ .then(buf => ctx.decodeAudioData(buf))
36
+ .then(decoded => {
37
+ const source = ctx.createBufferSource();
38
+ source.buffer = decoded;
39
+ source.connect(ctx.destination);
40
+ source.start(0);
41
+ });
42
+ }, [src]);
43
+
44
+ return [play] as const;
45
+ }
46
+ ```
47
+
48
+ ### Usage
49
+ ```tsx
50
+ import { useSound } from "@/hooks/use-sound";
51
+ import { clickSoftSound } from "@/sounds/click-soft";
52
+
53
+ function Button() {
54
+ const [play] = useSound(clickSoftSound);
55
+ return <button onClick={() => { play(); handleAction(); }}>Save</button>;
56
+ }
57
+ ```
58
+
59
+ ### When to Use Sound
60
+ - **Button clicks**: soft, short click sound (50-100ms)
61
+ - **Success actions**: pleasant confirmation tone
62
+ - **Notifications**: attention-getting but not alarming chime
63
+ - **Errors**: subtle alert, not a harsh buzz
64
+ - **Toggle switches**: satisfying mechanical click
65
+ - **Transitions**: whoosh or swipe sound for page changes
66
+
67
+ ### Rules
68
+ - Always provide a sound toggle in the UI (respect user preference)
69
+ - Keep sounds under 200ms for UI interactions
70
+ - Use the Web Audio API, not `<audio>` elements (lower latency)
71
+ - Sound volume should be subtle by default (0.3-0.5 of max)
72
+ - Never auto-play sounds on page load
73
+ - Source sounds from CC0 collections (Kenney, Freesound)
74
+
75
+ ---
76
+
77
+ ## 2. Haptic Feedback
78
+
79
+ ### The Vibration API
80
+ ```javascript
81
+ // Check support
82
+ if ('vibrate' in navigator) {
83
+ // Simple tap (10ms pulse)
84
+ navigator.vibrate(10);
85
+
86
+ // Success pattern (two short pulses)
87
+ navigator.vibrate([10, 50, 10]);
88
+
89
+ // Error pattern (one longer pulse)
90
+ navigator.vibrate(30);
91
+
92
+ // Warning (three quick pulses)
93
+ navigator.vibrate([10, 30, 10, 30, 10]);
94
+ }
95
+ ```
96
+
97
+ ### When to Use Haptics
98
+ - **Button press confirmation**: 10ms pulse on touch
99
+ - **Toggle switch**: 10ms pulse on state change
100
+ - **Destructive action confirmation**: 30ms pulse before confirmation dialog
101
+ - **Pull-to-refresh threshold**: 10ms pulse when the threshold is reached
102
+ - **Drag and drop**: 10ms pulse on pickup and drop
103
+
104
+ ### Rules
105
+ - Gate behind feature detection (`'vibrate' in navigator`)
106
+ - Respect `prefers-reduced-motion` by disabling haptics when motion is reduced
107
+ - Keep durations very short (10-30ms for taps, never longer than 100ms)
108
+ - Do not use haptics for every interaction, only pivotal moments
109
+ - Mobile only: haptics have no effect on desktop
110
+
111
+ ### iOS Considerations
112
+ The Vibration API has limited support on iOS Safari. For broader iOS support, use the experimental Haptic Feedback API or accept that haptics are Android-primary.
113
+
114
+ ---
115
+
116
+ ## 3. Multi-Sensory Integration
117
+
118
+ The strongest UI moments combine visual, auditory, and haptic feedback simultaneously:
119
+
120
+ 1. User taps "Complete" button
121
+ 2. **Visual**: checkmark animation scales in with a satisfying bounce
122
+ 3. **Sound**: soft success chime (100ms)
123
+ 4. **Haptic**: double-pulse pattern (10ms, 50ms gap, 10ms)
124
+
125
+ This triple feedback creates a moment of certainty that a single visual change cannot match. Use it sparingly, for milestone moments (order placed, task completed, level achieved).