@ericrisco/rsc 3.3.0 → 3.4.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.
Files changed (56) hide show
  1. package/README.md +6 -3
  2. package/manifest.json +43 -22
  3. package/package.json +1 -1
  4. package/scripts/lib/default-skill-floor.js +5 -1
  5. package/scripts/lib/domains.js +2 -2
  6. package/scripts/lib/retired-skills.js +4 -0
  7. package/skills/design/SKILL.md +4 -3
  8. package/skills/design/evals/cases.yaml +2 -2
  9. package/skills/react/SKILL.md +2 -1
  10. package/skills/suggest/SKILL.md +12 -8
  11. package/skills/suggest/evals/cases.yaml +6 -0
  12. package/skills/ux/SKILL.md +149 -0
  13. package/skills/ux/THIRD_PARTY.md +32 -0
  14. package/skills/ux/evals/cases.yaml +40 -0
  15. package/skills/ux/references/flows.md +124 -0
  16. package/skills/ux/upstream/UPSTREAM.json +59 -0
  17. package/skills/ux/upstream/ux-designer/GUIDE.md +180 -0
  18. package/skills/ux/upstream/ux-designer/LICENSE +21 -0
  19. package/skills/ux/upstream/ux-designer/references/01-core-principles.md +251 -0
  20. package/skills/ux/upstream/ux-designer/references/02-laws-of-ux.md +330 -0
  21. package/skills/ux/upstream/ux-designer/references/03-accessibility.md +411 -0
  22. package/skills/ux/upstream/ux-designer/references/04-visual-design.md +433 -0
  23. package/skills/ux/upstream/ux-designer/references/05-information-architecture.md +432 -0
  24. package/skills/ux/upstream/ux-designer/references/06-interaction-design.md +495 -0
  25. package/skills/ux/upstream/ux-designer/references/07-forms-and-inputs.md +491 -0
  26. package/skills/ux/upstream/ux-designer/references/08-mobile-ux.md +539 -0
  27. package/skills/ux/upstream/ux-designer/references/09-ux-writing.md +493 -0
  28. package/skills/ux/upstream/ux-designer/references/10-user-research.md +533 -0
  29. package/skills/ux/upstream/ux-designer/references/11-design-systems.md +618 -0
  30. package/skills/ux/upstream/ux-designer/references/12a-presence-awareness.md +395 -0
  31. package/skills/ux/upstream/ux-designer/references/12b-conflict-resolution-sync.md +420 -0
  32. package/skills/ux/upstream/ux-designer/references/13a-canvas-navigation.md +388 -0
  33. package/skills/ux/upstream/ux-designer/references/13b-canvas-objects-performance.md +559 -0
  34. package/skills/ux/upstream/ux-designer/references/14-ai-ux-patterns.md +511 -0
  35. package/skills/ux/upstream/ux-designer/references/15-ethical-design.md +419 -0
  36. package/skills/ux/upstream/ux-designer/references/16-onboarding.md +430 -0
  37. package/skills/ux/upstream/ux-designer/references/17-notifications.md +461 -0
  38. package/skills/ux/upstream/ux-designer/references/18-data-visualization.md +453 -0
  39. package/skills/ux/upstream/ux-designer/references/19-search-ux.md +479 -0
  40. package/skills/ux/upstream/ux-designer/references/20-emotional-design.md +382 -0
  41. package/skills/ux/upstream/ux-designer/references/21-data-tables.md +629 -0
  42. package/skills/ux/upstream/ux-designer/references/22-performance-ux.md +626 -0
  43. package/skills/ux/upstream/ux-designer/references/23-internationalization.md +347 -0
  44. package/skills/ux/upstream/ux-designer/references/24-voice-and-multimodal.md +308 -0
  45. package/skills/ux/upstream/ux-designer/references/25a-barcode-camera-scanning.md +342 -0
  46. package/skills/ux/upstream/ux-designer/references/25b-barcode-scanner-workflows.md +412 -0
  47. package/skills/ux/upstream/ux-designer/references/25c-barcode-display.md +277 -0
  48. package/skills/ux/upstream/ux-usability-foundations/GUIDE.md +285 -0
  49. package/skills/ux/upstream/ux-usability-foundations/LICENSE +21 -0
  50. package/skills/ux/upstream/ux-usability-foundations/references/anti-patterns.md +267 -0
  51. package/skills/ux/upstream/ux-usability-foundations/references/checklists.md +130 -0
  52. package/skills/ux/upstream/ux-usability-foundations/references/decision-prompts.md +340 -0
  53. package/skills/ux/upstream/ux-usability-foundations/references/principle-cards.md +449 -0
  54. package/targets/clone-bootstrap.mjs +1 -1
  55. package/skills/ui-engineering/SKILL.md +0 -113
  56. package/skills/ui-engineering/evals/cases.yaml +0 -38
@@ -0,0 +1,388 @@
1
+ # Canvas Apps: Navigation & Interaction
2
+
3
+ Canvas-based interfaces provide an infinite 2D workspace where users can freely place, move, and manipulate objects. Found in design tools (Figma, Sketch), whiteboards (Miro, FigJam), maps, and diagramming apps.
4
+
5
+ ---
6
+
7
+ ## Contents
8
+
9
+ - Canvas Navigation
10
+ - Object Selection
11
+ - Object Manipulation
12
+ - Key Metrics
13
+ - Anti-Patterns
14
+ - Sources
15
+
16
+ ---
17
+
18
+ ## Canvas Navigation
19
+
20
+ ### Coordinate Systems
21
+
22
+ A canvas app has two coordinate systems:
23
+
24
+ ```javascript
25
+ // Screen coordinates: pixel position on display
26
+ const screenPoint = { x: 500, y: 300 };
27
+
28
+ // Canvas coordinates: position in the infinite canvas space
29
+ const canvasPoint = {
30
+ x: (screenPoint.x - camera.x) / camera.zoom,
31
+ y: (screenPoint.y - camera.y) / camera.zoom
32
+ };
33
+
34
+ // Camera state
35
+ const camera = {
36
+ x: 0, // Horizontal offset
37
+ y: 0, // Vertical offset
38
+ zoom: 1 // Scale factor (1 = 100%)
39
+ };
40
+ ```
41
+
42
+ ### Zoom UI
43
+
44
+ Zoom should feel natural, focusing on the cursor position.
45
+
46
+ ```javascript
47
+ function handleZoom(event) {
48
+ event.preventDefault();
49
+
50
+ const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
51
+ const newZoom = clamp(camera.zoom * zoomFactor, MIN_ZOOM, MAX_ZOOM);
52
+
53
+ // Zoom toward cursor position
54
+ const cursorX = event.clientX;
55
+ const cursorY = event.clientY;
56
+
57
+ // Calculate new camera position to keep cursor point fixed
58
+ camera.x = cursorX - (cursorX - camera.x) * (newZoom / camera.zoom);
59
+ camera.y = cursorY - (cursorY - camera.y) * (newZoom / camera.zoom);
60
+ camera.zoom = newZoom;
61
+
62
+ render();
63
+ }
64
+ ```
65
+
66
+ #### Zoom Controls
67
+
68
+ ```
69
+ ┌──────────────────────────────┐
70
+ │ [−] ━━━●━━━━━━━━━ [+] │ Slider
71
+ │ 100% │ Current level
72
+ │ [Fit] [50%] [100%] [200%] │ Presets
73
+ └──────────────────────────────┘
74
+ ```
75
+
76
+ | Control | Action |
77
+ |---------|--------|
78
+ | Scroll wheel | Zoom in/out |
79
+ | Ctrl/Cmd + scroll | Zoom (alternative) |
80
+ | Pinch gesture | Zoom on touch devices |
81
+ | +/- keys | Step zoom |
82
+ | 0 or 1 | Reset to 100% |
83
+ | Shift + 1 | Fit all to view |
84
+ | Shift + 2 | Fit selection to view |
85
+
86
+ #### Zoom Levels
87
+
88
+ ```
89
+ Typical range: 10% to 4000%+
90
+ Common presets: 25%, 50%, 75%, 100%, 150%, 200%, 400%
91
+
92
+ Semantic zooming (content changes at different levels):
93
+ - 10-25% → Show only shapes/outlines
94
+ - 25-50% → Show basic content, hide details
95
+ - 50-100% → Full content visible
96
+ - 100-200% → Pixel-perfect editing
97
+ - 200%+ → Sub-pixel precision work
98
+ ```
99
+
100
+ ### Pan/Scroll
101
+
102
+ ```javascript
103
+ // Space + drag to pan
104
+ let isPanning = false;
105
+ let lastPanPoint = { x: 0, y: 0 };
106
+
107
+ function handleKeyDown(event) {
108
+ if (event.code === 'Space' && !isPanning) {
109
+ isPanning = true;
110
+ document.body.style.cursor = 'grab';
111
+ }
112
+ }
113
+
114
+ function handleMouseMove(event) {
115
+ if (isPanning && event.buttons === 1) {
116
+ camera.x += event.clientX - lastPanPoint.x;
117
+ camera.y += event.clientY - lastPanPoint.y;
118
+ document.body.style.cursor = 'grabbing';
119
+ render();
120
+ }
121
+ lastPanPoint = { x: event.clientX, y: event.clientY };
122
+ }
123
+ ```
124
+
125
+ #### Pan Methods
126
+
127
+ | Method | Trigger | Notes |
128
+ |--------|---------|-------|
129
+ | Space + drag | Hold space, click and drag | Most common in design tools |
130
+ | Middle mouse | Middle click and drag | Power user shortcut |
131
+ | Two-finger drag | Trackpad gesture | Natural for laptop users |
132
+ | Scroll bars | Click and drag | Visible scroll position |
133
+ | Arrow keys | Keyboard navigation | Step-based movement |
134
+
135
+ ### Minimap / Overview
136
+
137
+ Bird's-eye navigation for large canvases.
138
+
139
+ ```html
140
+ <div class="minimap">
141
+ <canvas id="minimap-canvas"></canvas>
142
+ <div class="minimap__viewport"></div>
143
+ </div>
144
+ ```
145
+
146
+ ```css
147
+ .minimap {
148
+ position: fixed;
149
+ bottom: 16px;
150
+ right: 16px;
151
+ width: 200px;
152
+ height: 150px;
153
+ background: #f3f4f6;
154
+ border: 1px solid #d1d5db;
155
+ border-radius: 8px;
156
+ overflow: hidden;
157
+ }
158
+
159
+ .minimap__viewport {
160
+ position: absolute;
161
+ border: 2px solid #3b82f6;
162
+ background: rgba(59, 130, 246, 0.1);
163
+ cursor: move;
164
+ }
165
+ ```
166
+
167
+ **Minimap Guidelines:**
168
+ - Show at 5-10% of actual canvas scale
169
+ - Highlight current viewport area
170
+ - Allow click-to-navigate
171
+ - Allow drag viewport rectangle
172
+ - Consider hiding when zoomed to fit all
173
+
174
+ ---
175
+
176
+ ## Object Selection
177
+
178
+ ### Selection Modes
179
+
180
+ ```javascript
181
+ // Click selection
182
+ function handleClick(event) {
183
+ const hitObject = hitTest(event.x, event.y);
184
+
185
+ if (event.shiftKey) {
186
+ // Add to / remove from selection
187
+ toggleSelection(hitObject);
188
+ } else {
189
+ // Replace selection
190
+ clearSelection();
191
+ if (hitObject) select(hitObject);
192
+ }
193
+ }
194
+
195
+ // Marquee selection
196
+ function handleMarquee(startPoint, endPoint) {
197
+ const rect = {
198
+ x: Math.min(startPoint.x, endPoint.x),
199
+ y: Math.min(startPoint.y, endPoint.y),
200
+ width: Math.abs(endPoint.x - startPoint.x),
201
+ height: Math.abs(endPoint.y - startPoint.y)
202
+ };
203
+
204
+ const contained = objects.filter(obj =>
205
+ isContainedIn(obj.bounds, rect)
206
+ );
207
+
208
+ if (event.shiftKey) {
209
+ addToSelection(contained);
210
+ } else {
211
+ setSelection(contained);
212
+ }
213
+ }
214
+ ```
215
+
216
+ ### Selection Visuals
217
+
218
+ ```css
219
+ /* Selected object */
220
+ .object--selected {
221
+ outline: 2px solid #3b82f6;
222
+ outline-offset: 2px;
223
+ }
224
+
225
+ /* Selection marquee */
226
+ .selection-marquee {
227
+ position: absolute;
228
+ border: 1px solid #3b82f6;
229
+ background: rgba(59, 130, 246, 0.1);
230
+ pointer-events: none;
231
+ }
232
+
233
+ /* Multi-selection bounding box */
234
+ .selection-bounds {
235
+ position: absolute;
236
+ border: 1px dashed #3b82f6;
237
+ }
238
+ ```
239
+
240
+ ### Selection Keyboard Shortcuts
241
+
242
+ | Shortcut | Action |
243
+ |----------|--------|
244
+ | Click | Select single object |
245
+ | Shift + Click | Add/remove from selection |
246
+ | Cmd/Ctrl + A | Select all |
247
+ | Escape | Deselect all |
248
+ | Tab | Select next object |
249
+ | Shift + Tab | Select previous object |
250
+ | Cmd/Ctrl + Click | Deep select (nested elements) |
251
+
252
+ ---
253
+
254
+ ## Object Manipulation
255
+
256
+ ### Transform Controls
257
+
258
+ ```
259
+ ┌─────────────────────────────────┐
260
+ ◯───┤ ├───◯ ← Rotation handle
261
+ │ │
262
+ ◻───┤ Selected Object ├───◻ ← Resize handles
263
+ │ │
264
+ ◯───┤ ├───◯
265
+ └─────────────────────────────────┘
266
+ ◻ ◻
267
+ ```
268
+
269
+ ```css
270
+ /* Resize handles */
271
+ .resize-handle {
272
+ position: absolute;
273
+ width: 8px;
274
+ height: 8px;
275
+ background: white;
276
+ border: 1px solid #3b82f6;
277
+ border-radius: 2px;
278
+ }
279
+
280
+ /* Corner handles - resize */
281
+ .resize-handle--nw { cursor: nwse-resize; top: -4px; left: -4px; }
282
+ .resize-handle--ne { cursor: nesw-resize; top: -4px; right: -4px; }
283
+ .resize-handle--sw { cursor: nesw-resize; bottom: -4px; left: -4px; }
284
+ .resize-handle--se { cursor: nwse-resize; bottom: -4px; right: -4px; }
285
+
286
+ /* Edge handles */
287
+ .resize-handle--n { cursor: ns-resize; top: -4px; left: 50%; }
288
+ .resize-handle--s { cursor: ns-resize; bottom: -4px; left: 50%; }
289
+ .resize-handle--e { cursor: ew-resize; right: -4px; top: 50%; }
290
+ .resize-handle--w { cursor: ew-resize; left: -4px; top: 50%; }
291
+
292
+ /* Rotation handle */
293
+ .rotate-handle {
294
+ position: absolute;
295
+ top: -30px;
296
+ left: 50%;
297
+ width: 10px;
298
+ height: 10px;
299
+ background: white;
300
+ border: 1px solid #3b82f6;
301
+ border-radius: 50%;
302
+ cursor: grab;
303
+ }
304
+ ```
305
+
306
+ ### Drag and Drop
307
+
308
+ ```javascript
309
+ function handleDrag(object, event) {
310
+ // Show ghost/preview at cursor
311
+ showDragPreview(object, event.x, event.y);
312
+
313
+ // Check drop targets
314
+ const dropTarget = findDropTarget(event.x, event.y);
315
+ if (dropTarget) {
316
+ highlightDropTarget(dropTarget);
317
+ }
318
+ }
319
+
320
+ function handleDrop(object, event) {
321
+ const dropTarget = findDropTarget(event.x, event.y);
322
+
323
+ // Animate to final position
324
+ animateTo(object, {
325
+ x: snapToGrid(event.x),
326
+ y: snapToGrid(event.y)
327
+ }, { duration: 100 });
328
+
329
+ hideDragPreview();
330
+ }
331
+ ```
332
+
333
+ **Drag and Drop Guidelines:**
334
+ - Show visual feedback during drag (elevation, shadow)
335
+ - Animate other elements moving out of the way
336
+ - Use 100ms animation for drop
337
+ - Provide clear drop zone indicators
338
+ - Support keyboard alternatives (WCAG 2.5.7)
339
+
340
+ ### Keyboard Movement
341
+
342
+ ```javascript
343
+ const MOVE_STEP = 1; // Arrow key
344
+ const MOVE_STEP_LARGE = 10; // Shift + Arrow
345
+
346
+ function handleArrowKey(key, shiftKey) {
347
+ const step = shiftKey ? MOVE_STEP_LARGE : MOVE_STEP;
348
+
349
+ selectedObjects.forEach(obj => {
350
+ switch(key) {
351
+ case 'ArrowUp': obj.y -= step; break;
352
+ case 'ArrowDown': obj.y += step; break;
353
+ case 'ArrowLeft': obj.x -= step; break;
354
+ case 'ArrowRight': obj.x += step; break;
355
+ }
356
+ });
357
+ }
358
+ ```
359
+
360
+ ---
361
+
362
+ ## Key Metrics
363
+
364
+ | Metric | Value | Context |
365
+ |--------|-------|---------|
366
+ | Zoom range | 10% - 4000% | Typical design tools |
367
+ | Pan speed | 1:1 cursor ratio | Natural feeling |
368
+ | Touch target | 44×44px | Minimum for handles |
369
+ | Render target | 60fps | During pan/zoom |
370
+
371
+ ---
372
+
373
+ ## Anti-Patterns
374
+
375
+ 1. **Fixed canvas size** - Limiting creativity with artificial boundaries
376
+ 2. **Zoom at screen center** - Disorienting; zoom at cursor instead
377
+ 3. **Sluggish pan/zoom** - Must feel instant and smooth
378
+ 4. **Inconsistent shortcuts** - Differs from industry standard
379
+ 5. **Poor touch support** - Gestures should match platform conventions
380
+
381
+ ---
382
+
383
+ ## Sources
384
+
385
+ - [Steve Ruiz - Creating a Zoom UI](https://www.steveruiz.me/posts/zoom-ui)
386
+ - [NN/g - Drag and Drop](https://www.nngroup.com/articles/drag-drop/)
387
+ - [Pencil & Paper - Drag & Drop UX Patterns](https://www.pencilandpaper.io/articles/ux-pattern-drag-and-drop)
388
+ - [Salesforce - 4 Patterns for Accessible Drag and Drop](https://medium.com/salesforce-ux/4-major-patterns-for-accessible-drag-and-drop-1d43f64ebf09)