picasso-skill 2.0.0 → 2.0.2

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,291 @@
1
+ # Micro-Interactions Reference
2
+
3
+ ## Table of Contents
4
+ 1. Scroll-Triggered Animations
5
+ 2. Page Transitions
6
+ 3. Button and Press Feedback
7
+ 4. Toggle and Switch Animations
8
+ 5. Number and Text Morphing
9
+ 6. Magnetic and Cursor Effects
10
+ 7. Gesture-Based Interactions
11
+ 8. Animation Performance Budget
12
+ 9. Common Mistakes
13
+
14
+ ---
15
+
16
+ ## 1. Scroll-Triggered Animations
17
+
18
+ Use IntersectionObserver. Never `addEventListener('scroll')` — it fires on every pixel and destroys performance.
19
+
20
+ ```js
21
+ const observer = new IntersectionObserver((entries) => {
22
+ entries.forEach(entry => {
23
+ if (entry.isIntersecting) {
24
+ entry.target.classList.add('revealed');
25
+ observer.unobserve(entry.target); // animate once
26
+ }
27
+ });
28
+ }, { threshold: 0.15, rootMargin: '0px 0px -50px 0px' });
29
+
30
+ document.querySelectorAll('.reveal-on-scroll').forEach(el => observer.observe(el));
31
+ ```
32
+
33
+ ```css
34
+ .reveal-on-scroll {
35
+ opacity: 0;
36
+ transform: translateY(20px);
37
+ transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
38
+ }
39
+ .reveal-on-scroll.revealed {
40
+ opacity: 1;
41
+ transform: translateY(0);
42
+ }
43
+ ```
44
+
45
+ Parallax: max 10% speed difference. More than that causes motion sickness.
46
+
47
+ ```css
48
+ .parallax-slow { transform: translateY(calc(var(--scroll-y) * -0.05)); }
49
+ ```
50
+
51
+ ---
52
+
53
+ ## 2. Page Transitions
54
+
55
+ View Transitions API for smooth page-to-page navigation:
56
+
57
+ ```css
58
+ ::view-transition-old(root) {
59
+ animation: fade-out 200ms ease-in forwards;
60
+ }
61
+ ::view-transition-new(root) {
62
+ animation: fade-in 300ms ease-out forwards;
63
+ }
64
+
65
+ @keyframes fade-out { to { opacity: 0; } }
66
+ @keyframes fade-in { from { opacity: 0; } }
67
+ ```
68
+
69
+ ```js
70
+ // In Next.js App Router or SPA
71
+ if (document.startViewTransition) {
72
+ document.startViewTransition(() => {
73
+ // Update DOM here
74
+ });
75
+ }
76
+ ```
77
+
78
+ Shared element transitions for items that persist between pages:
79
+
80
+ ```css
81
+ .product-image { view-transition-name: product-hero; }
82
+
83
+ ::view-transition-old(product-hero),
84
+ ::view-transition-new(product-hero) {
85
+ animation-duration: 300ms;
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 3. Button and Press Feedback
92
+
93
+ Combine scale + shadow reduction for a physical "press" feel:
94
+
95
+ ```css
96
+ .btn {
97
+ transition: transform 80ms ease-out, box-shadow 80ms ease-out;
98
+ }
99
+ .btn:active {
100
+ transform: scale(0.97);
101
+ box-shadow: 0 1px 2px oklch(0 0 0 / 0.1); /* shadow shrinks on press */
102
+ }
103
+ ```
104
+
105
+ For icon buttons, use a radial ripple:
106
+
107
+ ```css
108
+ .icon-btn {
109
+ position: relative;
110
+ overflow: hidden;
111
+ }
112
+ .icon-btn::after {
113
+ content: '';
114
+ position: absolute;
115
+ inset: 0;
116
+ background: radial-gradient(circle at var(--x, 50%) var(--y, 50%), oklch(1 0 0 / 0.1) 0%, transparent 60%);
117
+ opacity: 0;
118
+ transition: opacity 150ms;
119
+ }
120
+ .icon-btn:active::after { opacity: 1; }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## 4. Toggle and Switch Animations
126
+
127
+ ```css
128
+ .toggle-track {
129
+ width: 44px;
130
+ height: 24px;
131
+ border-radius: 12px;
132
+ background: var(--surface-3);
133
+ transition: background-color 200ms ease-out;
134
+ position: relative;
135
+ }
136
+ .toggle-track[aria-checked="true"] {
137
+ background: var(--accent);
138
+ }
139
+ .toggle-thumb {
140
+ width: 20px;
141
+ height: 20px;
142
+ border-radius: 50%;
143
+ background: white;
144
+ position: absolute;
145
+ top: 2px;
146
+ left: 2px;
147
+ transition: transform 200ms var(--ease-out);
148
+ box-shadow: 0 1px 3px oklch(0 0 0 / 0.15);
149
+ }
150
+ .toggle-track[aria-checked="true"] .toggle-thumb {
151
+ transform: translateX(20px);
152
+ }
153
+ ```
154
+
155
+ Checkbox checkmark: draw with SVG stroke-dasharray animation (150ms).
156
+
157
+ ---
158
+
159
+ ## 5. Number and Text Morphing
160
+
161
+ For counters and changing text, use `torph` or CSS counter animation:
162
+
163
+ ```css
164
+ @property --num {
165
+ syntax: '<integer>';
166
+ initial-value: 0;
167
+ inherits: false;
168
+ }
169
+ .counter {
170
+ transition: --num 1s ease-out;
171
+ counter-reset: num var(--num);
172
+ }
173
+ .counter::after {
174
+ content: counter(num);
175
+ }
176
+ ```
177
+
178
+ For text morphing between labels (e.g., tab switching):
179
+
180
+ ```jsx
181
+ import { TextMorph } from 'torph/react';
182
+
183
+ <TextMorph>{activeTab === 'overview' ? 'Overview' : 'Details'}</TextMorph>
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 6. Magnetic and Cursor Effects
189
+
190
+ Subtle magnetic pull toward buttons/links. Max displacement: 8px. Only on desktop (no hover on mobile).
191
+
192
+ ```js
193
+ // Apply to elements with data-magnetic
194
+ document.querySelectorAll('[data-magnetic]').forEach(el => {
195
+ el.addEventListener('mousemove', (e) => {
196
+ const rect = el.getBoundingClientRect();
197
+ const x = e.clientX - rect.left - rect.width / 2;
198
+ const y = e.clientY - rect.top - rect.height / 2;
199
+ el.style.transform = `translate(${x * 0.15}px, ${y * 0.15}px)`;
200
+ });
201
+ el.addEventListener('mouseleave', () => {
202
+ el.style.transform = '';
203
+ el.style.transition = 'transform 0.3s var(--ease-out)';
204
+ });
205
+ });
206
+ ```
207
+
208
+ Custom cursor (use sparingly — only for creative/portfolio sites):
209
+
210
+ ```css
211
+ .custom-cursor {
212
+ cursor: none;
213
+ }
214
+ .cursor-dot {
215
+ width: 8px; height: 8px;
216
+ background: var(--accent);
217
+ border-radius: 50%;
218
+ position: fixed;
219
+ pointer-events: none;
220
+ z-index: 9999;
221
+ transition: transform 0.1s;
222
+ mix-blend-mode: difference;
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## 7. Gesture-Based Interactions
229
+
230
+ Mobile gestures require `touch-action` CSS to prevent browser defaults:
231
+
232
+ ```css
233
+ /* Horizontal swipe area — disable horizontal scroll */
234
+ .swipe-container { touch-action: pan-y; }
235
+
236
+ /* Pinch-to-zoom area */
237
+ .zoomable { touch-action: manipulation; }
238
+ ```
239
+
240
+ Swipe detection (vanilla, no library):
241
+
242
+ ```js
243
+ let startX = 0;
244
+ el.addEventListener('touchstart', e => { startX = e.touches[0].clientX; });
245
+ el.addEventListener('touchend', e => {
246
+ const diff = e.changedTouches[0].clientX - startX;
247
+ if (Math.abs(diff) > 50) { // 50px threshold
248
+ diff > 0 ? onSwipeRight() : onSwipeLeft();
249
+ }
250
+ });
251
+ ```
252
+
253
+ Gesture discoverability: NEVER rely on hidden gestures. Always provide a visible button alternative. Swipe should be a shortcut, not the only way.
254
+
255
+ ---
256
+
257
+ ## 8. Animation Performance Budget
258
+
259
+ - **Max 3 concurrent animations** on screen at once. More causes frame drops on mid-range devices.
260
+ - **Only animate `transform` and `opacity`** — these are compositor-only and skip layout/paint.
261
+ - **Use `will-change` sparingly** — add before animation starts, remove after. Never `will-change: all`.
262
+ - **16ms frame budget** — if your animation JS takes more than 16ms per frame, it drops below 60fps.
263
+ - **Test with CPU throttling** — Chrome DevTools → Performance → CPU 6x slowdown. If it stutters there, it stutters on real phones.
264
+
265
+ ```css
266
+ /* Good: compositor-only */
267
+ .animate-enter {
268
+ will-change: transform, opacity;
269
+ animation: slide-in 300ms var(--ease-out) forwards;
270
+ }
271
+ .animate-enter.done { will-change: auto; } /* remove after animation */
272
+
273
+ @keyframes slide-in {
274
+ from { opacity: 0; transform: translateY(10px); }
275
+ to { opacity: 1; transform: translateY(0); }
276
+ }
277
+ ```
278
+
279
+ ---
280
+
281
+ ## 9. Common Mistakes
282
+
283
+ - **`addEventListener('scroll')` for animations.** Use IntersectionObserver.
284
+ - **Animating `width`, `height`, `top`, `left`.** Triggers layout. Use `transform` only.
285
+ - **More than 3 concurrent animations.** Causes jank on real devices.
286
+ - **`will-change` on everything.** Creates a compositing layer per element. Memory hog.
287
+ - **Parallax with large speed differences.** Max 10% or users get motion sick.
288
+ - **Hidden gestures with no visible alternative.** Users won't discover them.
289
+ - **No `prefers-reduced-motion` check.** Wrap all non-essential motion in the media query.
290
+ - **Magnetic effects on mobile.** No hover on touch devices. Gate with `@media (hover: hover)`.
291
+ - **Page transitions without fallback.** View Transitions API isn't universal. Always provide a no-transition fallback.
@@ -0,0 +1,247 @@
1
+ # Navigation Patterns Reference
2
+
3
+ ## Table of Contents
4
+ 1. Breadcrumb Systems
5
+ 2. Sidebar and Drawer Navigation
6
+ 3. Tab Navigation vs Button Groups
7
+ 4. Mobile Bottom Bar vs Hamburger
8
+ 5. Sticky Headers
9
+ 6. Mega Menus
10
+ 7. Skip Links
11
+ 8. Common Mistakes
12
+
13
+ ---
14
+
15
+ ## 1. Breadcrumb Systems
16
+
17
+ ```html
18
+ <nav aria-label="Breadcrumb">
19
+ <ol class="flex items-center gap-1.5 text-sm">
20
+ <li><a href="/" class="text-muted hover:text-primary">Home</a></li>
21
+ <li aria-hidden="true" class="text-muted">/</li>
22
+ <li><a href="/products" class="text-muted hover:text-primary">Products</a></li>
23
+ <li aria-hidden="true" class="text-muted">/</li>
24
+ <li><span aria-current="page" class="text-primary font-medium">Widget Pro</span></li>
25
+ </ol>
26
+ </nav>
27
+ ```
28
+
29
+ Rules:
30
+ - Use `<nav aria-label="Breadcrumb">` with `<ol>` (ordered list).
31
+ - Mark current page with `aria-current="page"`. Don't make it a link.
32
+ - Separator (`/` or `>`) gets `aria-hidden="true"`.
33
+ - Show 3-5 levels max. Truncate middle levels with `...` on mobile.
34
+
35
+ ---
36
+
37
+ ## 2. Sidebar and Drawer Navigation
38
+
39
+ Desktop: fixed sidebar, 240-280px width. Mobile: off-canvas drawer with overlay.
40
+
41
+ ```jsx
42
+ <aside className={`fixed inset-y-0 left-0 w-[260px] bg-surface-1 border-r border-border
43
+ transform transition-transform duration-200 z-50
44
+ ${mobileOpen ? 'translate-x-0' : '-translate-x-full'} md:translate-x-0`}>
45
+ <nav aria-label="Main navigation">
46
+ {/* nav items */}
47
+ </nav>
48
+ </aside>
49
+
50
+ {/* Mobile overlay */}
51
+ {mobileOpen && (
52
+ <div className="fixed inset-0 bg-black/50 z-40 md:hidden"
53
+ onClick={() => setMobileOpen(false)} />
54
+ )}
55
+ ```
56
+
57
+ Active state: use background tint + accent color text, not bold weight (weight change shifts text width).
58
+
59
+ ```css
60
+ .nav-item { padding: 7px 12px; border-radius: 6px; transition: background 100ms; }
61
+ .nav-item:hover { background: oklch(1 0 0 / 0.04); }
62
+ .nav-item.active { background: oklch(0.65 0.15 230 / 0.1); color: var(--accent); }
63
+ ```
64
+
65
+ Section labels: 10px, uppercase, `tracking-[0.12em]`, muted color. Group related items visually.
66
+
67
+ ---
68
+
69
+ ## 3. Tab Navigation vs Button Groups
70
+
71
+ **Tabs:** switch between views of the SAME content. One is always active. Use `role="tablist"`.
72
+
73
+ ```html
74
+ <div role="tablist" aria-label="Account sections">
75
+ <button role="tab" aria-selected="true" aria-controls="panel-profile" id="tab-profile">
76
+ Profile
77
+ </button>
78
+ <button role="tab" aria-selected="false" aria-controls="panel-billing" id="tab-billing">
79
+ Billing
80
+ </button>
81
+ </div>
82
+ <div role="tabpanel" id="panel-profile" aria-labelledby="tab-profile">
83
+ <!-- content -->
84
+ </div>
85
+ ```
86
+
87
+ **Button groups:** trigger independent ACTIONS. Multiple can be active (toggle group) or none.
88
+
89
+ ```html
90
+ <div role="group" aria-label="View options">
91
+ <button aria-pressed="true">Grid</button>
92
+ <button aria-pressed="false">List</button>
93
+ </div>
94
+ ```
95
+
96
+ Decision: if clicking switches what you see → tabs. If clicking does something → button group.
97
+
98
+ ---
99
+
100
+ ## 4. Mobile Bottom Bar vs Hamburger
101
+
102
+ Bottom bar outperforms hamburger for engagement. Hamburger hides navigation behind a tap, reducing discoverability.
103
+
104
+ **When to use bottom bar:**
105
+ - 3-5 primary destinations
106
+ - Mobile-first app (not marketing site)
107
+ - User needs to switch between sections frequently
108
+
109
+ **When to use hamburger:**
110
+ - 6+ navigation items
111
+ - Marketing/content site with hierarchical nav
112
+ - Secondary navigation (settings, help)
113
+
114
+ ```css
115
+ .bottom-bar {
116
+ position: fixed;
117
+ bottom: 0;
118
+ left: 0;
119
+ right: 0;
120
+ height: 56px; /* 48px min for touch, 56px comfortable */
121
+ padding-bottom: env(safe-area-inset-bottom); /* notch-safe */
122
+ display: flex;
123
+ justify-content: space-around;
124
+ align-items: center;
125
+ background: var(--surface-1);
126
+ border-top: 1px solid var(--border);
127
+ z-index: 50;
128
+ }
129
+
130
+ .bottom-bar-item {
131
+ display: flex;
132
+ flex-direction: column;
133
+ align-items: center;
134
+ gap: 2px;
135
+ min-width: 64px;
136
+ padding: 4px 12px;
137
+ font-size: 10px;
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 5. Sticky Headers
144
+
145
+ Keep slim: 48-56px height. Hide on scroll-down, reveal on scroll-up.
146
+
147
+ ```js
148
+ let lastScroll = 0;
149
+ const header = document.querySelector('header');
150
+
151
+ window.addEventListener('scroll', () => {
152
+ const current = window.scrollY;
153
+ if (current > lastScroll && current > 80) {
154
+ header.style.transform = 'translateY(-100%)';
155
+ } else {
156
+ header.style.transform = 'translateY(0)';
157
+ }
158
+ lastScroll = current;
159
+ }, { passive: true });
160
+ ```
161
+
162
+ ```css
163
+ header {
164
+ position: sticky;
165
+ top: 0;
166
+ height: 56px;
167
+ z-index: 40;
168
+ backdrop-filter: blur(12px);
169
+ background: oklch(var(--bg-l) var(--bg-c) var(--bg-h) / 0.8);
170
+ border-bottom: 1px solid var(--border);
171
+ transition: transform 200ms ease-out;
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ ## 6. Mega Menus
178
+
179
+ For sites with 20+ navigation items. Two approaches:
180
+
181
+ **Full-width panel** (preferred): Flyout spans full viewport width. Organized in columns.
182
+
183
+ ```css
184
+ .mega-menu {
185
+ position: absolute;
186
+ top: 100%;
187
+ left: 0;
188
+ right: 0;
189
+ padding: 2rem;
190
+ display: grid;
191
+ grid-template-columns: repeat(4, 1fr);
192
+ gap: 2rem;
193
+ background: var(--surface-1);
194
+ border-top: 1px solid var(--border);
195
+ box-shadow: var(--shadow-lg);
196
+ }
197
+ ```
198
+
199
+ **Alternatives to mega menus:**
200
+ - Search-first navigation (Command+K palette)
201
+ - Two-level sidebar (category → items)
202
+ - Card-based navigation page
203
+
204
+ Keep mega menu hover delay at 200-300ms to prevent accidental opens.
205
+
206
+ ---
207
+
208
+ ## 7. Skip Links
209
+
210
+ First focusable element on the page. Visually hidden until focused.
211
+
212
+ ```html
213
+ <a href="#main-content" class="skip-link">Skip to main content</a>
214
+ <!-- header, nav, etc -->
215
+ <main id="main-content" tabindex="-1">
216
+ ```
217
+
218
+ ```css
219
+ .skip-link {
220
+ position: absolute;
221
+ top: -100%;
222
+ left: 1rem;
223
+ z-index: 100;
224
+ padding: 0.5rem 1rem;
225
+ background: var(--accent);
226
+ color: white;
227
+ border-radius: 0 0 8px 8px;
228
+ font-weight: 600;
229
+ }
230
+ .skip-link:focus {
231
+ top: 0;
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## 8. Common Mistakes
238
+
239
+ - **Hamburger as the only mobile navigation.** Bottom bar is better for primary destinations.
240
+ - **Active nav state using `font-weight: bold`.** Shifts text width. Use background/color instead.
241
+ - **Sidebar over 300px wide.** Steals too much screen. 240-280px is ideal.
242
+ - **Sticky header over 64px.** Takes too much vertical space. 48-56px max.
243
+ - **No `aria-current="page"` on breadcrumbs.** Screen readers can't identify current page.
244
+ - **Tabs without `role="tablist"`/`role="tab"`.** Not accessible.
245
+ - **Mega menu without hover delay.** Opens accidentally when moving mouse across nav.
246
+ - **No skip link.** Keyboard users must tab through entire header to reach content.
247
+ - **Bottom bar without `safe-area-inset-bottom`.** Content hides behind iPhone notch.