references/05-information-architecture.md
# Information Architecture
Information Architecture (IA) is the structural design of shared information environments—how content is organized, labeled, and navigated.
---
## IA Fundamentals
### What is Information Architecture?
IA is the blueprint of a digital product, defining:
- How content is organized and categorized
- How users navigate between sections
- How information is labeled and described
- How users find what they're looking for
### Core Components
1. **Organization Systems** - How content is grouped
2. **Labeling Systems** - How content is named
3. **Navigation Systems** - How users move through content
4. **Search Systems** - How users find specific content
---
## Organization Structures
### Hierarchical (Tree)
Most common structure. Content flows from broad to specific.
```
Home
├── Products
│ ├── Category A
│ │ ├── Product 1
│ │ └── Product 2
│ └── Category B
├── About
└── Contact
```
**Best for:** E-commerce, documentation, corporate sites
### Sequential (Linear)
Content follows a specific order.
```
Step 1 → Step 2 → Step 3 → Step 4 → Done
```
**Best for:** Onboarding flows, tutorials, checkout processes
### Matrix
Multiple navigation paths to the same content.
```
Products can be accessed by:
- Category
- Brand
- Price range
- New arrivals
```
**Best for:** E-commerce, catalogs, databases
### Network (Web)
No fixed hierarchy; content interconnects freely.
```
Page A ←→ Page B
↕ ↕
Page C ←→ Page D
```
**Best for:** Wikis, knowledge bases, blogs
---
## Navigation Patterns
### Global Navigation
Consistent navigation across all pages.
```
Horizontal Navigation Bar:
┌────────────────────────────────────────┐
│ Logo Home Products About Contact│
└────────────────────────────────────────┘
Best for:
- Sites with 5-7 top-level sections
- Desktop-first designs
- Corporate/marketing sites
```
### Local Navigation
Section-specific navigation.
```
Sidebar Navigation:
┌──────────┬─────────────────┐
│ Section │ │
│ ├─ Page 1│ Content │
│ ├─ Page 2│ │
│ └─ Page 3│ │
└──────────┴─────────────────┘
Best for:
- Documentation sites
- Admin dashboards
- Settings panels
```
### Contextual Navigation
Related content links within the page.
```
"Related Articles"
"Customers Also Bought"
"See Also"
```
### Utility Navigation
Functional links (login, cart, search, settings).
```
┌─────────────────────────────────────────┐
│ Search 🔍 Cart 🛒 👤│
└─────────────────────────────────────────┘
```
### Hamburger Menu
Hidden navigation behind an icon (☰).
```
Pros:
- Saves space
- Clean interface
- Mobile-friendly
Cons:
- Reduces discoverability
- Adds interaction step
- Less suitable for desktop
```
### Bottom Navigation (Mobile)
Tab bar at bottom of screen.
```
┌─────────────────────────────────────┐
│ │
│ Content │
│ │
├─────────────────────────────────────┤
│ 🏠 🔍 ➕ ❤️ 👤 │
│ Home Search Add Saved Profile │
└─────────────────────────────────────┘
Best for:
- Mobile apps
- 3-5 primary destinations
- Frequent navigation actions
```
### Breadcrumbs
Show the user's location in the hierarchy.
```
Home > Products > Electronics > Phones > iPhone
Best for:
- Deep hierarchies
- E-commerce
- When users may enter via search
```
---
## Labeling Best Practices
### Clarity Over Cleverness
```
Good: "Pricing"
Bad: "Investment Levels"
Good: "Contact Us"
Bad: "Let's Chat"
Good: "Settings"
Bad: "Command Center"
```
### Consistency
```
Consistent verb forms:
- Create Project
- Edit Project
- Delete Project
Not:
- New Project
- Edit Project
- Remove
```
### User Language
- Use terms users understand
- Test labels with real users
- Avoid internal jargon
- Research common search terms
### Scannability
- Front-load important words
- Keep labels short (1-3 words)
- Use parallel structure
- Differentiate clearly
---
## Search Design
### Search Box Best Practices
```html
<!-- Prominent, always visible -->
<form role="search">
<label for="search" class="sr-only">Search</label>
<input
type="search"
id="search"
placeholder="Search products..."
aria-label="Search products"
/>
<button type="submit">
<span class="sr-only">Search</span>
🔍
</button>
</form>
```
**Guidelines:**
- Place in expected location (header, top-right)
- Make it large enough to see query
- Use placeholder text to indicate scope
- Support autocomplete/suggestions
- Show recent searches
### Search Results
```
Essential elements:
- Number of results
- Sorting options
- Filters/facets
- Clear result snippets
- Query highlighting
- Pagination or infinite scroll
- "No results" helpful message
```
### Zero Results Page
```
"No results for 'xyz'"
- Check spelling
- Try different keywords
- Browse categories
- Contact support
```
---
## IA Research Methods
### Card Sorting
Users organize content into groups that make sense to them.
**Open Card Sort:**
- Users create their own category names
- Reveals mental models
- Good for new products
**Closed Card Sort:**
- Users sort into predefined categories
- Tests existing structure
- Good for validation
**Tools:** OptimalSort, Maze, physical cards
### Tree Testing
Test if users can find items in your proposed structure.
```
Task: "Find the return policy"
Structure:
- Home
- Shop
- Account
- Help Center ← Target
- Shipping
- Returns ← Success
- Contact
```
**Metrics:**
- Success rate
- Directness (first click correct)
- Time to complete
**Tools:** Treejack, UserZoom
### First-Click Testing
Measure where users first click to complete a task.
**Insight:** If the first click is wrong, only 46% eventually succeed.
---
## 2026 Trends
### AI-Powered IA
- Dynamic categorization based on user behavior
- Personalized navigation paths
- Predictive search and recommendations
- Auto-tagging and organization
### Voice and Conversational IA
- Structure content for voice interfaces
- Natural language navigation
- Question-based organization
- Conversational menus
### Zero UI
- Interfaces that anticipate needs
- Reduce navigation through prediction
- Ambient computing integration
- Context-aware content delivery
---
## IA Documentation
### Sitemap
Visual representation of page hierarchy.
```
Homepage
├── Products
│ ├── Category A
│ ├── Category B
│ └── All Products
├── About
│ ├── Our Story
│ ├── Team
│ └── Careers
├── Blog
│ ├── Article List
│ └── Single Article
└── Contact
```
**Tools:** Figma, XMind, Lucidchart, Slickplan
### Content Inventory
Spreadsheet of all existing content.
| URL | Title | Type | Owner | Last Updated | Action |
|-----|-------|------|-------|--------------|--------|
| /about | About Us | Page | Marketing | 2024-06 | Keep |
| /old-promo | Summer Sale | Landing | Marketing | 2023-08 | Delete |
### User Flow Diagrams
Map the paths users take through your product.
```
Entry → Landing Page → Product List → Product Detail
↓ ↓
Search → Add to Cart → Checkout → Success
↓
Continue Shopping
```
---
## Common IA Mistakes
1. **Organizing by org structure** - Users don't know your departments
2. **Too deep** - More than 3-4 levels becomes unwieldy
3. **Too wide** - Too many top-level options overwhelms
4. **Duplicate content** - Same info in multiple places
5. **Vague labels** - "Resources," "Solutions," "Services"
6. **No search** - Users often prefer search
7. **Breaking conventions** - Cart not in top-right
8. **Mobile afterthought** - IA must work for all devices
---
## Sources
- Rosenfeld, L., Morville, P., & Arango, J. (2015). "Information Architecture: For the Web and Beyond"
- [Nielsen Norman Group - IA](https://www.nngroup.com/topic/information-architecture/)
- [Interaction Design Foundation - IA](https://www.interaction-design.org/literature/topics/information-architecture)
- [A List Apart - IA](https://alistapart.com/)
LICENSE
MIT License
Copyright (c) 2026 Szilárd Hajba
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
references/06-interaction-design.md
# Interaction Design
Interaction design (IxD) focuses on creating engaging interfaces with well-thought-out behaviors—how the system responds to user actions.
---
## Micro-interactions
Small, contained moments that accomplish a single task while enhancing the user experience.
### Dan Saffer's Four Components
#### 1. Trigger
What initiates the micro-interaction.
**User Triggers:**
- Click/tap
- Swipe
- Hover
- Scroll
- Voice command
- Keyboard input
**System Triggers:**
- Time-based (notification)
- Location-based
- Data changes
- Error conditions
#### 2. Rules
What happens once triggered.
```
Example: Like button
1. User taps heart icon
2. Icon changes to filled state
3. Color changes to red
4. Counter increments
5. Animation plays
6. Haptic feedback (mobile)
7. Data sent to server
```
#### 3. Feedback
How the system communicates what's happening.
**Visual:** Color, shape, position changes
**Auditory:** Sounds, voice
**Haptic:** Vibration, force feedback
**Motion:** Animation
#### 4. Loops and Modes
How the interaction changes over time or in different states.
```
Loops:
- First use vs. repeated use
- Decay over time
- Escalation
Modes:
- Light/dark mode
- Editing mode
- Offline mode
```
---
## Feedback States
### Button States
```css
/* Default state */
.button {
background: #0066cc;
color: white;
}
/* Hover state (desktop) */
.button:hover {
background: #0052a3;
}
/* Focus state (keyboard navigation) */
.button:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Active/pressed state */
.button:active {
background: #004080;
transform: scale(0.98);
}
/* Disabled state */
.button:disabled {
background: #cccccc;
cursor: not-allowed;
opacity: 0.7;
}
/* Loading state */
.button.loading {
pointer-events: none;
/* Show spinner */
}
```
### Form Field States
```css
/* Default */
.input { border: 1px solid #ccc; }
/* Focus */
.input:focus { border-color: #0066cc; box-shadow: 0 0 0 3px rgba(0,102,204,0.2); }
/* Valid */
.input:valid { border-color: #28a745; }
/* Invalid */
.input:invalid { border-color: #dc3545; }
/* Disabled */
.input:disabled { background: #f5f5f5; cursor: not-allowed; }
```
### System States
| State | Visual Indicator | User Action |
|-------|-----------------|-------------|
| Loading | Spinner, skeleton, progress bar | Wait |
| Success | Green check, confirmation message | Continue |
| Error | Red alert, error message | Correct and retry |
| Warning | Yellow/orange alert | Proceed with caution |
| Empty | Illustration, helpful message | Add content |
| Offline | Banner, cached indicator | Limited functionality |
---
## Animation Principles
### Timing
```css
/* Natural feeling durations */
.quick { transition-duration: 100ms; } /* Hovers, immediate feedback */
.normal { transition-duration: 200ms; } /* Standard transitions */
.smooth { transition-duration: 300ms; } /* Panel transitions */
.slow { transition-duration: 500ms; } /* Large/complex animations */
/* Maximum for focused attention */
.complex { animation-duration: 500ms; } /* Don't exceed 500ms typically */
```
### Easing Functions
```css
/* Standard easing curves */
.ease-out { transition-timing-function: ease-out; }
/* Fast start, slow end - entering elements */
.ease-in { transition-timing-function: ease-in; }
/* Slow start, fast end - exiting elements */
.ease-in-out { transition-timing-function: ease-in-out; }
/* Smooth start and end - moving elements */
/* Custom bezier curves */
.bounce { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
```
### The 12 Principles of Animation (Disney)
Applied to UI:
1. **Squash and Stretch** - Subtle scaling on press
2. **Anticipation** - Brief pause before action
3. **Staging** - Direct attention to important elements
4. **Straight Ahead/Pose to Pose** - Keyframe animations
5. **Follow Through/Overlapping** - Elements don't stop all at once
6. **Slow In/Slow Out** - Easing functions
7. **Arc** - Natural curved motion paths
8. **Secondary Action** - Supporting animations
9. **Timing** - Speed conveys weight and emotion
10. **Exaggeration** - Subtle emphasis (don't overdo)
11. **Solid Drawing** - 3D consistency
12. **Appeal** - Pleasing, polished animations
---
## Loading States
### Skeleton Screens
```html
<div class="card skeleton">
<div class="skeleton-image"></div>
<div class="skeleton-title"></div>
<div class="skeleton-text"></div>
<div class="skeleton-text short"></div>
</div>
<style>
.skeleton * {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</style>
```
### Progress Indicators
```
Determinate (known duration):
┌──────────────────────────────────────┐
│████████████░░░░░░░░░░░░░░░ 45% │
└──────────────────────────────────────┘
Indeterminate (unknown duration):
┌──────────────────────────────────────┐
│ ████████░░░░░░ │ (moving)
└──────────────────────────────────────┘
```
### Spinner Guidelines
- Show after 300ms delay (avoid flash for fast operations)
- Use on buttons during submission
- Disable interaction during loading
- Provide cancel option for long operations
---
## Gesture Design
### Standard Touch Gestures
| Gesture | Action | Example |
|---------|--------|---------|
| Tap | Select, activate | Button press |
| Double tap | Zoom, like | Photo zoom |
| Long press | Context menu | Delete options |
| Swipe | Navigate, dismiss | Page turn, delete |
| Pinch | Zoom | Map, photo |
| Rotate | Rotate object | Image editing |
| Drag | Move, reorder | List sorting |
### Gesture Best Practices
```
1. Provide visual affordances
- Swipe indicators
- Drag handles
- Touch hints
2. Offer alternatives (WCAG 2.5.7)
- Buttons for swipe actions
- Menus for gestures
- Keyboard equivalents
3. Give feedback
- Haptic response
- Visual confirmation
- Sound (optional)
4. Allow undo
- Swipe to delete → Undo button
- Accidental gestures happen
```
---
## Transitions
### Page Transitions
```css
/* Fade transition */
.page-enter {
opacity: 0;
}
.page-enter-active {
opacity: 1;
transition: opacity 200ms ease-out;
}
.page-exit {
opacity: 1;
}
.page-exit-active {
opacity: 0;
transition: opacity 200ms ease-in;
}
/* Slide transition */
.page-enter {
transform: translateX(100%);
}
.page-enter-active {
transform: translateX(0);
transition: transform 300ms ease-out;
}
```
### Modal Transitions
```css
/* Backdrop */
.backdrop {
opacity: 0;
transition: opacity 200ms ease;
}
.backdrop.open {
opacity: 1;
}
/* Modal */
.modal {
transform: scale(0.95);
opacity: 0;
transition: transform 200ms ease, opacity 200ms ease;
}
.modal.open {
transform: scale(1);
opacity: 1;
}
```
### Accordion/Collapse
```css
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 300ms ease-out;
}
.accordion-content.open {
max-height: 500px; /* Use calculated height for best results */
}
```
---
## Reduced Motion
### Respecting User Preferences
```css
/* Check user preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Alternative: provide reduced but not eliminated motion */
@media (prefers-reduced-motion: reduce) {
.animated {
animation: none;
transition: opacity 0.01ms;
}
}
```
### When to Reduce Motion
- Vestibular disorders
- Motion sickness
- Cognitive load reduction
- Battery/performance concerns
---
## Haptic Feedback
### Mobile Haptics
```javascript
// Web Vibration API
if ('vibrate' in navigator) {
// Single vibration (ms)
navigator.vibrate(50);
// Pattern: vibrate, pause, vibrate
navigator.vibrate([50, 100, 50]);
}
```
### Haptic Patterns
| Action | Pattern | Feel |
|--------|---------|------|
| Success | Short, light | Confirmation |
| Error | Two quick pulses | Alert |
| Selection | Very short | Tactile |
| Warning | Medium, strong | Attention |
---
## 2026 Trends
### Functional Motion (motion earns its keep)
The dominant 2026 framing: motion should **guide, not flash**. Every animation must
do a job—orient the user, show cause and effect, or maintain spatial continuity—
otherwise it's decoration that adds cognitive load. Reserve flourish for moments
that matter (success, first-run delight); keep everyday transitions calm and quick.
- Use motion to **explain state change** (where did this come from / go to?)
- Use motion to **direct attention** to what just changed
- Use motion to **preserve context** across views (shared-element transitions)
- Cut motion that exists only to look impressive—calm interfaces win in 2026
### Giving Users Control Over Motion
`prefers-reduced-motion` (see [Reduced Motion](#reduced-motion) above) is the
baseline, but it's an OS-level all-or-nothing switch many users never set. Mature
products add an **explicit in-product "Reduce motion" toggle** in settings so users
can dial motion down without touching system preferences.
```
Settings → Accessibility → Motion
○ Full motion (default)
● Reduced motion ← respects this even if OS pref is unset
○ Off
```
- Default to the OS preference, then let the in-app setting override it
- Persist the choice per user/device; apply it everywhere, not just key screens
- Treat "reduced" as *reduced* (instant fades) not *broken* (no feedback at all)
### AI-Driven Interactions
- Predictive UI that anticipates needs (offered, never forced)
- Personalized micro-interactions
- Adaptive animation complexity tuned to device performance
### Voice + Multimodal
- Multimodal feedback (voice confirms visual action)
- Combining voice, touch, pointer, and keyboard input
- See [24-voice-and-multimodal.md](24-voice-and-multimodal.md) for depth
---
## Common Mistakes
1. **Too much animation** - Distracting, performance issues
2. **Too fast/slow** - Feels broken or sluggish
3. **No feedback** - Users unsure if action worked
4. **Inconsistent timing** - Jarring experience
5. **Ignoring reduced motion** - Accessibility failure
6. **Blocking content** - Loading spinners over everything
7. **No cancel option** - Trapped in loading state
8. **Overusing haptics** - Annoying, drains battery
---
## Sources
- Saffer, D. (2013). "Microinteractions: Designing with Details"
- Material Design Motion Guidelines
- Apple Human Interface Guidelines - Motion
- [UXDesign.cc - Motion Design](https://uxdesign.cc/)
- [CSS Tricks - Animation](https://css-tricks.com/tag/animation/)
README.md
# UX Designer Skill
A Claude Code skill that provides comprehensive UX/UI design guidance based on modern best practices (2026).
## What It Does
Gives Claude deep knowledge of user experience design principles so it can:
- Design new interfaces and components with proper patterns
- Review existing UI/UX and frontend code for issues
- Apply WCAG 2.2 AA accessibility standards
- Guide interaction design, form design, and navigation
- Advise on mobile-first and responsive approaches
- Write effective UI copy and microcopy
- Design collaborative/multiplayer and canvas-based apps
- Build AI-powered interfaces (chat, copilots, agents)
- Evaluate designs for dark patterns and ethical compliance
## Installation
Copy or symlink the `ux-designer-skill/` directory into your Claude Code skills location:
```
~/.claude/skills/ux-designer/
```
The skill is invocable by both user (`/ux-designer`) and Claude (auto-triggered when UX topics arise).
## Structure
```
ux-designer/
├── SKILL.md # Main skill definition (297 lines)
└── references/ # 24 detailed reference files (~10,700 lines)
├── 01-core-principles.md # Nielsen heuristics, Gestalt, UX hierarchy
├── 02-laws-of-ux.md # Fitts's, Hick's, Miller's, Jakob's, etc.
├── 03-accessibility.md # WCAG 2.2 AA compliance
├── 04-visual-design.md # Typography, color, spacing, hierarchy
├── 05-information-architecture.md # Navigation, sitemaps, card sorting
├── 06-interaction-design.md # Modals, tooltips, drag-and-drop
├── 07-forms-and-inputs.md # Validation, field types, error handling
├── 08-mobile-ux.md # Touch targets, gestures, responsive
├── 09-ux-writing.md # Microcopy, tone, error messages
├── 10-user-research.md # Interviews, usability testing, surveys
├── 11-design-systems.md # Tokens, components, documentation
├── 12a-presence-awareness.md # Live cursors, avatars, typing indicators
├── 12b-conflict-resolution-sync.md # OT/CRDTs, locking, offline sync
├── 13a-canvas-navigation.md # Zoom, pan, selection, manipulation
├── 13b-canvas-objects-performance.md # Layers, snapping, LOD, culling
├── 14-ai-ux-patterns.md # Chat UI, copilots, agents, generative UI
├── 15-ethical-design.md # Dark patterns, consent, GDPR/DSA
├── 16-onboarding.md # First-run, activation, empty states
├── 17-notifications.md # Toasts, badges, push, attention management
├── 18-data-visualization.md # Charts, dashboards, KPIs
├── 19-search-ux.md # Autocomplete, filters, results ranking
├── 20-emotional-design.md # Trust, delight, brand personality
├── 21-data-tables.md # Sorting, pagination, bulk actions, inline edit
└── 22-performance-ux.md # Skeletons, optimistic updates, CLS, lazy loading
```
## SKILL.md Highlights
The main file (always loaded into context) includes:
- **Quick reference checklists** for visual design, interaction, forms, navigation, accessibility, collaboration, canvas, AI, onboarding, notifications, and ethical design
- **Decision trees** for choosing between modal/side panel/full page and notification types
- **Key numbers** grouped by category (layout, interaction, collaboration, AI, engagement)
- **23 anti-patterns** with links to the reference file that shows the correct approach
Reference files are loaded on demand when the topic is relevant, keeping context usage efficient.
## Coverage Areas
| Domain | References |
|--------|-----------|
| Foundations | Core principles, Laws of UX, accessibility, visual design |
| Structure | Information architecture, interaction design, forms |
| Platform | Mobile UX, design systems |
| Content | UX writing, user research |
| Collaboration | Presence/awareness, conflict resolution/sync |
| Canvas/Spatial | Navigation/interaction, objects/performance |
| Modern | AI interfaces, ethical design, emotional design |
| Flows | Onboarding, notifications, search |
| Data | Data visualization, data tables, performance/loading |
## Sources
The skill synthesizes guidance from 19 authoritative sources including Nielsen Norman Group, WCAG 2.2, Material Design, Apple HIG, Laws of UX, Google PAIR, Microsoft HAX Toolkit, Baymard Institute, The A11y Project, and web.dev.
references/08-mobile-ux.md
# Mobile UX Design
Mobile-first design is essential in 2026, with over 60% of web traffic coming from mobile devices.
---
## The Thumb Zone
### Understanding Thumb Reach
Coined by mobile UX expert Steven Hoober, the Thumb Zone defines how users interact with phones using one hand.
```
┌─────────────────────────────┐
│ HARD │ HARD │ ← Top corners: Avoid CTAs
│ │ │
├────────────┼───────────────┤
│ │ │
│ STRETCH │ STRETCH │ ← Middle: Secondary actions
│ │ │
├────────────┼───────────────┤
│ │ │
│ EASY │ EASY │ ← Bottom: Primary actions
│ │ │
└─────────────────────────────┘
```
### Zone Definitions
| Zone | Location | Usage |
|------|----------|-------|
| **Easy** | Bottom center/left | Primary actions, frequent navigation |
| **Stretch** | Middle, far left/right | Secondary actions |
| **Hard** | Top corners | Rarely used actions |
### Modern Device Considerations
- Screen sizes exceed 6.5 inches
- One-handed use dominates
- Bottom navigation 20-30% faster than top
- Foldable devices require flexible layouts
---
## Touch Target Sizes
### Platform Guidelines
| Platform | Minimum Size | Recommended |
|----------|--------------|-------------|
| Apple iOS | 44×44pt | 44-48pt |
| Material Design | 48×48dp | 48dp |
| WCAG 2.2 | 24×24px | 44×44px |
### Implementation
```css
/* Touch-friendly buttons */
.button {
min-width: 44px;
min-height: 44px;
padding: 12px 24px;
}
/* Tap target expansion for small visual elements */
.small-icon {
position: relative;
width: 24px;
height: 24px;
}
.small-icon::before {
content: '';
position: absolute;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
/* Invisible but tappable */
}
/* Adequate spacing between targets */
.button-group .button + .button {
margin-left: 8px; /* Minimum 8px */
}
```
### Touch vs. Click Considerations
```css
/* Remove touch delay on mobile */
touch-action: manipulation;
/* Prevent accidental double-tap zoom */
html {
touch-action: manipulation;
}
```
---
## Mobile Navigation Patterns
### Bottom Navigation Bar
```html
<nav class="bottom-nav" role="navigation">
<a href="/" class="nav-item active">
<span class="icon">🏠</span>
<span class="label">Home</span>
</a>
<a href="/search" class="nav-item">
<span class="icon">🔍</span>
<span class="label">Search</span>
</a>
<a href="/add" class="nav-item">
<span class="icon">➕</span>
<span class="label">Add</span>
</a>
<a href="/saved" class="nav-item">
<span class="icon">❤️</span>
<span class="label">Saved</span>
</a>
<a href="/profile" class="nav-item">
<span class="icon">👤</span>
<span class="label">Profile</span>
</a>
</nav>
<style>
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
background: white;
padding: 8px 0;
padding-bottom: env(safe-area-inset-bottom); /* iPhone notch */
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
min-width: 64px;
padding: 8px;
}
</style>
```
**Best Practices:**
- 3-5 items maximum
- Icons with labels (not icons alone)
- Clear active state
- Thumb-reachable position
- Consider safe areas for notched devices
### Hamburger Menu
```html
<button class="menu-toggle" aria-expanded="false" aria-controls="menu">
<span class="sr-only">Menu</span>
<span class="hamburger-icon"></span>
</button>
<nav id="menu" class="slide-menu" aria-hidden="true">
<!-- Menu content -->
</nav>
```
**When to Use:**
- Many navigation items (6+)
- Secondary/utility navigation
- Space-constrained headers
**When to Avoid:**
- Primary navigation (use bottom bar)
- When discoverability is crucial
- E-commerce main categories
### Floating Action Button (FAB)
```css
.fab {
position: fixed;
bottom: 80px; /* Above bottom nav */
right: 16px;
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--accent);
color: white;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
display: flex;
align-items: center;
justify-content: center;
}
```
**Use For:**
- Single primary action (create, compose, add)
- Actions available on every screen
- Thumb-friendly access
---
## Responsive Gestures
### Standard Mobile Gestures
```
Tap → Select, activate
Double tap → Zoom, like
Long press → Context menu, drag start
Swipe → Navigate, dismiss, reveal actions
Pinch → Zoom in/out
Pull down → Refresh
Edge swipe → Back navigation
```
### Swipe Actions
```html
<!-- Swipe-to-reveal actions -->
<div class="list-item swipeable">
<div class="content">Item content</div>
<div class="actions hidden">
<button class="action-edit">Edit</button>
<button class="action-delete">Delete</button>
</div>
</div>
<!-- Always provide alternatives (WCAG 2.5.7) -->
<button class="action-menu">•••</button>
```
### Pull-to-Refresh
```css
.pull-indicator {
height: 0;
transition: height 200ms;
overflow: hidden;
}
.pulling .pull-indicator {
height: 60px;
}
.refreshing .pull-indicator .spinner {
animation: spin 1s linear infinite;
}
```
---
## Mobile Forms
### Keyboard Optimization
```html
<!-- Appropriate keyboard types -->
<input type="email" inputmode="email" />
<input type="tel" inputmode="tel" />
<input type="number" inputmode="decimal" />
<input type="text" inputmode="numeric" pattern="[0-9]*" />
<!-- Prevent zoom on focus (iOS) -->
<input type="text" style="font-size: 16px;" />
```
### Form Layout
```css
/* Stack labels above inputs */
.form-field {
display: flex;
flex-direction: column;
margin-bottom: 16px;
}
/* Full-width inputs */
.form-field input {
width: 100%;
padding: 12px;
font-size: 16px; /* Prevent zoom */
}
/* Sticky submit button */
.submit-area {
position: sticky;
bottom: 0;
background: white;
padding: 16px;
padding-bottom: calc(16px + env(safe-area-inset-bottom));
}
```
### Mobile-Specific Patterns
```html
<!-- Date picker alternatives -->
<select name="month">
<option>January</option>
<!-- ... -->
</select>
<!-- Stepper for quantities -->
<div class="stepper">
<button>−</button>
<input type="number" value="1" />
<button>+</button>
</div>
<!-- Toggle instead of checkbox -->
<label class="toggle">
<input type="checkbox" />
<span class="toggle-slider"></span>
Enable notifications
</label>
```
---
## Performance on Mobile
### Key Metrics
| Metric | Target | Impact |
|--------|--------|--------|
| First Contentful Paint | < 1.8s | User perceives loading |
| Largest Contentful Paint | < 2.5s | Main content visible |
| Time to Interactive | < 3.8s | Users can interact |
| Cumulative Layout Shift | < 0.1 | Visual stability |
### Mobile Performance Tips
```html
<!-- Lazy load images -->
<img loading="lazy" src="image.jpg" alt="" />
<!-- Preload critical resources -->
<link rel="preload" href="font.woff2" as="font" crossorigin />
<!-- Reduce JavaScript -->
<script defer src="app.js"></script>
```
```css
/* Optimize animations */
.animated {
transform: translateZ(0);
will-change: transform;
}
/* Reduce motion for performance/preference */
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}
```
---
## Touch Feedback
### Response Time
- **< 100ms** - Perceived as instant
- **100-300ms** - Noticeable but acceptable
- **> 300ms** - Feels sluggish
### Visual Feedback
```css
/* Touch feedback */
.button:active {
transform: scale(0.98);
opacity: 0.9;
}
/* Ripple effect (Material) */
.ripple {
position: relative;
overflow: hidden;
}
.ripple::after {
content: '';
position: absolute;
background: rgba(255,255,255,0.3);
border-radius: 50%;
transform: scale(0);
animation: ripple 0.6s linear;
}
```
### Haptic Feedback
```javascript
// Simple vibration
if ('vibrate' in navigator) {
navigator.vibrate(10); // Light tap
}
// Success pattern
navigator.vibrate([10, 50, 10]);
// Error pattern
navigator.vibrate([50, 50, 50]);
```
---
## Safe Areas
### Handling Device Notches
```css
/* iPhone notch and home indicator */
.container {
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
}
/* Fixed bottom elements */
.bottom-bar {
padding-bottom: calc(16px + env(safe-area-inset-bottom));
}
/* Enable safe area support */
<meta name="viewport" content="viewport-fit=cover" />
```
---
## Orientation and Adaptation
### Responsive Layouts
```css
/* Portrait (default) */
.container {
flex-direction: column;
}
/* Landscape */
@media (orientation: landscape) {
.container {
flex-direction: row;
}
/* Hide bottom nav, show sidebar */
.bottom-nav {
display: none;
}
.sidebar-nav {
display: block;
}
}
```
### Foldable Devices
```css
/* Detect foldable screen */
@media (horizontal-viewport-segments: 2) {
/* Dual-screen layout */
.main {
display: grid;
grid-template-columns: 1fr 1fr;
gap: env(viewport-segment-width 0 0);
}
}
```
---
## Common Mobile Mistakes
1. **Touch targets too small** - Use 44px minimum
2. **Important actions at top** - Place in thumb zone
3. **Too much content** - Prioritize ruthlessly
4. **Desktop forms on mobile** - Optimize for touch
5. **Ignoring safe areas** - Content hidden by notches
6. **No offline support** - Users lose connectivity
7. **Heavy animations** - Drains battery, causes jank
8. **Zoom disabled** - Accessibility violation
9. **Horizontal scrolling** - Confusing navigation
10. **Pop-ups covering content** - Interstitials annoy users
---
## Testing Checklist
- [ ] Test on real devices (not just simulators)
- [ ] Check various screen sizes
- [ ] Test in portrait and landscape
- [ ] Verify touch targets are large enough
- [ ] Test with slow network (3G)
- [ ] Check offline behavior
- [ ] Verify safe area handling
- [ ] Test form keyboard interaction
- [ ] Check gesture conflicts
- [ ] Verify haptic feedback works
---
## Sources
- [Material Design - Mobile Guidelines](https://m3.material.io/)
- [Apple HIG - iOS](https://developer.apple.com/design/human-interface-guidelines/)
- [Nielsen Norman - Mobile UX](https://www.nngroup.com/topic/mobile-usability/)
- Hoober, S. (2017). "Touch Bigger"
- [Google Web Fundamentals - Mobile](https://developers.google.com/web/fundamentals)
references/02-laws-of-ux.md
# Laws of UX
Psychological principles that inform user experience design, collected and popularized by Jon Yablonski.
---
## Fitts's Law
> The time to acquire a target is a function of the distance to and size of the target.
**Formulated by:** Paul Fitts (1954)
### Key Implications
1. **Make important targets large** - Buttons, links, and interactive elements should be big enough to click/tap easily
2. **Reduce distance to targets** - Place frequently used actions near common cursor/finger positions
3. **Use screen edges and corners** - These are effectively infinite in size (cursor stops at edge)
### Application in UX
```
Good:
- Large, prominent CTAs
- Sticky navigation bars
- Context menus near cursor
- Floating action buttons on mobile
Avoid:
- Tiny click targets
- Important actions far from content
- Small close buttons on modals
```
### Minimum Target Sizes
- **Touch (mobile):** 44×44px (Apple) / 48×48dp (Material Design)
- **Mouse (desktop):** 24×24px minimum, 44×44px recommended
- **Spacing between targets:** 8px minimum
---
## Hick's Law (Hick-Hyman Law)
> The time it takes to make a decision increases with the number and complexity of choices.
**Formulated by:** William Edmund Hick & Ray Hyman (1952)
### Key Implications
1. **Limit choices** - Fewer options = faster decisions
2. **Group and categorize** - Break complex choices into simpler steps
3. **Highlight recommendations** - Guide users toward optimal choices
4. **Progressive disclosure** - Show only necessary options initially
### Application in UX
```
Good:
- "Top 10" lists on Netflix
- 3-4 pricing tiers
- Stepped forms/wizards
- Clear default selections
Avoid:
- Overwhelming dropdown menus
- Too many navigation items
- Endless feature lists
- Choice paralysis situations
```
### The Magic Number
Navigation menus should typically have **7±2 items** at the top level, though research suggests **4-5 items** may be optimal for quick scanning.
---
## Miller's Law
> The average person can only keep about 7 (±2) items in their working memory.
**Formulated by:** George A. Miller (1956)
### Key Implications
1. **Chunk information** - Group related items into meaningful units
2. **Limit simultaneous elements** - Don't overwhelm working memory
3. **Use recognizable patterns** - Familiar structures reduce memory load
4. **Provide external memory aids** - Let the interface remember for users
### Application in UX
```
Good:
- Phone numbers: (555) 123-4567
- Credit cards: 1234 5678 9012 3456
- Step indicators: Step 2 of 4
- Breadcrumb navigation
Avoid:
- Long strings of numbers/codes
- Too many form fields visible at once
- Complex multi-step processes without progress indication
```
---
## Jakob's Law
> Users spend most of their time on other sites. This means that users prefer your site to work the same way as all the other sites they already know.
**Named after:** Jakob Nielsen
### Key Implications
1. **Follow conventions** - Use established patterns users expect
2. **Don't reinvent the wheel** - Standard UI patterns exist for good reason
3. **Meet expectations** - Cart icon, hamburger menu, search bar placement
4. **Innovate carefully** - Novel interactions require learning investment
### Application in UX
```
Conventional Patterns:
- Logo in top-left links to home
- Shopping cart in top-right
- Search bar in header
- Footer contains legal/contact links
- Underlined text = hyperlink
- Blue links (or clearly styled alternatives)
Risky Innovations:
- Non-standard navigation placement
- Unusual scroll behaviors
- Custom cursor styles
- Non-standard form controls
```
---
## Peak-End Rule
> People judge an experience largely based on how they felt at its most intense point (peak) and at its end, rather than based on the sum of every moment.
**Formulated by:** Daniel Kahneman
### Key Implications
1. **Design memorable moments** - Create positive peaks in the experience
2. **End on a high note** - Final impressions disproportionately matter
3. **Manage low points** - Minimize negative peaks in user journeys
4. **Focus on key moments** - Not every interaction needs equal polish
### Application in UX
```
Positive Peaks:
- Delightful animations on success
- Personalized welcome messages
- Achievement celebrations
- Surprise and delight moments
Strong Endings:
- Clear confirmation of completed tasks
- Helpful "what's next" suggestions
- Thank you messages
- Easy save/export of results
```
---
## Tesler's Law (Law of Conservation of Complexity)
> For any system there is a certain amount of complexity which cannot be reduced. The question is: who deals with it—the user or the designer?
**Formulated by:** Larry Tesler
### Key Implications
1. **Absorb complexity** - Move complexity from users to the system
2. **Don't oversimplify** - Some complexity is inherent and necessary
3. **Smart defaults** - Handle common cases automatically
4. **Progressive disclosure** - Hide complexity until needed
### Application in UX
```
System absorbs complexity:
- Auto-detecting location
- Pre-filling forms with known data
- Smart suggestions and autocomplete
- Automatic formatting (dates, phone numbers)
- Intelligent defaults
Don't remove necessary complexity:
- Tax filing requires certain information
- Medical forms need complete details
- Security requires authentication steps
```
---
## Gestalt Principles
Principles of visual perception that explain how humans group visual elements.
### Proximity
Elements close together are perceived as related.
```
Use for:
- Grouping form fields
- Organizing navigation
- Creating visual sections
- Associating labels with inputs
```
### Similarity
Elements that look similar are perceived as related.
```
Use for:
- Consistent button styles
- Icon families
- Color coding categories
- Typography indicating function
```
### Continuity
Elements arranged in a line or curve are perceived as related.
```
Use for:
- Progress indicators
- Timelines
- Navigation flows
- Reading direction
```
### Closure
The mind fills in missing information to complete shapes.
```
Use for:
- Minimalist icons
- Loading indicators
- Implied boundaries
- Logo design
```
### Common Region
Elements within a boundary are perceived as grouped.
```
Use for:
- Card designs
- Form sections
- Modal dialogs
- Feature groupings
```
### Figure-Ground
The mind separates foreground from background.
```
Use for:
- Modal overlays
- Focus states
- Dropdown menus
- Image/text relationships
```
---
## Additional Important Laws
### Aesthetic-Usability Effect
Users often perceive aesthetically pleasing designs as more usable, even when they're not.
### Doherty Threshold
Productivity increases when computer and user interact at a pace (< 400ms) that ensures neither has to wait on the other.
### Law of Common Region
Elements tend to be perceived as groups if they share an enclosed area.
### Law of Prägnanz
People interpret ambiguous images as simple and complete.
### Mere Exposure Effect
The more exposure people have to something, the more they prefer it.
### Occam's Razor
Among competing hypotheses, the one with the fewest assumptions should be selected.
### Pareto Principle (80/20 Rule)
Roughly 80% of effects come from 20% of causes. Focus on the vital few features/screens.
### Postel's Law
Be liberal in what you accept and conservative in what you send.
### Serial Position Effect
Users best remember the first and last items in a series.
### Von Restorff Effect (Isolation Effect)
Items that stand out are more likely to be remembered.
### Zeigarnik Effect
People remember uncompleted tasks better than completed ones.
---
## Sources
- Yablonski, J. (2020). "Laws of UX: Using Psychology to Design Better Products & Services"
- [lawsofux.com](https://lawsofux.com/)
- Nielsen Norman Group research
- Kahneman, D. (2011). "Thinking, Fast and Slow"
references/03-accessibility.md
# Accessibility & WCAG 2.2
Accessibility ensures digital products are usable by people with disabilities. Beyond ethics and inclusion, it's increasingly a legal requirement.
---
## WCAG 2.2 Overview
**Web Content Accessibility Guidelines 2.2** is the latest W3C recommendation (October 2023, updated December 2024), now also an ISO standard (ISO/IEC 40500:2025).
### Conformance Levels
| Level | Description | Requirement |
|-------|-------------|-------------|
| **A** | Minimum accessibility | Essential barriers removed |
| **AA** | Recommended standard | Most common target for compliance |
| **AAA** | Highest accessibility | Maximum inclusion (not always feasible) |
### Four Principles (POUR)
1. **Perceivable** - Information must be presentable in ways users can perceive
2. **Operable** - Interface components must be operable
3. **Understandable** - Information and operation must be understandable
4. **Robust** - Content must be robust enough for various assistive technologies
---
## New Success Criteria in WCAG 2.2
### Focus Not Obscured (Minimum) - 2.4.11 (AA)
When an element receives keyboard focus, it is not entirely hidden by other content.
```css
/* Ensure focused elements aren't covered */
.modal {
/* Don't cover the element that triggered focus */
}
/* Sticky headers should not cover focused content */
.sticky-header {
/* Account for header height in scroll calculations */
}
```
### Focus Not Obscured (Enhanced) - 2.4.12 (AAA)
When focused, no part of the element is hidden by author-created content.
### Focus Appearance - 2.4.13 (AAA)
Focus indicators must have sufficient size and contrast.
### Dragging Movements - 2.5.7 (AA)
Functionality using dragging can be operated with a single pointer without dragging.
```html
<!-- Provide alternative to drag-and-drop -->
<div draggable="true">Item</div>
<button>Move Up</button>
<button>Move Down</button>
```
### Target Size (Minimum) - 2.5.8 (AA)
Touch targets must be at least 24×24 CSS pixels, with exceptions for inline links, user agent controls, and essential presentations.
### Accessible Authentication (Minimum) - 3.3.8 (AA)
Authentication processes don't require cognitive function tests (like remembering passwords) unless alternatives are provided.
```
Compliant methods:
- Password managers (paste allowed)
- Email/SMS verification codes
- Biometric authentication
- OAuth/social login
```
### Accessible Authentication (Enhanced) - 3.3.9 (AAA)
No cognitive test required at all for authentication.
### Redundant Entry - 3.3.7 (A)
Don't require users to re-enter information already provided in the same session.
---
## Color and Contrast
### Contrast Requirements
| Content Type | Minimum Ratio (AA) | Enhanced (AAA) |
|--------------|-------------------|----------------|
| Normal text (< 18pt) | 4.5:1 | 7:1 |
| Large text (≥ 18pt or 14pt bold) | 3:1 | 4.5:1 |
| UI components & graphics | 3:1 | - |
| Focus indicators | 3:1 | - |
### Testing Contrast
```javascript
// Calculate contrast ratio
function getContrastRatio(l1, l2) {
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
```
**Tools:**
- WebAIM Contrast Checker
- Stark (Figma plugin)
- Chrome DevTools accessibility panel
- axe DevTools
### Color Blindness
Approximately 8% of men and 0.5% of women have some form of color vision deficiency.
```
Never rely on color alone to convey:
- Error states (add icons, text)
- Success/failure (add icons, patterns)
- Required fields (add asterisks)
- Link differentiation (add underline)
- Data visualization (add patterns, labels)
```
---
## Keyboard Navigation
### Requirements
All functionality must be accessible via keyboard.
```html
<!-- Ensure logical tab order -->
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<!-- Use tabindex appropriately -->
<div tabindex="0" role="button">Custom Button</div>
<div tabindex="-1">Programmatically focusable only</div>
<!-- Never use tabindex > 0 -->
```
### Focus Management
```css
/* Never hide focus outlines without replacement */
:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* For mouse users who don't need visible focus */
:focus:not(:focus-visible) {
outline: none;
}
:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
```
### Keyboard Shortcuts
```
Standard expectations:
- Tab: Move to next focusable element
- Shift+Tab: Move to previous element
- Enter/Space: Activate buttons
- Arrow keys: Navigate within widgets
- Escape: Close modals/popups
```
---
## Screen Reader Compatibility
### Semantic HTML
```html
<!-- Use semantic elements -->
<header>...</header>
<nav>...</nav>
<main>...</main>
<article>...</article>
<aside>...</aside>
<footer>...</footer>
<!-- Not divs with roles -->
<div role="navigation">...</div> <!-- Less ideal -->
```
### ARIA (Accessible Rich Internet Applications)
```html
<!-- Labels -->
<button aria-label="Close dialog">×</button>
<!-- Descriptions -->
<input aria-describedby="password-hint" />
<p id="password-hint">Must be at least 8 characters</p>
<!-- States -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" aria-hidden="true">...</ul>
<!-- Live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true">
<!-- Updates announced to screen readers -->
</div>
```
### Image Accessibility
```html
<!-- Informative images -->
<img src="chart.png" alt="Sales increased 20% in Q4 2024" />
<!-- Decorative images -->
<img src="divider.png" alt="" role="presentation" />
<!-- Complex images -->
<figure>
<img src="diagram.png" alt="System architecture diagram" />
<figcaption>
Full description of the system architecture...
</figcaption>
</figure>
```
---
## Forms Accessibility
```html
<form>
<!-- Always associate labels -->
<label for="email">Email address</label>
<input type="email" id="email" name="email"
aria-required="true"
aria-describedby="email-error" />
<span id="email-error" role="alert">
Please enter a valid email address
</span>
<!-- Group related fields -->
<fieldset>
<legend>Shipping Address</legend>
<!-- Address fields -->
</fieldset>
<!-- Required field indication -->
<label for="name">
Name <span aria-hidden="true">*</span>
<span class="sr-only">(required)</span>
</label>
</form>
```
### Error Handling
```html
<!-- Announce errors -->
<div role="alert" aria-live="assertive">
Please correct the following errors:
<ul>
<li><a href="#email">Email is required</a></li>
</ul>
</div>
```
---
## Motion and Animation
### Respecting User Preferences
```css
/* Reduce motion for users who prefer it */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Or provide opt-out */
.animation {
animation: slide-in 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
.animation {
animation: none;
}
}
```
### Auto-Playing Content
- Never auto-play video with sound
- Provide pause/stop controls for any auto-playing content
- Avoid flashing content (> 3 flashes per second can trigger seizures)
---
## Touch Accessibility
### Target Sizes (WCAG 2.2)
```css
/* Minimum 24×24px for WCAG 2.2 AA */
/* Recommended 44×44px for comfortable tapping */
.button {
min-width: 44px;
min-height: 44px;
padding: 12px 24px;
}
/* Adequate spacing between targets */
.button + .button {
margin-left: 8px;
}
```
### Touch Alternatives
```html
<!-- Provide alternatives to complex gestures -->
<div class="carousel">
<button aria-label="Previous slide">←</button>
<!-- Content (also swipeable) -->
<button aria-label="Next slide">→</button>
</div>
```
---
## Legal Requirements
Accessibility is now a hard legal obligation in major markets, not just a best
practice. The headline change is the European Accessibility Act.
### European Accessibility Act (EAA)
- **In force since:** June 28, 2025 — enforcement is active now
- **Applies to:** A broad set of digital products and services sold in the EU —
e-commerce, banking, e-books, ticketing, transport, electronic communications,
and consumer hardware/OS. **Reach is extraterritorial:** non-EU businesses
(incl. US/UK companies) that sell to EU customers must comply.
- **Technical baseline:** **EN 301 549**, the harmonized EU standard, which
references **WCAG 2.1 Level AA** as its web/app floor (and adds requirements
beyond WCAG, e.g. for hardware and documentation).
- **Transition end:** June 28, 2030 — a limited grace window for certain
pre-existing service contracts and self-service terminals already in use.
- **Practical takeaway:** Targeting **WCAG 2.2 AA** (covered above) meets and
exceeds the EAA's WCAG 2.1 AA floor, so a WCAG 2.2 AA product is well-positioned
for compliance. Verify the EN 301 549 non-web clauses if you ship hardware,
documents, or support channels.
### Other Regulations
- **US:** ADA (case law increasingly treats websites as places of public
accommodation), Section 508 (federal procurement, aligned to WCAG 2.0 AA)
- **EU:** Web Accessibility Directive (public-sector sites/apps, EN 301 549)
- **UK:** Equality Act 2010
- **Canada:** AODA, ACA
- **Australia:** DDA
---
## Testing Checklist
### Automated Testing
- [ ] axe DevTools
- [ ] WAVE
- [ ] Lighthouse
- [ ] Pa11y
### Manual Testing
- [ ] Keyboard-only navigation
- [ ] Screen reader testing (NVDA, VoiceOver, JAWS)
- [ ] 200% zoom level
- [ ] High contrast mode
- [ ] Reduced motion preference
### User Testing
- [ ] Include users with disabilities
- [ ] Test with assistive technologies
- [ ] Gather feedback on pain points
---
## Sources
- [WCAG 2.2](https://www.w3.org/TR/WCAG22/)
- [WAI-ARIA 1.2](https://www.w3.org/TR/wai-aria-1.2/)
- [European Accessibility Act](https://ec.europa.eu/social/main.jsp?catId=1202) - EU directive 2019/882
- [EN 301 549](https://www.etsi.org/standards) - Harmonized EU accessibility standard
- [WebAIM](https://webaim.org/)
- [A11y Project](https://www.a11yproject.com/)
- [Deque University](https://dequeuniversity.com/)
references/01-core-principles.md
# Core UX Principles & Usability Heuristics
## Nielsen's 10 Usability Heuristics
Jakob Nielsen's heuristics are fundamental principles for interaction design, refined over decades of usability research.
### 1. Visibility of System Status
The design should always keep users informed about what is going on, through appropriate feedback within a reasonable amount of time.
**Implementation:**
- Show loading states and progress indicators
- Confirm successful actions (saves, submissions)
- Display current location in navigation
- Show real-time status of processes
### 2. Match Between System and the Real World
The design should speak the users' language. Use words, phrases, and concepts familiar to the user, rather than internal jargon.
**Implementation:**
- Use plain language, not technical terms
- Follow real-world conventions (calendar layouts, folder metaphors)
- Order information logically (chronological, alphabetical)
- Use familiar icons and symbols
### 3. User Control and Freedom
Users often perform actions by mistake. They need a clearly marked "emergency exit" to leave the unwanted action without having to go through an extended process.
**Implementation:**
- Provide undo/redo functionality
- Include clear cancel buttons
- Allow easy navigation back
- Support exiting flows at any point
- Auto-save work when possible
### 4. Consistency and Standards
Users should not have to wonder whether different words, situations, or actions mean the same thing. Follow platform and industry conventions.
**Implementation:**
- Use consistent terminology throughout
- Maintain uniform visual design (colors, fonts, spacing)
- Follow platform conventions (iOS, Android, Web)
- Reuse the same components for similar functions
### 5. Error Prevention
Good error messages are important, but the best designs carefully prevent problems from occurring in the first place.
**Implementation:**
- Use constraints to prevent invalid input
- Provide confirmation dialogs for destructive actions
- Offer suggestions and autocomplete
- Disable unavailable options rather than showing errors after
- Use sensible defaults
### 6. Recognition Rather Than Recall
Minimize the user's memory load by making elements, actions, and options visible. Users should not have to remember information from one part of the interface to another.
**Implementation:**
- Show recently used items
- Provide contextual help and hints
- Display options visibly instead of requiring memorization
- Use descriptive labels, not codes
- Maintain visible navigation breadcrumbs
### 7. Flexibility and Efficiency of Use
Shortcuts—hidden from novice users—can speed up the interaction for expert users. Allow users to tailor frequent actions.
**Implementation:**
- Provide keyboard shortcuts
- Allow customization of interface
- Support both simple and advanced modes
- Remember user preferences
- Enable batch operations
### 8. Aesthetic and Minimalist Design
Interfaces should not contain information which is irrelevant or rarely needed. Every extra unit of information competes with relevant information and diminishes visibility.
**Implementation:**
- Remove unnecessary elements
- Use progressive disclosure
- Prioritize important content
- Use whitespace effectively
- Avoid decorative clutter
### 9. Help Users Recognize, Diagnose, and Recover from Errors
Error messages should be expressed in plain language (no error codes), precisely indicate the problem, and constructively suggest a solution.
**Implementation:**
- Write human-readable error messages
- Explain what went wrong specifically
- Suggest how to fix the problem
- Avoid technical jargon and error codes
- Place errors near the relevant field
### 10. Help and Documentation
It's best if the system doesn't need any additional explanation. However, it may be necessary to provide documentation to help users complete their tasks.
**Implementation:**
- Provide contextual help (tooltips, inline hints)
- Make help searchable
- Focus documentation on user tasks
- List concrete steps
- Keep help concise and scannable
---
## Core Design Principles
### Clarity
Users should never have to stop and think about what something means or how to use it.
- Use clear, descriptive labels
- Avoid ambiguous icons without labels
- Make clickable elements look clickable
- Distinguish between primary and secondary actions
### Simplicity
Remove everything that isn't essential to the user's goal.
- Start with the minimum viable interface
- Add complexity only when proven necessary
- Hide advanced features behind progressive disclosure
- One primary action per screen
### Consistency
Predictability reduces cognitive load and learning time.
- Visual consistency (colors, typography, spacing)
- Functional consistency (same gestures = same results)
- Internal consistency (within your product)
- External consistency (with platform conventions)
### Feedback
Every user action should have an immediate, visible response.
- Instant feedback (< 100ms) for direct manipulation
- Progress indicators for operations > 1 second
- Clear success and error states
- Confirmation for important actions
### Hierarchy
Guide users' attention to what matters most.
- Visual weight indicates importance
- Size, color, and position create hierarchy
- Group related items together
- Separate distinct sections clearly
---
## User-Centered Design Fundamentals
### The UX Design Process
1. **Research** - Understand users, their goals, and context
2. **Define** - Synthesize findings into problems to solve
3. **Ideate** - Generate multiple possible solutions
4. **Prototype** - Create testable representations
5. **Test** - Validate with real users
6. **Implement** - Build the solution
7. **Iterate** - Continuously improve based on feedback
### Understanding Users
**User Research Methods:**
- User interviews
- Contextual inquiry
- Surveys and questionnaires
- Analytics analysis
- Usability testing
- A/B testing
**Key Questions:**
- Who are the users?
- What are their goals?
- What are their pain points?
- What is their context of use?
- What do they already know?
### Mental Models
Users approach your interface with expectations based on prior experience. Design should align with these mental models.
- Research how users think about the domain
- Use familiar patterns and metaphors
- When introducing new concepts, bridge from known ones
- Test assumptions about user understanding
---
## Visual Hierarchy Techniques
### Size
Larger elements attract more attention. Use size to indicate importance.
```
Heading 1 (32-48px) - Most important
Heading 2 (24-32px) - Section titles
Heading 3 (18-24px) - Subsections
Body text (16px) - Content
Small text (12-14px) - Metadata, captions
```
### Color
Color draws attention and conveys meaning.
- Use accent colors sparingly for important elements
- Maintain sufficient contrast (4.5:1 minimum)
- Don't rely on color alone to convey information
- Consider cultural color associations
### Position
Eye-tracking studies show predictable scanning patterns.
- F-pattern for text-heavy pages
- Z-pattern for minimal content
- Top-left receives most attention (LTR languages)
- Place critical elements in natural focal points
### Contrast
Difference creates emphasis.
- High contrast for important elements
- Low contrast for secondary information
- Use contrast in typography (bold, italic, size)
- Combine multiple contrast techniques
### Whitespace
Empty space is a design element, not wasted space.
- Creates breathing room
- Groups related elements
- Separates distinct sections
- Improves readability and comprehension
---
## Sources
- Nielsen, J. (1994, 2020). "10 Usability Heuristics for User Interface Design"
- Norman, D. (2013). "The Design of Everyday Things"
- Krug, S. (2014). "Don't Make Me Think, Revisited"
- Nielsen Norman Group research articles
references/04-visual-design.md
# Visual Design
Visual design encompasses color theory, typography, layout, and the principles that make interfaces aesthetically pleasing and functional.
---
## Color Theory
### Color Psychology
Colors evoke specific emotions and influence user behavior.
| Color | Associations | Common Uses |
|-------|--------------|-------------|
| **Red** | Energy, urgency, passion, danger | Errors, sales, CTAs |
| **Orange** | Enthusiasm, creativity, warmth | Notifications, CTAs |
| **Yellow** | Optimism, warning, attention | Warnings, highlights |
| **Green** | Growth, success, nature, calm | Success states, eco themes |
| **Blue** | Trust, stability, professionalism | Corporate, tech, links |
| **Purple** | Luxury, creativity, mystery | Premium products |
| **Pink** | Playful, romantic, youthful | Fashion, lifestyle |
| **Black** | Sophistication, power, elegance | Luxury, fashion |
| **White** | Purity, simplicity, cleanliness | Minimalist designs |
| **Gray** | Neutral, professional, balanced | Backgrounds, borders |
### The 60-30-10 Rule
A balanced color palette distribution:
```
60% - Dominant color (backgrounds, large areas)
30% - Secondary color (supporting elements)
10% - Accent color (CTAs, highlights)
```
**Example:**
```css
:root {
--color-dominant: #ffffff; /* 60% - Background */
--color-secondary: #f5f5f5; /* 30% - Cards, sections */
--color-accent: #0066cc; /* 10% - Buttons, links */
}
```
### Color Harmonies
| Harmony | Description | Use Case |
|---------|-------------|----------|
| **Monochromatic** | Shades of one hue | Clean, cohesive |
| **Complementary** | Opposite on color wheel | High contrast, vibrant |
| **Analogous** | Adjacent on wheel | Harmonious, natural |
| **Triadic** | Three equidistant colors | Balanced, dynamic |
| **Split-complementary** | Base + two adjacent to complement | Vibrant but balanced |
### Contrast for Accessibility
```css
/* WCAG 2.2 Requirements */
/* Normal text (< 18pt) */
/* Minimum contrast: 4.5:1 (AA), 7:1 (AAA) */
/* Large text (≥ 18pt or 14pt bold) */
/* Minimum contrast: 3:1 (AA), 4.5:1 (AAA) */
/* UI Components */
/* Minimum contrast: 3:1 */
```
### Colorblind-Safe Design
~8% of men and ~0.5% of women have color vision deficiency.
```
Best Practices:
- Don't use color alone to convey meaning
- Add icons, patterns, or text labels
- Test with colorblind simulation tools
- Use sufficient contrast between colors
- Avoid red/green combinations for critical info
```
### Dark Mode Considerations
```css
/* Use CSS custom properties for theming */
:root {
--bg-primary: #ffffff;
--text-primary: #1a1a1a;
--accent: #0066cc;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--text-primary: #f5f5f5;
--accent: #66b3ff;
}
}
/* Reduce contrast slightly in dark mode */
/* Pure white (#fff) on black (#000) can cause eye strain */
```
---
## Typography
### Font Selection
**Categories:**
- **Sans-serif:** Clean, modern, screen-friendly (Roboto, Inter, SF Pro)
- **Serif:** Traditional, authoritative, editorial (Georgia, Merriweather)
- **Monospace:** Technical, code, data (Fira Code, JetBrains Mono)
**Guidelines:**
- Limit to 2-3 typefaces maximum
- Use web-safe fonts or system fonts for performance
- Consider font loading strategy (FOUT, FOIT)
```css
/* System font stack for performance */
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
```
### Size Scale
```css
/* Type scale with 1.25 ratio (Major Third) */
:root {
--text-xs: 0.75rem; /* 12px - Captions */
--text-sm: 0.875rem; /* 14px - Small text */
--text-base: 1rem; /* 16px - Body (minimum) */
--text-lg: 1.25rem; /* 20px - Large body */
--text-xl: 1.5rem; /* 24px - H3 */
--text-2xl: 2rem; /* 32px - H2 */
--text-3xl: 2.5rem; /* 40px - H1 */
--text-4xl: 3rem; /* 48px - Display */
}
```
**Minimum sizes:**
- Body text: 16px
- Mobile body: 16px (prevents zoom on iOS)
- Small text: 12px (use sparingly)
### Line Height
```css
/* Optimal line heights */
body {
line-height: 1.5; /* 1.4-1.6 for body text */
}
h1, h2, h3 {
line-height: 1.2; /* Tighter for headings */
}
.compact {
line-height: 1.3; /* UI elements, buttons */
}
```
### Line Length
Optimal reading: **50-75 characters per line**
```css
/* Constrain line length */
.prose {
max-width: 65ch; /* ~65 characters */
}
/* Alternatively */
.content {
max-width: 700px;
padding: 0 1rem;
}
```
### Text Alignment
```
Left-aligned (default):
- Best for readability
- Consistent starting point
- Recommended for body text
Center-aligned:
- Short text only (headlines, CTAs)
- Creates visual anchor
Right-aligned:
- Numbers in tables
- RTL languages
- Avoid for body text
Justified:
- Creates uneven word spacing
- Avoid for web (no hyphenation)
- WCAG discourages for accessibility
```
### Fluid Typography
```css
/* Responsive font sizes with clamp() */
h1 {
font-size: clamp(2rem, 5vw + 1rem, 4rem);
}
body {
font-size: clamp(1rem, 2.5vw, 1.25rem);
}
```
---
## Visual Hierarchy
### Establishing Hierarchy
1. **Size** - Larger = more important
2. **Weight** - Bolder = more important
3. **Color** - Contrast draws attention
4. **Position** - Top/left (LTR) = primary
5. **Space** - Isolation = emphasis
6. **Depth** - Shadows, layers
### Heading Hierarchy
```html
<!-- Single H1 per page -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h4>Detail</h4>
<h2>Another Section</h2>
```
### Visual Weight
```css
/* Primary action - highest weight */
.btn-primary {
background: var(--accent);
color: white;
font-weight: 600;
}
/* Secondary action - medium weight */
.btn-secondary {
background: transparent;
border: 2px solid var(--accent);
color: var(--accent);
}
/* Tertiary action - lowest weight */
.btn-tertiary {
background: transparent;
color: var(--accent);
text-decoration: underline;
}
```
---
## Whitespace and Spacing
### The Role of Whitespace
- **Breathing room** - Reduces visual clutter
- **Grouping** - Related items closer together
- **Focus** - Isolation creates emphasis
- **Readability** - Improves comprehension
### Spacing Scale
```css
/* Consistent spacing scale */
:root {
--space-xs: 0.25rem; /* 4px */
--space-sm: 0.5rem; /* 8px */
--space-md: 1rem; /* 16px */
--space-lg: 1.5rem; /* 24px */
--space-xl: 2rem; /* 32px */
--space-2xl: 3rem; /* 48px */
--space-3xl: 4rem; /* 64px */
}
```
### Applying Spacing
```css
/* Consistent component spacing */
.card {
padding: var(--space-lg);
margin-bottom: var(--space-xl);
}
/* Related items closer */
.form-field label {
margin-bottom: var(--space-xs);
}
/* Distinct sections further */
.section + .section {
margin-top: var(--space-3xl);
}
```
---
## Layout Principles
### Grid Systems
```css
/* CSS Grid for page layout */
.page {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--space-lg);
}
/* Flexbox for component layout */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: var(--space-md);
}
```
### Responsive Breakpoints
```css
/* Mobile-first breakpoints */
/* Base: Mobile (< 640px) */
@media (min-width: 640px) {
/* Tablet portrait */
}
@media (min-width: 768px) {
/* Tablet landscape */
}
@media (min-width: 1024px) {
/* Desktop */
}
@media (min-width: 1280px) {
/* Large desktop */
}
```
### Content Alignment
```css
/* Center content with max-width */
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--space-md);
}
```
---
## 2026 Trends
### AI-Adaptive Interfaces
- Colors and layouts that adjust based on user behavior
- Personalized visual preferences
- Context-aware theming
### Warm, Soft Colors
- Moving away from stark whites
- Beige, pale pink, peach tones
- Psychologically inviting
### Fluid Design
- Dynamic, responsive everything
- Container queries
- Fluid spacing and typography
### Glassmorphism (Continued)
- Frosted glass effects
- Subtle transparency
- Depth through blur
---
## Tools and Resources
### Color
- [Coolors](https://coolors.co/) - Palette generator
- [Adobe Color](https://color.adobe.com/) - Color wheel
- [Contrast Checker](https://webaim.org/resources/contrastchecker/)
### Typography
- [Type Scale](https://type-scale.com/) - Visual calculator
- [Fontpair](https://fontpair.co/) - Font combinations
- [Google Fonts](https://fonts.google.com/)
### Design Systems
- [Tailwind UI](https://tailwindui.com/)
- [Material Design](https://m3.material.io/)
- [Apple HIG](https://developer.apple.com/design/)
---
## Sources
- [Smashing Magazine - Color Psychology](https://www.smashingmagazine.com/)
- [Interaction Design Foundation - Typography](https://www.interaction-design.org/)
- [Practical Typography](https://practicaltypography.com/)
- [Refactoring UI](https://www.refactoringui.com/)
references/07-forms-and-inputs.md
# Forms & Input Design
Forms are often where users complete their goals—signing up, checking out, submitting data. Good form design directly impacts conversion rates and user satisfaction.
---
## Form Design Principles
### Key Statistics
- **81%** of users abandon forms after starting
- Reducing form fields by **20-60%** often loses no necessary data
- Multi-step forms can increase completion by **86%**
### Fundamental Principles
1. **Ask only what you need** - Every field is friction
2. **Group related fields** - Logical chunking
3. **One column is usually best** - Easier scanning
4. **Clear labels** - No placeholder-only labels
5. **Smart defaults** - Pre-fill when possible
6. **Progressive disclosure** - Show fields when relevant
---
## Labels and Placeholders
### Labels
```html
<!-- Always use labels (not just placeholders) -->
<div class="form-field">
<label for="email">Email address</label>
<input type="email" id="email" name="email" />
</div>
<!-- Label placement -->
<!-- Top-aligned: Best for long forms, mobile -->
<!-- Left-aligned: Good for short forms, scanning -->
<!-- Right-aligned: Avoid (harder to scan) -->
```
**Best Practices:**
- Position labels above inputs (top-aligned)
- Keep labels short and clear
- Don't end with colons (modern convention)
- Associate with `for` attribute
### Placeholders
```html
<!-- Use for hints, not labels -->
<input
type="email"
placeholder="you@example.com"
aria-label="Email address"
/>
<!-- Never use as the only label -->
<!-- Disappears on focus, causing confusion -->
```
**Placeholder Guidelines:**
- Show example format, not label
- Use gray text (but not too light)
- Don't rely on it for critical info
- Disappears—users may forget what field is
---
## Input Types
### HTML5 Input Types
```html
<!-- Use appropriate types for mobile keyboards and validation -->
<input type="text" /> <!-- Default -->
<input type="email" /> <!-- Email keyboard -->
<input type="tel" /> <!-- Phone keyboard -->
<input type="number" /> <!-- Numeric keyboard -->
<input type="url" /> <!-- URL keyboard -->
<input type="password" /> <!-- Hidden characters -->
<input type="search" /> <!-- Search field -->
<input type="date" /> <!-- Date picker -->
<input type="time" /> <!-- Time picker -->
<input type="datetime-local" /> <!-- DateTime picker -->
```
### Input Attributes
```html
<input
type="email"
name="email"
id="email"
required <!-- Validation -->
aria-required="true" <!-- Accessibility -->
autocomplete="email" <!-- Autofill hint -->
inputmode="email" <!-- Keyboard hint -->
pattern="[^@]+@[^@]+\.[^@]+" <!-- Custom validation -->
placeholder="you@example.com"
/>
```
### Autocomplete Values
```html
<!-- Enable browser autofill -->
<input autocomplete="given-name" /> <!-- First name -->
<input autocomplete="family-name" /> <!-- Last name -->
<input autocomplete="email" />
<input autocomplete="tel" />
<input autocomplete="street-address" />
<input autocomplete="postal-code" />
<input autocomplete="cc-number" /> <!-- Credit card -->
<input autocomplete="new-password" /> <!-- For password creation -->
<input autocomplete="current-password" /> <!-- For login -->
```
---
## Validation
### Validation Timing
| Timing | Pros | Cons | Best For |
|--------|------|------|----------|
| On blur | Immediate feedback | Can feel aggressive | Most fields |
| On submit | Low friction | Delayed error discovery | Short forms |
| On input | Real-time | Annoying while typing | Password strength |
| Hybrid | Balanced | Complex to implement | Professional forms |
### Recommended Approach
```javascript
// Validate on blur (when user leaves field)
// Don't validate while typing
// Re-validate on correction
input.addEventListener('blur', validate);
input.addEventListener('input', () => {
// Only clear errors while typing if previously invalid
if (hasError) validate();
});
```
### Error Messages
```html
<!-- Good: Specific, actionable -->
<span class="error">
Please enter a valid email address (e.g., name@example.com)
</span>
<!-- Bad: Vague, unhelpful -->
<span class="error">Invalid input</span>
```
**Error Message Guidelines:**
- Place near the field (below or inline)
- Use red color + icon (not color alone)
- Be specific about what's wrong
- Suggest how to fix
- Use human-friendly language
- Don't blame the user
### Visual Error Indicators
```css
.input-error {
border-color: #dc3545;
background-color: #fff8f8;
}
.error-message {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
display: flex;
align-items: center;
gap: 0.25rem;
}
.error-message::before {
content: "⚠️";
}
```
### Accessibility for Errors
```html
<div class="form-field">
<label for="email">Email</label>
<input
type="email"
id="email"
aria-describedby="email-error"
aria-invalid="true"
/>
<span id="email-error" role="alert">
Please enter a valid email address
</span>
</div>
```
---
## Required Fields
### Marking Required Fields
```html
<!-- Visual indicator -->
<label for="name">
Name <span class="required" aria-hidden="true">*</span>
<span class="sr-only">(required)</span>
</label>
<!-- Alternative: Mark optional fields -->
<label for="phone">
Phone <span class="optional">(optional)</span>
</label>
```
**Convention:**
- If most fields required: mark optional
- If most fields optional: mark required
- Use asterisk (*) for required
- Explain asterisk at form start
---
## Multi-Step Forms
### When to Use
- Long forms (7+ fields)
- Complex processes (checkout, applications)
- Collecting different types of info
- Reducing cognitive load
### Progress Indicator
```html
<nav aria-label="Form progress">
<ol class="steps">
<li class="completed" aria-current="false">
<span class="step-number">1</span>
<span class="step-label">Personal Info</span>
</li>
<li class="current" aria-current="step">
<span class="step-number">2</span>
<span class="step-label">Shipping</span>
</li>
<li class="upcoming" aria-current="false">
<span class="step-number">3</span>
<span class="step-label">Payment</span>
</li>
</ol>
</nav>
```
### Best Practices
- Show step count (Step 2 of 4)
- Allow going back to previous steps
- Save progress automatically
- Validate each step before proceeding
- Keep navigation visible
- Allow skipping optional sections
---
## Specific Field Patterns
### Password Fields
```html
<div class="password-field">
<label for="password">Password</label>
<div class="input-wrapper">
<input
type="password"
id="password"
autocomplete="new-password"
minlength="8"
/>
<button type="button" class="toggle-visibility">
Show
</button>
</div>
<div class="password-strength" aria-live="polite">
<!-- Strength indicator -->
</div>
<ul class="password-requirements">
<li>At least 8 characters</li>
<li>One uppercase letter</li>
<li>One number</li>
</ul>
</div>
```
### Phone Numbers
```html
<input
type="tel"
inputmode="numeric"
autocomplete="tel"
placeholder="(555) 123-4567"
/>
<!-- Consider: -->
<!-- - Auto-format as user types -->
<!-- - Accept various formats -->
<!-- - Show expected format -->
```
### Credit Cards
```html
<input
type="text"
inputmode="numeric"
autocomplete="cc-number"
pattern="[\d ]{13,19}"
placeholder="1234 5678 9012 3456"
maxlength="19"
/>
<!-- Auto-format with spaces -->
<!-- Detect card type from number -->
<!-- Show card icon -->
```
### Addresses
```html
<input
type="text"
autocomplete="address-line1"
placeholder="Street address"
/>
<!-- Consider: -->
<!-- - Address autocomplete (Google Places) -->
<!-- - Country-specific formats -->
<!-- - Postal code lookup -->
```
### Date Selection
```html
<!-- Native date picker -->
<input type="date" />
<!-- Or custom for better UX -->
<input
type="text"
placeholder="MM/DD/YYYY"
inputmode="numeric"
/>
<!-- Consider: -->
<!-- - Relative dates ("Today", "Tomorrow") -->
<!-- - Date range pickers -->
<!-- - Mobile-friendly selectors -->
```
---
## Form Layout
### Single Column
```
┌─────────────────────────────┐
│ First Name │
├─────────────────────────────┤
│ Last Name │
├─────────────────────────────┤
│ Email │
├─────────────────────────────┤
│ [Submit] │
└─────────────────────────────┘
Best for: Mobile, most web forms
```
### Multi-Column (Use Sparingly)
```
┌──────────────┬──────────────┐
│ First Name │ Last Name │
├──────────────┴──────────────┤
│ Email │
├──────────────┬──────────────┤
│ City │ State │ Zip │
└──────────────┴──────────────┘
Use for: Related short fields
Avoid: Different topics side by side
```
### Grouping
```html
<fieldset>
<legend>Shipping Address</legend>
<!-- Address fields -->
</fieldset>
<fieldset>
<legend>Billing Address</legend>
<!-- Address fields -->
</fieldset>
```
---
## Submit Buttons
### Button Labels
```html
<!-- Specific, action-oriented -->
<button type="submit">Create Account</button>
<button type="submit">Place Order</button>
<button type="submit">Send Message</button>
<!-- Avoid generic -->
<button type="submit">Submit</button>
```
### Button States
```html
<button type="submit" disabled>
<!-- Before form is valid -->
Create Account
</button>
<button type="submit" class="loading">
<span class="spinner" aria-hidden="true"></span>
<span>Creating Account...</span>
</button>
```
### Button Placement
- Primary action on the right (or full-width on mobile)
- Cancel/back on the left
- Don't hide the submit button
- Ensure visible without scrolling
---
## Accessibility Checklist
- [ ] Labels properly associated with inputs
- [ ] Error messages linked with `aria-describedby`
- [ ] Required fields marked with `aria-required`
- [ ] Invalid fields marked with `aria-invalid`
- [ ] Errors announced with `role="alert"`
- [ ] Keyboard navigation works correctly
- [ ] Focus management on errors
- [ ] Sufficient color contrast
- [ ] Touch targets at least 44×44px
---
## Sources
- [Nielsen Norman Group - Form Design](https://www.nngroup.com/articles/web-form-design/)
- [Baymard Institute - Form Usability](https://baymard.com/blog/current-state-of-checkout-ux)
- [Luke Wroblewski - Web Form Design](https://www.lukew.com/resources/web_form_design.asp)
- [GOV.UK Design System - Forms](https://design-system.service.gov.uk/patterns/)
references/12a-presence-awareness.md
# Collaborative UX: Presence & Awareness
Real-time collaboration UX focuses on enabling multiple users to work together seamlessly in shared digital spaces. Good collaborative UX creates "social translucence"—the ability to see and feel others' activities within digital environments.
---
## Presence & Awareness
Awareness is "an understanding of the activities of others, which provides a context for your own activity" (Dourish & Bellotti, 1992).
### Live Cursors
Live cursors show the real-time pointer position of collaborators, creating a sense of shared presence.
```css
/* Live cursor component */
.live-cursor {
position: absolute;
pointer-events: none;
z-index: 1000;
transition: transform 50ms linear; /* Smooth interpolation */
}
.live-cursor__pointer {
width: 20px;
height: 20px;
/* Each user gets unique color */
}
.live-cursor__label {
position: absolute;
left: 16px;
top: 16px;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
color: white;
/* Background matches cursor color */
}
```
#### Best Practices
| Aspect | Recommendation |
|--------|---------------|
| Label length | Max 12 characters, truncate with ellipsis |
| Color assignment | Unique per user, consistent across session |
| Update frequency | 50-100ms for smooth movement |
| Visibility | Hide after 30-60s of inactivity |
| Scaling | Consider hiding cursors at extreme zoom levels |
#### When Cursors Become Too Many
```
1-5 users → Show all cursors with labels
6-10 users → Show cursors, abbreviated labels
11-20 users → Show cursors without labels
20+ users → Consider hiding distant cursors or showing only nearby
```
### Avatar Stacks
Show who's currently viewing or editing a document.
```html
<div class="avatar-stack">
<div class="avatar" style="background: #e63946;" title="Alice">A</div>
<div class="avatar" style="background: #2a9d8f;" title="Bob">B</div>
<div class="avatar" style="background: #e9c46a;" title="Carol">C</div>
<div class="avatar-overflow">+3</div>
</div>
```
```css
.avatar-stack {
display: flex;
flex-direction: row-reverse; /* Newest on left */
}
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px solid white;
margin-left: -8px; /* Overlap */
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 600;
color: white;
}
.avatar-overflow {
background: #6b7280;
/* Same styling as .avatar */
}
```
#### Guidelines
- Show 3-5 avatars maximum before overflow
- Use "+N" pattern for additional users
- Clicking should reveal full list
- Consider adding online/away status indicators
### Typing Indicators
Show when collaborators are actively editing.
```javascript
// Typing indicator with debounce
let typingTimeout;
function handleInput() {
broadcastTyping(true);
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
broadcastTyping(false);
}, 2000); // Stop after 2s of inactivity
}
```
```css
/* Typing indicator animation */
.typing-indicator {
display: flex;
gap: 4px;
padding: 8px 12px;
}
.typing-indicator__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #6b7280;
animation: typing-bounce 1.4s infinite ease-in-out both;
}
.typing-indicator__dot:nth-child(1) { animation-delay: 0s; }
.typing-indicator__dot:nth-child(2) { animation-delay: 0.16s; }
.typing-indicator__dot:nth-child(3) { animation-delay: 0.32s; }
@keyframes typing-bounce {
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
```
### Activity Feeds
Show recent changes and who made them.
```
┌─────────────────────────────────────────────────────┐
│ Activity │
├─────────────────────────────────────────────────────┤
│ 🟢 Alice edited "Header component" 2m ago │
│ 🔵 Bob added comment on "Login flow" 5m ago │
│ 🟡 Carol joined the document 12m ago │
│ 🟢 Alice created "Footer component" 15m ago │
└─────────────────────────────────────────────────────┘
```
---
## Communication Features
### Threaded Comments
```css
/* Comment thread styling */
.comment-thread {
border-left: 3px solid #3b82f6;
padding-left: 12px;
margin: 8px 0;
}
.comment {
padding: 8px;
margin-bottom: 4px;
background: #f3f4f6;
border-radius: 8px;
}
.comment__header {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #6b7280;
margin-bottom: 4px;
}
.comment__reply-form {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #e5e7eb;
}
```
### Contextual Comments (Annotations)
Pin comments to specific elements or canvas positions.
```javascript
// Attach comment to element
const annotation = {
id: "comment-123",
targetElement: "component-456",
position: { x: 100, y: 50 }, // Relative to target
thread: [
{ author: "alice", text: "Should this be blue?", timestamp: "..." },
{ author: "bob", text: "Good idea, updating now", timestamp: "..." }
],
resolved: false
};
```
### Emoji Reactions
Quick feedback without requiring a full comment.
```html
<div class="reaction-bar">
<button class="reaction" data-emoji="👍">👍 <span>3</span></button>
<button class="reaction" data-emoji="❤️">❤️ <span>1</span></button>
<button class="reaction" data-emoji="🎉">🎉 <span>2</span></button>
<button class="reaction reaction--add">+</button>
</div>
```
### @Mentions
Notify specific collaborators.
```javascript
// Mention autocomplete
function handleMentionTrigger(text) {
if (text.includes('@')) {
const query = text.split('@').pop();
const matches = collaborators.filter(c =>
c.name.toLowerCase().includes(query.toLowerCase())
);
showMentionSuggestions(matches);
}
}
```
### Anonymous Voting
Prevent groupthink by hiding voter identity.
```
┌───────────────────────────────────────────┐
│ Vote: Which design direction? │
├───────────────────────────────────────────┤
│ Option A: Bold colors ████████░░ 4 │
│ Option B: Minimal ██████████ 5 │
│ Option C: Playful ████░░░░░░ 2 │
├───────────────────────────────────────────┤
│ 11 votes total • Voting ends in 5:00 │
│ Your vote: Option B │
└───────────────────────────────────────────┘
```
---
## Viewport Coordination
### Follow Mode
Lock your viewport to follow another user's view.
```javascript
// Follow mode implementation
class ViewportFollower {
constructor() {
this.following = null;
}
follow(userId) {
this.following = userId;
this.showFollowingIndicator(userId);
// Subscribe to their viewport changes
subscribeToViewport(userId, (viewport) => {
animateToViewport(viewport, { duration: 300 });
});
}
stopFollowing() {
this.following = null;
this.hideFollowingIndicator();
unsubscribeFromViewport();
}
}
```
```css
/* Following indicator */
.viewport--following {
border: 3px solid var(--user-color);
border-radius: 8px;
}
.following-banner {
position: fixed;
top: 60px;
left: 50%;
transform: translateX(-50%);
padding: 8px 16px;
background: var(--user-color);
color: white;
border-radius: 20px;
font-size: 14px;
}
```
### Spotlight / Presenter Mode
Present to collaborators by controlling their view.
```
┌─────────────────────────────────────────────────────────┐
│ 🎯 Alice is presenting │
│ Following her view • Click anywhere to stop [Stop] │
└─────────────────────────────────────────────────────────┘
```
**Implementation:**
1. Presenter clicks "Spotlight me"
2. All viewers receive notification
3. Viewers have 3-5 seconds to opt out
4. Non-opted-out viewers' viewports lock to presenter
5. Presenter's cursor becomes more prominent
### Jump to User
Navigate to where a collaborator is working.
```javascript
function jumpToUser(userId) {
const userViewport = getUserViewport(userId);
animateToViewport(userViewport, {
duration: 500,
easing: 'ease-out'
});
highlightUserCursor(userId, { duration: 2000 });
}
```
---
## Key Metrics
| Metric | Value | Context |
|--------|-------|---------|
| Cursor update rate | 50-100ms | Smooth movement perception |
| Typing indicator timeout | 2-3s | After last keystroke |
| Avatar stack max | 3-5 visible | Before "+N" overflow |
| Cursor label max | 12 chars | Truncate longer names |
---
## Anti-Patterns
1. **No presence indicators** - Users feel alone, cause conflicts
2. **Cursor overload** - Too many cursors create visual noise
3. **Forced following** - Lock users to presenter without consent
4. **Comment spam** - No way to resolve or archive discussions
---
## Sources
- [Liveblocks - Presence Documentation](https://liveblocks.io/presence)
- [Ably - Collaborative UX Best Practices](https://ably.com/blog/collaborative-ux-best-practices)
- [Figma - How Multiplayer Technology Works](https://www.figma.com/blog/how-figmas-multiplayer-technology-works/)
- Dourish, P. & Bellotti, V. (1992). "Awareness and Coordination in Shared Workspaces"
references/10-user-research.md
# User Research Methods
User research is the systematic study of target users to understand their behaviors, needs, and motivations. It informs design decisions with evidence rather than assumptions.
---
## Why User Research?
### Benefits
- **Reduce risk** - Validate ideas before building
- **Save resources** - Avoid building unwanted features
- **Improve usability** - Design for real user needs
- **Increase satisfaction** - Create products people love
- **Drive conversion** - Remove friction in user journeys
### Key Statistics (2025)
| Method | Popularity |
|--------|------------|
| User interviews | 86% |
| Usability testing | 84% |
| Surveys | 77% |
| Concept testing | 64% |
---
## Research Categories
### Qualitative vs. Quantitative
| Qualitative | Quantitative |
|-------------|--------------|
| "Why" and "How" | "What" and "How many" |
| Deep understanding | Statistical significance |
| Small sample sizes | Large sample sizes |
| Exploratory | Measurable |
| Interviews, observations | Surveys, analytics |
### Generative vs. Evaluative
| Generative (Discovery) | Evaluative (Validation) |
|------------------------|------------------------|
| What should we build? | Does this work? |
| Early-stage exploration | Testing solutions |
| Understand problems | Measure effectiveness |
| Interviews, field studies | Usability tests, A/B tests |
---
## Core Research Methods
### 1. User Interviews
One-on-one conversations to understand user experiences, needs, and pain points.
**When to use:**
- Early discovery phase
- Understanding context of use
- Exploring new problem spaces
- Deep-diving into specific topics
**Best Practices:**
```
Preparation:
- Define clear research questions
- Write a discussion guide
- Recruit representative participants (5-8)
- Plan for 45-60 minute sessions
During:
- Ask open-ended questions
- Follow up with "why" and "how"
- Avoid leading questions
- Listen more than you talk
- Take notes or record (with permission)
Question Examples:
✓ "Tell me about the last time you..."
✓ "Walk me through how you..."
✓ "What was the hardest part about..."
✓ "Why did you choose that approach?"
✗ "Do you like feature X?" (leading)
✗ "Would you use this?" (hypothetical)
✗ "Isn't this confusing?" (leading)
```
### 2. Usability Testing
Observing users as they attempt to complete tasks with your product.
**When to use:**
- Validating design decisions
- Identifying usability issues
- Comparing design alternatives
- Before and after redesigns
**Types:**
| Type | Description | Sample Size |
|------|-------------|-------------|
| Moderated | Facilitator guides the session | 5-8 |
| Unmoderated | Participant completes alone | 10-20+ |
| Remote | Via video conferencing | Any |
| In-person | Same physical location | 5-8 |
**Best Practices:**
```
Task Design:
- Realistic scenarios
- Specific, measurable goals
- Avoid hints in wording
- Test critical paths
Metrics:
- Task success rate
- Time on task
- Error rate
- Satisfaction rating (SUS, NPS)
- Qualitative observations
Running the Test:
- "Think aloud" protocol
- Don't help unless stuck
- Note where users struggle
- Ask follow-up questions after tasks
```
### 3. Surveys
Collecting structured data from many users.
**When to use:**
- Measuring satisfaction (NPS, CSAT)
- Validating findings at scale
- Prioritizing features
- Segmenting users
- Benchmarking over time
**Best Practices:**
```
Question Design:
- One concept per question
- Avoid leading language
- Include "I don't know" option
- Mix question types
Question Types:
- Multiple choice (easy to analyze)
- Rating scales (1-5 or 1-7)
- Open-ended (qualitative insights)
- Matrix questions (use sparingly)
Keep It Short:
- 5-10 minutes maximum
- Front-load important questions
- Make it mobile-friendly
- Show progress indicator
```
**Tools:** Typeform, Google Forms, SurveyMonkey, Qualtrics
### 4. Card Sorting
Understanding how users categorize and label information.
**When to use:**
- Designing navigation
- Organizing content
- Creating taxonomies
- Validating IA decisions
**Types:**
| Type | Description | Use |
|------|-------------|-----|
| Open | Users create their own categories | Discovery |
| Closed | Users sort into predefined categories | Validation |
| Hybrid | Mix of open and closed | Refinement |
**Tools:** OptimalSort, Maze, Miro
### 5. Tree Testing
Validating whether users can find items in your information architecture.
**When to use:**
- Testing navigation structure
- Validating IA changes
- Before/after IA redesigns
**Process:**
```
1. Create text-only sitemap (tree)
2. Write tasks: "Find where to return an item"
3. Users navigate the tree to find the target
4. Measure:
- Success rate
- Directness (first click correct)
- Time to complete
```
**Tools:** Treejack, UXtweak
### 6. Analytics Review
Analyzing behavioral data to understand what users do.
**When to use:**
- Always (continuous)
- Identifying problem areas
- Measuring feature adoption
- Understanding user flows
**Key Metrics:**
```
Engagement:
- Page views, sessions
- Time on page
- Bounce rate
- Feature usage
Conversion:
- Conversion rate
- Drop-off points
- Funnel completion
User Behavior:
- Click patterns (heatmaps)
- Scroll depth
- Search queries
- Error rates
```
**Tools:** Google Analytics, Mixpanel, Amplitude, Hotjar, FullStory
### 7. A/B Testing
Comparing two or more versions to see which performs better.
**When to use:**
- Optimizing specific metrics
- Validating design changes
- Testing copy variations
- Resolving design debates
**Requirements:**
```
- Sufficient traffic/users
- Clear hypothesis
- Single variable testing (ideally)
- Statistical significance threshold
- Adequate test duration
```
**Tools:** Optimizely, VWO, Google Optimize (deprecated), LaunchDarkly
---
## Research Planning
### Research Questions
```
Good research questions:
- Specific and focused
- Answerable through research
- Actionable outcomes
Examples:
✓ "What prevents users from completing checkout?"
✓ "How do users currently track their expenses?"
✓ "Which onboarding flow leads to higher activation?"
Not:
✗ "Is our product good?"
✗ "What do users want?"
✗ "Should we build feature X?"
```
### Participant Recruitment
**Sample Sizes:**
| Method | Typical Sample |
|--------|---------------|
| Interviews | 5-12 |
| Usability testing | 5-8 per round |
| Card sorting | 15-30 |
| Surveys | 100+ for quantitative |
| A/B tests | Varies (statistical power) |
**Recruitment Sources:**
- Existing user base
- Recruitment platforms (UserTesting, Respondent.io)
- Social media
- Intercept surveys
- Panel providers
**Screening:**
- Demographics
- Relevant experience
- Product usage
- Technical requirements
---
## Remote vs. In-Person Research
### Remote Research
**Advantages:**
- Global reach
- Lower cost
- More convenient
- Larger sample sizes
- Natural environment
**Challenges:**
- Technical issues
- Harder to build rapport
- Less contextual observation
- Distractions
**Tools:** Zoom, UserTesting, Lookback, Maze
### In-Person Research
**Advantages:**
- Deeper rapport
- Full context observation
- Non-verbal cues visible
- Better for complex tasks
- Prototype testing
**Challenges:**
- Geographic limitations
- Higher cost
- Scheduling difficulties
- Lab environment effect
---
## Research Analysis
### Affinity Mapping
Grouping observations to find patterns.
```
1. Write each observation on a sticky note
2. Group similar observations
3. Name the groups
4. Identify themes and patterns
5. Prioritize findings
```
**Tools:** Miro, FigJam, physical sticky notes
### Thematic Analysis
```
1. Familiarize with data
2. Generate initial codes
3. Search for themes
4. Review themes
5. Define and name themes
6. Report findings
```
### Quantitative Analysis
```
Basic:
- Descriptive statistics
- Task success rates
- Comparison of means
Advanced:
- Statistical significance testing
- Regression analysis
- Cohort analysis
```
---
## Presenting Research Findings
### Research Report Structure
```
1. Executive Summary
- Key findings
- Recommendations
2. Research Overview
- Goals
- Methods
- Participants
3. Detailed Findings
- Organized by theme
- Evidence and quotes
- Severity ratings
4. Recommendations
- Prioritized actions
- Design implications
- Next steps
```
### Making Findings Actionable
```
Instead of:
"Users were confused by the checkout flow"
Say:
"5 of 8 users couldn't find the promo code field.
Recommendation: Move promo code input above the order summary.
Impact: Could recover ~15% of abandoned carts."
```
---
## 2026 Trends
### AI in Research
- **Transcription:** Otter.ai, Dovetail
- **Analysis:** Pattern recognition in qualitative data
- **Synthesis:** Summarizing findings across studies
- **Moderation:** AI-assisted interview guides
54% of researchers are experimenting with AI tools (2025 State of User Research)
### Continuous Research
Moving from project-based to always-on research:
- Regular user touchpoints
- Embedded research in product teams
- Research repositories (Dovetail, Notion)
- Democratized research
### Research Operations (ResOps)
- Standardized processes
- Participant panels
- Tool management
- Knowledge management
- Research training
---
## Ethical Considerations
### Informed Consent
```
Participants must know:
- Purpose of research
- How data will be used
- Their right to withdraw
- Recording/privacy details
- Compensation details
```
### Privacy
```
- Anonymize data when possible
- Secure data storage
- Clear data retention policies
- GDPR/CCPA compliance
- Limit data access
```
### Inclusive Research
```
- Recruit diverse participants
- Accommodate disabilities
- Consider cultural contexts
- Avoid biased questions
- Provide fair compensation
```
---
## Tools Overview
| Category | Tools |
|----------|-------|
| Interviews | Zoom, UserInterviews.com |
| Usability | UserTesting, Maze, Lookback |
| Surveys | Typeform, SurveyMonkey |
| Analytics | Mixpanel, Amplitude, Hotjar |
| Card Sorting | OptimalSort, Maze |
| Repository | Dovetail, Condens, Notion |
| Prototyping | Figma, ProtoPie |
---
## Sources
- [User Interviews - State of User Research 2025](https://www.userinterviews.com/state-of-user-research-report)
- [Nielsen Norman Group - Research Methods](https://www.nngroup.com/articles/which-ux-research-methods/)
- Portigal, S. (2013). "Interviewing Users"
- Kuniavsky, M. (2012). "Observing the User Experience"
- [Maze - UX Research Methods](https://maze.co/guides/ux-research/)
references/11-design-systems.md
# Design Systems
A design system is a complete set of standards, documentation, and reusable components intended to manage design at scale.
---
## What is a Design System?
### Definition
A design system is the single source of truth that groups all elements that allow teams to design, realize, and develop a product.
### Components of a Design System
```
Design System
├── Foundations (Design Tokens)
│ ├── Colors
│ ├── Typography
│ ├── Spacing
│ ├── Shadows
│ └── Motion
├── Components
│ ├── Buttons
│ ├── Forms
│ ├── Cards
│ ├── Navigation
│ └── ...
├── Patterns
│ ├── Forms
│ ├── Authentication
│ ├── Search
│ └── ...
├── Guidelines
│ ├── Accessibility
│ ├── Content
│ ├── Voice & Tone
│ └── Usage
└── Documentation
├── Getting Started
├── Component APIs
└── Contributing
```
---
## Design System vs. Related Concepts
### Style Guide
Focus on **visual elements**:
- Brand colors
- Typography
- Logo usage
- Imagery guidelines
### Pattern Library
Collection of **UI patterns**:
- Navigation patterns
- Form patterns
- Layout templates
- Interaction patterns
### Component Library
**Coded UI components**:
- Buttons, inputs, modals
- Ready-to-use code
- Framework-specific
- Documented APIs
### Design System
**All of the above, plus:**
- Governance processes
- Contribution guidelines
- Versioning
- Team workflows
---
## Design Tokens
Design tokens are the atomic values that define visual design decisions.
### Tokens as the Universal Language
By 2026 the leading framing is that design tokens are not just a theming
convenience—they are the **infrastructure layer** that makes consistency possible
across an expanding surface of platforms and modalities (web, iOS, Android, watch,
TV, voice, in-car, AR). A token is a named, platform-agnostic decision (e.g.
`color.action.primary`, `space.4`) that a build step (Style Dictionary, Tokens
Studio) compiles into CSS custom properties, Swift, Kotlin, JSON, etc.
```
Single source of truth (tokens.json)
│ transform / build
├──► CSS --color-action-primary
├──► iOS Color.actionPrimary
├──► Android colorActionPrimary
└──► docs / Figma variables
```
- **One decision, many outputs** — change a token, every platform updates in sync
- **Tiered structure** — primitive (raw values) → semantic (intent) → component
- **Theming falls out for free** — light/dark, brand, density, high-contrast are
just alternate token sets resolved at the semantic layer
- Tokens are the contract between design and engineering; treat them as versioned API
### Token Types
```css
/* Color tokens */
--color-primary: #0066cc;
--color-primary-light: #3399ff;
--color-primary-dark: #004499;
--color-background: #ffffff;
--color-text-primary: #1a1a1a;
--color-text-secondary: #666666;
--color-error: #dc3545;
--color-success: #28a745;
/* Typography tokens */
--font-family-base: 'Inter', sans-serif;
--font-family-mono: 'JetBrains Mono', monospace;
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.25rem;
--font-size-xl: 1.5rem;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;
--line-height-tight: 1.25;
--line-height-normal: 1.5;
/* Spacing tokens */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.5rem; /* 24px */
--space-6: 2rem; /* 32px */
--space-8: 3rem; /* 48px */
/* Shadow tokens */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
/* Border radius tokens */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--radius-full: 9999px;
/* Animation tokens */
--duration-fast: 150ms;
--duration-normal: 250ms;
--duration-slow: 400ms;
--easing-default: ease-out;
```
### Semantic Tokens
```css
/* Semantic layer on top of primitive tokens */
--color-action-primary: var(--color-primary);
--color-action-secondary: var(--color-secondary);
--color-feedback-error: var(--color-error);
--color-feedback-success: var(--color-success);
--color-surface: var(--color-background);
--color-surface-raised: var(--color-white);
/* Component-specific tokens */
--button-padding: var(--space-3) var(--space-5);
--button-radius: var(--radius-md);
--card-padding: var(--space-5);
--card-radius: var(--radius-lg);
```
### Variable Fonts
A **variable font** packs an entire type family—every weight, width, and optical
size—into a single file along continuous axes, replacing the old "load 6 static
weight files" approach. This is now the default recommendation for design systems.
```css
@font-face {
font-family: 'InterVar';
src: url('/fonts/Inter.var.woff2') format('woff2-variations');
font-weight: 100 900; /* full weight range in one file */
font-display: swap;
}
:root {
--font-family-base: 'InterVar', sans-serif;
}
/* Use any weight on the axis, not just preset steps */
.heading { font-weight: 620; }
/* Optical sizing axis tracks the rendered size automatically */
body { font-optical-sizing: auto; }
```
**Why it matters:**
- **Performance** — one HTTP request and ~one file instead of many static weights
- **Responsive typography** — interpolate weight/width fluidly across breakpoints
and even animate them (respecting reduced-motion); great for fluid `clamp()` scales
- **Design flexibility** — fine-grained control (e.g. weight 540) without new files
- Expose axes as tokens (`--font-weight-*`) so the system stays the single source
- Caveat: subset to needed glyphs/axes; a full variable font can be large
---
## Component Architecture
### Anatomy of a Component
```
Button Component
├── States
│ ├── Default
│ ├── Hover
│ ├── Active/Pressed
│ ├── Focus
│ ├── Disabled
│ └── Loading
├── Variants
│ ├── Primary
│ ├── Secondary
│ ├── Tertiary/Ghost
│ └── Destructive
├── Sizes
│ ├── Small
│ ├── Medium (default)
│ └── Large
├── Props/Options
│ ├── Label
│ ├── Icon (left/right)
│ ├── Full width
│ └── Type (button/submit)
└── Accessibility
├── Keyboard navigation
├── ARIA attributes
└── Focus management
```
### Component API Design
```tsx
// Well-designed component API
interface ButtonProps {
/** Button label */
children: React.ReactNode;
/** Visual style variant */
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive';
/** Button size */
size?: 'sm' | 'md' | 'lg';
/** Disabled state */
disabled?: boolean;
/** Loading state */
loading?: boolean;
/** Full width */
fullWidth?: boolean;
/** Icon before label */
iconLeft?: React.ReactNode;
/** Icon after label */
iconRight?: React.ReactNode;
/** Click handler */
onClick?: () => void;
}
// Usage
<Button variant="primary" size="lg" iconLeft={<PlusIcon />}>
Add Item
</Button>
```
---
## Documentation Best Practices
### Component Documentation
Each component should include:
1. **Overview**
- Description and purpose
- When to use / when not to use
2. **Examples**
- Interactive playground
- Common use cases
- Edge cases
3. **API Reference**
- Props/parameters
- Types
- Default values
4. **Design Guidelines**
- Visual specifications
- Do's and don'ts
- Spacing and layout
5. **Accessibility**
- Keyboard interactions
- Screen reader behavior
- ARIA requirements
6. **Related Components**
- Similar components
- Composability patterns
### Documentation Tools
- **Storybook** - Component documentation and playground
- **Docusaurus** - Documentation websites
- **Zeroheight** - Design system documentation
- **Notion** - Lightweight documentation
- **Supernova** - Design token management
---
## Notable Design Systems
### IBM Carbon Design System
```
Open source, comprehensive
- React, Vue, Angular, Web Components
- Detailed accessibility guidelines
- Strong data visualization components
- Enterprise-focused
https://carbondesignsystem.com/
```
### Atlassian Design System
```
Product design focus
- Detailed component documentation
- Design tokens approach
- Accessibility built-in
- Confluence, Jira, Trello patterns
https://atlassian.design/
```
### Salesforce Lightning
```
Enterprise-grade
- Extensive component library
- Accessibility focused
- Mobile-responsive
- Customizable themes
https://www.lightningdesignsystem.com/
```
### Material Design (Google)
```
Platform-agnostic design language
- Comprehensive guidelines
- Motion and animation specs
- Cross-platform support
- Open-source implementations
https://m3.material.io/
```
### Apple Human Interface Guidelines
```
Platform-specific guidance
- iOS, macOS, watchOS, tvOS
- System component integration
- Platform conventions
- Design resources (Figma, Sketch)
https://developer.apple.com/design/
```
---
## React Component Libraries (2026)
### Untitled UI React
```
- 10,000+ Figma components
- Tailwind CSS v4.1
- React Aria for accessibility
- TypeScript v5.8
- Most comprehensive free library
```
### React Aria Components (Adobe)
```
- Unstyled, accessible
- ARIA patterns built-in
- Internationalization
- Keyboard navigation
- Foundation for custom systems
```
### Radix UI
```
- Unstyled primitives
- Accessibility focused
- Composable
- Foundation for shadcn/ui
```
### shadcn/ui
```
- Copy-paste components
- Tailwind + Radix
- Full customization
- Popular for rapid prototyping
```
### Chakra UI
```
- Styled components
- Dark mode built-in
- Accessible
- Good DX
```
---
## Governance & Contribution
### Versioning
```
Semantic versioning (SemVer):
MAJOR.MINOR.PATCH
MAJOR - Breaking changes
MINOR - New features (backward compatible)
PATCH - Bug fixes
Example: 2.3.1
- Version 2 (breaking changes from v1)
- 3 new feature additions
- 1 bug fix since last minor
```
### Change Log
```markdown
## [2.3.0] - 2025-01-15
### Added
- New `Select` component
- Dark mode support for `Card`
### Changed
- Updated Button padding tokens
### Fixed
- Modal focus trap on Safari
### Deprecated
- Legacy Grid component (use CSS Grid)
```
### Contribution Model
```
Federated Model:
- Central team maintains core
- Product teams can contribute
- Review and approval process
- Clear contribution guidelines
Centralized Model:
- Dedicated DS team owns everything
- Product teams request features
- Slower but more consistent
Hybrid:
- Core components centralized
- Extensions/themes distributed
```
---
## Building a Design System
### Phases
```
Phase 1: Audit
- Inventory existing UI
- Identify inconsistencies
- Prioritize components
- Define scope
Phase 2: Foundation
- Design tokens
- Core components (button, input)
- Basic documentation
- Initial adoption
Phase 3: Expansion
- More components
- Patterns and templates
- Contribution guidelines
- Advanced documentation
Phase 4: Maturity
- Full component library
- Governance processes
- Automated testing
- Continuous improvement
```
### Success Metrics
```
Adoption:
- % of products using the system
- Component usage analytics
- Developer satisfaction
Quality:
- Accessibility audit scores
- Bug/issue counts
- Design consistency reviews
Efficiency:
- Time to build new features
- Reduced design/dev sync issues
- Code reuse metrics
```
---
## Common Pitfalls
1. **Building too much upfront** - Start small, iterate
2. **No governance** - Define ownership and processes
3. **Designer-dev disconnect** - Single source of truth
4. **Outdated documentation** - Automate where possible
5. **No adoption strategy** - Train teams, provide support
6. **Ignoring accessibility** - Build in from the start
7. **Over-engineering** - Solve current problems first
8. **No versioning** - Breaking changes break trust
9. **Component bloat** - Audit and deprecate regularly
10. **Premature abstraction** - Wait for patterns to emerge
---
## Tools Ecosystem
### Design
| Tool | Purpose |
|------|---------|
| Figma | Design and prototyping |
| Figma Tokens | Token management |
| Storybook | Component development |
### Development
| Tool | Purpose |
|------|---------|
| Style Dictionary | Token transformation |
| Chromatic | Visual testing |
| Playwright/Testing Library | Component testing |
### Documentation
| Tool | Purpose |
|------|---------|
| Storybook | Component docs |
| Supernova | DS documentation |
| Docusaurus | Website docs |
---
## Sources
- [Nielsen Norman - Design Systems 101](https://www.nngroup.com/articles/design-systems-101/)
- [Atlassian Design System](https://atlassian.design/)
- [Carbon Design System](https://carbondesignsystem.com/)
- Nathan Curtis - [EightShapes](https://eightshapes.com/)
- Brad Frost - [Atomic Design](https://atomicdesign.bradfrost.com/)
- [Design Systems Repo](https://designsystemsrepo.com/)
references/12b-conflict-resolution-sync.md
# Collaborative UX: Conflict Resolution & Sync
This reference covers the technical and UX aspects of concurrent editing, conflict resolution, version control, sharing permissions, and offline synchronization in collaborative applications.
---
## Concurrent Editing
### Conflict Resolution Approaches
#### Operational Transformation (OT)
Used by Google Docs. Server-centric, transforms operations to maintain consistency.
```
User A types "Hello" at position 0
User B types "World" at position 0 (simultaneously)
Server transforms B's operation:
- B intended position 0
- A inserted 5 chars before B's cursor
- B's operation becomes: insert "World" at position 5
Result: "HelloWorld" (consistent for both)
```
**UX Implications:**
- Low-latency feel with optimistic updates
- Requires constant server connection
- Complex to implement correctly for rich content
#### CRDTs (Conflict-free Replicated Data Types)
Used by Figma, Notion. Mathematically guaranteed to converge.
```
Benefits:
- Works offline (sync when reconnected)
- No central server required
- Automatic conflict resolution
UX Benefits:
- True offline-first capability
- Peer-to-peer collaboration possible
- Seamless reconnection experience
```
### Component Locking
Prevent simultaneous edits on the same element.
```css
/* Locked component indicator */
.component--locked {
outline: 2px dashed #6b7280;
pointer-events: none;
position: relative;
}
.component--locked::after {
content: "🔒 Editing: " attr(data-locked-by);
position: absolute;
top: -24px;
left: 0;
background: #374151;
color: white;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
```
#### Lock Timeout Pattern
```javascript
// Auto-release lock after inactivity
const LOCK_TIMEOUT = 60000; // 60 seconds
let lockTimer;
function refreshLock() {
clearTimeout(lockTimer);
lockTimer = setTimeout(() => {
showDialog("Are you still editing? Lock will release in 30 seconds.");
// If no response, release lock
}, LOCK_TIMEOUT);
}
```
### Optimistic Updates
Apply changes locally before server confirmation.
```javascript
async function updateDocument(change) {
// 1. Apply immediately (optimistic)
applyLocalChange(change);
updateUI();
try {
// 2. Send to server
await sendToServer(change);
} catch (error) {
// 3. Rollback if failed
revertLocalChange(change);
showError("Failed to save. Your change was reverted.");
}
}
```
### Merge Conflict UI
When automatic resolution isn't possible, provide clear conflict resolution.
```
┌─────────────────────────────────────────────────────────────┐
│ Conflict Detected │
├─────────────────────────────────────────────────────────────┤
│ Your version │ Server version │ Merged │
│ ─────────────────────│────────────────────│───────────────│
│ The quick brown fox │ The slow brown fox │ │
│ ↓ │ ↓ │ ↓ │
│ [Accept] │ [Accept] │ [Edit & Save]│
└─────────────────────────────────────────────────────────────┘
```
**Best Practice:** Show differences side-by-side with accept/reject buttons for each conflicting section.
---
## Version Control & History
### Version History UI
```
┌──────────────────────────────────────────────────────────┐
│ Version History [x] │
├──────────────────────────────────────────────────────────┤
│ Current version │
│ ├── Today, 3:45 PM - Alice (auto-saved) │
│ ├── Today, 2:30 PM - Bob "Added new section" │
│ ├── Today, 11:00 AM - Alice "Initial draft" │
│ ├── Yesterday, 4:00 PM - Carol (auto-saved) │
│ └── Dec 15, 2024 - Document created │
│ │
│ [Restore Selected] [Compare with Current] │
└──────────────────────────────────────────────────────────┘
```
### Multiplayer Undo/Redo
In collaborative environments, undo must be client-specific.
```javascript
// Each user has their own undo stack
class CollaborativeUndoManager {
constructor(userId) {
this.userId = userId;
this.undoStack = [];
this.redoStack = [];
}
execute(action) {
// Record only this user's actions
if (action.userId === this.userId) {
this.undoStack.push(action);
this.redoStack = []; // Clear redo on new action
}
}
undo() {
const action = this.undoStack.pop();
if (action) {
const inverseAction = action.inverse();
this.redoStack.push(action);
return inverseAction; // Apply to document
}
}
}
```
**Key Principle:** Users can only undo their own actions, not others'.
### Change Attribution
Show who changed what and when.
```css
/* Inline attribution on hover */
.text-block[data-author]::before {
content: attr(data-author) " • " attr(data-edited);
position: absolute;
top: -20px;
left: 0;
font-size: 11px;
color: #6b7280;
opacity: 0;
transition: opacity 150ms;
}
.text-block:hover::before {
opacity: 1;
}
/* Author highlight colors */
.text-block[data-author="alice"] { border-left: 3px solid #e63946; }
.text-block[data-author="bob"] { border-left: 3px solid #2a9d8f; }
```
---
## Sharing & Permissions
### Invite Flow Patterns
#### Email Invitation
```
┌─────────────────────────────────────────────────────┐
│ Invite people │
├─────────────────────────────────────────────────────┤
│ Email address │
│ ┌─────────────────────────────────────────────────┐ │
│ │ alice@example.com │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ Permission level │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Can edit ▼ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ Add a message (optional) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Here's the project we discussed... │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ [Cancel] [Send Invite] │
└─────────────────────────────────────────────────────┘
```
#### Link Sharing
```
┌─────────────────────────────────────────────────────┐
│ Share via link │
├─────────────────────────────────────────────────────┤
│ Anyone with the link │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Can view ▼ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ https://app.example.com/doc/abc123... [Copy] │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ⚠️ Anyone with this link can access the document │
└─────────────────────────────────────────────────────┘
```
### Permission Levels
| Level | Capabilities |
|-------|-------------|
| Owner | Full control, transfer ownership, delete |
| Admin | Manage members, edit settings, edit content |
| Editor | Edit content, add comments |
| Commenter | View content, add comments |
| Viewer | View only |
### Permission Dialog Pattern
```
┌─────────────────────────────────────────────────────────────┐
│ Share "Project Alpha" │
├─────────────────────────────────────────────────────────────┤
│ People with access │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 👤 Alice (you) Owner │ │ │
│ │ 👤 Bob Editor [▼] │ │ │
│ │ 👤 Carol Viewer [▼] │ │ │
│ │ 🔗 Anyone with link Viewer [▼] │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [+ Add people] │
└─────────────────────────────────────────────────────────────┘
```
---
## Offline & Sync
### Connection Status Indicators
```css
/* Connection status banner */
.connection-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 8px 16px;
text-align: center;
font-size: 14px;
z-index: 9999;
}
.connection-banner--offline {
background: #fef3c7;
color: #92400e;
}
.connection-banner--syncing {
background: #dbeafe;
color: #1e40af;
}
.connection-banner--error {
background: #fee2e2;
color: #991b1b;
}
```
### Offline State Communication
```
┌─────────────────────────────────────────────────────────┐
│ ⚠️ You're offline │
│ Changes will sync when you reconnect. │
│ Last synced: 2 minutes ago [Dismiss] │
└─────────────────────────────────────────────────────────┘
```
**Key Principles:**
- Avoid technical jargon ("no network connection" vs "offline")
- Show last sync time
- Indicate that work is safe
- Provide manual sync option when reconnected
### Sync Queue Visualization
```
┌─────────────────────────────────────────────────────────┐
│ Syncing 3 changes... │
│ ████████████░░░░░░░░░░░░░░░░░░░ │
│ │
│ ✓ "Header updated" │
│ ⟳ "Added new section" │
│ ○ "Changed footer color" │
└─────────────────────────────────────────────────────────┘
```
### Reconnection Flow
```javascript
// Graceful reconnection
async function handleReconnection() {
showBanner("Reconnecting...");
try {
// 1. Fetch remote changes
const remoteChanges = await fetchChanges(lastSyncTimestamp);
// 2. Merge with local changes
const merged = mergeChanges(localQueue, remoteChanges);
// 3. Apply merged state
applyChanges(merged);
// 4. Push local changes
await pushLocalChanges(localQueue);
showBanner("You're back online!", { duration: 3000 });
} catch (error) {
showBanner("Reconnection failed. Retrying...");
scheduleRetry();
}
}
```
---
## Key Metrics
| Metric | Value | Context |
|--------|-------|---------|
| Lock inactivity timeout | 60s | Before prompting release |
| Reconnection retry | 1s, 2s, 4s, 8s... | Exponential backoff |
| Optimistic timeout | 5-10s | Before showing sync error |
---
## Anti-Patterns
1. **No conflict prevention** - Simultaneous edits overwrite each other
2. **Silent failures** - Sync errors without user notification
3. **No offline indication** - Users think they're connected
4. **Permission confusion** - Unclear what each role can do
5. **Blocking on sync** - UI freezes during synchronization
6. **No undo attribution** - Users undo others' work accidentally
---
## Sources
- [Figma - Multiplayer Editing](https://www.figma.com/blog/multiplayer-editing-in-figma/)
- [CKEditor - Real-Time Collaboration Lessons](https://ckeditor.com/blog/lessons-learned-from-creating-a-rich-text-editor-with-real-time-collaboration/)
- [DEV - Building Collaborative Interfaces: OT vs CRDTs](https://dev.to/puritanic/building-collaborative-interfaces-operational-transforms-vs-crdts-2obo)
- [Liveblocks - Undo/Redo in Multiplayer](https://liveblocks.io/blog/how-to-build-undo-redo-in-a-multiplayer-environment)
- [A List Apart - Designing Offline-First](https://alistapart.com/article/offline-first/)
references/13a-canvas-navigation.md
# Canvas Apps: Navigation & Interaction
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.
---
## Canvas Navigation
### Coordinate Systems
A canvas app has two coordinate systems:
```javascript
// Screen coordinates: pixel position on display
const screenPoint = { x: 500, y: 300 };
// Canvas coordinates: position in the infinite canvas space
const canvasPoint = {
x: (screenPoint.x - camera.x) / camera.zoom,
y: (screenPoint.y - camera.y) / camera.zoom
};
// Camera state
const camera = {
x: 0, // Horizontal offset
y: 0, // Vertical offset
zoom: 1 // Scale factor (1 = 100%)
};
```
### Zoom UI
Zoom should feel natural, focusing on the cursor position.
```javascript
function handleZoom(event) {
event.preventDefault();
const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
const newZoom = clamp(camera.zoom * zoomFactor, MIN_ZOOM, MAX_ZOOM);
// Zoom toward cursor position
const cursorX = event.clientX;
const cursorY = event.clientY;
// Calculate new camera position to keep cursor point fixed
camera.x = cursorX - (cursorX - camera.x) * (newZoom / camera.zoom);
camera.y = cursorY - (cursorY - camera.y) * (newZoom / camera.zoom);
camera.zoom = newZoom;
render();
}
```
#### Zoom Controls
```
┌──────────────────────────────┐
│ [−] ━━━●━━━━━━━━━ [+] │ Slider
│ 100% │ Current level
│ [Fit] [50%] [100%] [200%] │ Presets
└──────────────────────────────┘
```
| Control | Action |
|---------|--------|
| Scroll wheel | Zoom in/out |
| Ctrl/Cmd + scroll | Zoom (alternative) |
| Pinch gesture | Zoom on touch devices |
| +/- keys | Step zoom |
| 0 or 1 | Reset to 100% |
| Shift + 1 | Fit all to view |
| Shift + 2 | Fit selection to view |
#### Zoom Levels
```
Typical range: 10% to 4000%+
Common presets: 25%, 50%, 75%, 100%, 150%, 200%, 400%
Semantic zooming (content changes at different levels):
- 10-25% → Show only shapes/outlines
- 25-50% → Show basic content, hide details
- 50-100% → Full content visible
- 100-200% → Pixel-perfect editing
- 200%+ → Sub-pixel precision work
```
### Pan/Scroll
```javascript
// Space + drag to pan
let isPanning = false;
let lastPanPoint = { x: 0, y: 0 };
function handleKeyDown(event) {
if (event.code === 'Space' && !isPanning) {
isPanning = true;
document.body.style.cursor = 'grab';
}
}
function handleMouseMove(event) {
if (isPanning && event.buttons === 1) {
camera.x += event.clientX - lastPanPoint.x;
camera.y += event.clientY - lastPanPoint.y;
document.body.style.cursor = 'grabbing';
render();
}
lastPanPoint = { x: event.clientX, y: event.clientY };
}
```
#### Pan Methods
| Method | Trigger | Notes |
|--------|---------|-------|
| Space + drag | Hold space, click and drag | Most common in design tools |
| Middle mouse | Middle click and drag | Power user shortcut |
| Two-finger drag | Trackpad gesture | Natural for laptop users |
| Scroll bars | Click and drag | Visible scroll position |
| Arrow keys | Keyboard navigation | Step-based movement |
### Minimap / Overview
Bird's-eye navigation for large canvases.
```html
<div class="minimap">
<canvas id="minimap-canvas"></canvas>
<div class="minimap__viewport"></div>
</div>
```
```css
.minimap {
position: fixed;
bottom: 16px;
right: 16px;
width: 200px;
height: 150px;
background: #f3f4f6;
border: 1px solid #d1d5db;
border-radius: 8px;
overflow: hidden;
}
.minimap__viewport {
position: absolute;
border: 2px solid #3b82f6;
background: rgba(59, 130, 246, 0.1);
cursor: move;
}
```
**Minimap Guidelines:**
- Show at 5-10% of actual canvas scale
- Highlight current viewport area
- Allow click-to-navigate
- Allow drag viewport rectangle
- Consider hiding when zoomed to fit all
---
## Object Selection
### Selection Modes
```javascript
// Click selection
function handleClick(event) {
const hitObject = hitTest(event.x, event.y);
if (event.shiftKey) {
// Add to / remove from selection
toggleSelection(hitObject);
} else {
// Replace selection
clearSelection();
if (hitObject) select(hitObject);
}
}
// Marquee selection
function handleMarquee(startPoint, endPoint) {
const rect = {
x: Math.min(startPoint.x, endPoint.x),
y: Math.min(startPoint.y, endPoint.y),
width: Math.abs(endPoint.x - startPoint.x),
height: Math.abs(endPoint.y - startPoint.y)
};
const contained = objects.filter(obj =>
isContainedIn(obj.bounds, rect)
);
if (event.shiftKey) {
addToSelection(contained);
} else {
setSelection(contained);
}
}
```
### Selection Visuals
```css
/* Selected object */
.object--selected {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* Selection marquee */
.selection-marquee {
position: absolute;
border: 1px solid #3b82f6;
background: rgba(59, 130, 246, 0.1);
pointer-events: none;
}
/* Multi-selection bounding box */
.selection-bounds {
position: absolute;
border: 1px dashed #3b82f6;
}
```
### Selection Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| Click | Select single object |
| Shift + Click | Add/remove from selection |
| Cmd/Ctrl + A | Select all |
| Escape | Deselect all |
| Tab | Select next object |
| Shift + Tab | Select previous object |
| Cmd/Ctrl + Click | Deep select (nested elements) |
---
## Object Manipulation
### Transform Controls
```
┌─────────────────────────────────┐
◯───┤ ├───◯ ← Rotation handle
│ │
◻───┤ Selected Object ├───◻ ← Resize handles
│ │
◯───┤ ├───◯
└─────────────────────────────────┘
◻ ◻
```
```css
/* Resize handles */
.resize-handle {
position: absolute;
width: 8px;
height: 8px;
background: white;
border: 1px solid #3b82f6;
border-radius: 2px;
}
/* Corner handles - resize */
.resize-handle--nw { cursor: nwse-resize; top: -4px; left: -4px; }
.resize-handle--ne { cursor: nesw-resize; top: -4px; right: -4px; }
.resize-handle--sw { cursor: nesw-resize; bottom: -4px; left: -4px; }
.resize-handle--se { cursor: nwse-resize; bottom: -4px; right: -4px; }
/* Edge handles */
.resize-handle--n { cursor: ns-resize; top: -4px; left: 50%; }
.resize-handle--s { cursor: ns-resize; bottom: -4px; left: 50%; }
.resize-handle--e { cursor: ew-resize; right: -4px; top: 50%; }
.resize-handle--w { cursor: ew-resize; left: -4px; top: 50%; }
/* Rotation handle */
.rotate-handle {
position: absolute;
top: -30px;
left: 50%;
width: 10px;
height: 10px;
background: white;
border: 1px solid #3b82f6;
border-radius: 50%;
cursor: grab;
}
```
### Drag and Drop
```javascript
function handleDrag(object, event) {
// Show ghost/preview at cursor
showDragPreview(object, event.x, event.y);
// Check drop targets
const dropTarget = findDropTarget(event.x, event.y);
if (dropTarget) {
highlightDropTarget(dropTarget);
}
}
function handleDrop(object, event) {
const dropTarget = findDropTarget(event.x, event.y);
// Animate to final position
animateTo(object, {
x: snapToGrid(event.x),
y: snapToGrid(event.y)
}, { duration: 100 });
hideDragPreview();
}
```
**Drag and Drop Guidelines:**
- Show visual feedback during drag (elevation, shadow)
- Animate other elements moving out of the way
- Use 100ms animation for drop
- Provide clear drop zone indicators
- Support keyboard alternatives (WCAG 2.5.7)
### Keyboard Movement
```javascript
const MOVE_STEP = 1; // Arrow key
const MOVE_STEP_LARGE = 10; // Shift + Arrow
function handleArrowKey(key, shiftKey) {
const step = shiftKey ? MOVE_STEP_LARGE : MOVE_STEP;
selectedObjects.forEach(obj => {
switch(key) {
case 'ArrowUp': obj.y -= step; break;
case 'ArrowDown': obj.y += step; break;
case 'ArrowLeft': obj.x -= step; break;
case 'ArrowRight': obj.x += step; break;
}
});
}
```
---
## Key Metrics
| Metric | Value | Context |
|--------|-------|---------|
| Zoom range | 10% - 4000% | Typical design tools |
| Pan speed | 1:1 cursor ratio | Natural feeling |
| Touch target | 44×44px | Minimum for handles |
| Render target | 60fps | During pan/zoom |
---
## Anti-Patterns
1. **Fixed canvas size** - Limiting creativity with artificial boundaries
2. **Zoom at screen center** - Disorienting; zoom at cursor instead
3. **Sluggish pan/zoom** - Must feel instant and smooth
4. **Inconsistent shortcuts** - Differs from industry standard
5. **Poor touch support** - Gestures should match platform conventions
---
## Sources
- [Steve Ruiz - Creating a Zoom UI](https://www.steveruiz.me/posts/zoom-ui)
- [NN/g - Drag and Drop](https://www.nngroup.com/articles/drag-drop/)
- [Pencil & Paper - Drag & Drop UX Patterns](https://www.pencilandpaper.io/articles/ux-pattern-drag-and-drop)
- [Salesforce - 4 Patterns for Accessible Drag and Drop](https://medium.com/salesforce-ux/4-major-patterns-for-accessible-drag-and-drop-1d43f64ebf09)
references/09-ux-writing.md
# UX Writing & Microcopy
UX writing is the practice of crafting user-facing text that helps people use digital products. Every word in an interface is designed to enhance the user experience.
---
## What is Microcopy?
Microcopy refers to the small bits of text throughout an interface:
- Button labels
- Error messages
- Form labels and hints
- Tooltips
- Empty states
- Confirmation messages
- Loading text
- Navigation labels
- Onboarding text
### The Impact of Microcopy
Good microcopy:
- Reduces user errors
- Increases conversion rates
- Builds trust and brand personality
- Decreases support requests
- Guides users through complex tasks
---
## Core Principles
### Clarity
Users should instantly understand what they're reading.
```
Good: "Delete this file?"
Bad: "Are you sure you wish to proceed with the deletion of the selected item?"
Good: "Sign up"
Bad: "Get started on your journey"
Good: "Saved"
Bad: "Your changes have been successfully persisted to the database"
```
### Conciseness
Use the fewest words needed to communicate clearly.
```
Good: "Password must be 8+ characters"
Bad: "Please ensure that your password contains a minimum of eight characters"
Good: "Email sent"
Bad: "Your email has been sent successfully"
```
### Usefulness
Every word should help the user accomplish their goal.
```
Good: "Enter the 6-digit code from your authenticator app"
Bad: "Enter code"
Good: "We'll email you a receipt"
Bad: "Transaction complete"
```
### Conversational (But Professional)
Write like you speak, while maintaining appropriateness.
```
Good: "Looks like that email doesn't exist. Want to create an account?"
Bad: "Error 404: User not found in database"
Good: "We couldn't find any results for 'xyz'"
Bad: "Your search query returned zero results"
```
---
## Voice and Tone
### Voice vs. Tone
**Voice** = Your brand's personality (consistent)
**Tone** = How you adapt to context (varies)
```
Voice: Friendly, helpful, professional
Tone varies:
- Onboarding: Welcoming, encouraging
- Errors: Empathetic, solution-focused
- Success: Celebratory (subtly)
- Security: Serious, trustworthy
```
### Developing Brand Voice
Document your voice characteristics:
| Attribute | We are... | We are NOT... |
|-----------|-----------|---------------|
| Friendly | Warm, approachable | Overly casual, unprofessional |
| Helpful | Clear, guiding | Condescending, verbose |
| Confident | Assured, direct | Arrogant, pushy |
| Human | Conversational, real | Robotic, corporate |
### Adapting Tone
```
Context: Error Message
Voice: Helpful
Tone: Empathetic + Solution-focused
"Something went wrong on our end. We're looking into it.
Your work has been saved—you won't lose anything."
```
---
## Writing for Common UI Elements
### Buttons
```
Good Button Labels:
- "Sign up" / "Log in" (standard actions)
- "Add to cart" / "Buy now" (specific actions)
- "Save changes" / "Save" (what happens)
- "Send message" (what it does)
- "Try free for 14 days" (value proposition)
Avoid:
- "Submit" (too generic)
- "Click here" (not descriptive, accessibility issue)
- "Yes" / "No" (require context)
- "OK" (too generic for important actions)
```
### Error Messages
```
Structure: What happened + How to fix it
Good:
"We couldn't log you in. Check your email and password and try again."
"That email is already registered. Log in or reset your password."
"Your card was declined. Try a different payment method."
Avoid:
"Error"
"Invalid credentials"
"Error 422: Unprocessable entity"
```
### Empty States
```
Components:
1. What this area is for
2. Why it's empty
3. How to fill it
Example:
"No projects yet
Projects help you organize your work. Create your first project
to get started.
[Create project]"
```
### Success Messages
```
Good:
"Password updated" ✓
"Order placed! We'll email you when it ships."
"Welcome aboard! Let's set up your workspace."
Keep it brief—user wants to move on.
Provide next steps when helpful.
```
### Loading States
```
Generic:
"Loading..."
Contextual (better):
"Finding nearby restaurants..."
"Saving your changes..."
"Connecting to server..."
Humorous (use carefully):
"Reticulating splines..."
"Convincing pixels to behave..."
```
### Confirmation Dialogs
```
Structure:
- Clear headline stating action
- Brief explanation of consequences
- Actionable button labels
Example:
"Delete this project?
This will permanently delete 'Marketing Q4' and all its files.
This can't be undone.
[Cancel] [Delete project]"
Not:
"Are you sure?"
[Yes] [No]
```
---
## Form Copy
### Labels
```
Good:
"Email address"
"Password"
"First name"
Avoid:
"Your email address"
"Enter your password"
"Please provide first name"
```
### Placeholder Text
```
Use for examples, not labels:
"you@example.com"
"555-123-4567"
"Search products..."
Don't use as the only label (disappears on focus)
```
### Help Text
```
Place below input or as tooltip:
"Password must be at least 8 characters"
"We'll only contact you about your order"
"Optional—for faster shipping estimates"
```
### Validation Messages
```
Specific and helpful:
"Enter a valid email (e.g., name@company.com)"
"Phone number must be 10 digits"
"Passwords don't match"
Not:
"Invalid input"
"Please check this field"
"Error in form"
```
---
## Accessibility in Writing
### Link Text
```
Good:
"Read our privacy policy"
"Download the report (PDF, 2.3 MB)"
"Learn more about pricing"
Avoid:
"Click here"
"Read more"
"Here" (as standalone link)
```
### Alt Text
```
Informative images:
"Bar chart showing 40% increase in sales from Q3 to Q4 2024"
Decorative images:
alt="" (empty)
Functional images (buttons/links):
"Search" (not "magnifying glass icon")
```
### Headings
```
Use logical hierarchy for screen readers:
<h1>Account Settings</h1>
<h2>Profile</h2>
<h2>Security</h2>
<h3>Password</h3>
<h3>Two-factor authentication</h3>
```
---
## Localization Considerations
### Writing for Translation
```
Avoid idioms:
"Piece of cake" → "Easy"
"Kill two birds" → "Accomplish both"
"Ball is in your court" → "Your turn to decide"
Keep text expandable (30-40% expansion typical):
English: "Settings"
German: "Einstellungen"
Avoid text in images (can't be translated)
Don't embed variables mid-sentence:
"You have 5 messages" (number placement varies by language)
→ "Messages: 5"
```
### Cultural Sensitivity
- Date formats vary (MM/DD/YYYY vs. DD/MM/YYYY)
- Color meanings differ across cultures
- Gestures and icons may have different connotations
- Humor often doesn't translate
---
## AI Tools for UX Writing
### Content Generation
**ChatGPT / Claude:**
- Brainstorm alternative phrasings
- Generate variation options
- Check tone consistency
**Writer / Jasper:**
- Brand voice enforcement
- Style guide compliance
- Terminology consistency
### Quality Checking
**Grammarly:**
- Grammar and spelling
- Clarity suggestions
- Tone detection
**Hemingway Editor:**
- Readability score
- Sentence complexity
- Passive voice detection
### Important Note
AI is a co-pilot, not a replacement. Always:
- Review and edit AI suggestions
- Ensure brand voice consistency
- Test with real users
- Consider context AI might miss
---
## Testing Microcopy
### A/B Testing
Test button text:
```
A: "Sign up free"
B: "Get started"
C: "Create account"
```
Measure: Click-through rate, completion rate
### Usability Testing
- Can users complete the task?
- Do they understand what's happening?
- Any confusion or hesitation?
- What do they expect to happen next?
### Preference Testing
"Which of these messages is clearer?"
"Which feels more appropriate for this situation?"
---
## Content Design Process
### 1. Research
- Understand user needs and language
- Analyze existing content
- Review support tickets and FAQs
- Conduct user interviews
### 2. Strategy
- Define voice and tone
- Create content guidelines
- Map user journeys
- Identify content touchpoints
### 3. Write
- Draft multiple options
- Collaborate with design
- Iterate with feedback
- Consider edge cases
### 4. Validate
- Usability testing
- A/B testing
- Accessibility review
- Stakeholder review
### 5. Maintain
- Update as product evolves
- Track metrics
- Gather ongoing feedback
- Keep guidelines current
---
## Resources
### Books
- "Microcopy: The Complete Guide" - Kinneret Yifrah
- "Strategic Writing for UX" - Torrey Podmajersky
- "Conversational Design" - Erika Hall
### Courses
- UX Content Collective - UX Writing Fundamentals
- UX Writing Hub - UX Writing Academy
- Coursera - Microcopy & UX Writing
### Communities
- UX Writing Hub (Slack)
- Content Design (Slack)
- r/UXWriting (Reddit)
---
## Sources
- [UX Writing Hub](https://uxwritinghub.com/)
- [Nielsen Norman Group - Writing for the Web](https://www.nngroup.com/topic/writing-web/)
- [Google Design - Writing](https://design.google/library/ux-writing)
- [Shopify Polaris - Voice and Tone](https://polaris.shopify.com/foundations/voice-and-tone)
references/13b-canvas-objects-performance.md
# Canvas Apps: Objects, Performance & Patterns
This reference covers smart guides, layer management, canvas elements, performance optimization, whiteboard-specific patterns, and accessibility for canvas-based applications.
---
## Smart Guides & Snapping
### Alignment Guides
```javascript
function calculateAlignmentGuides(draggedObject, allObjects) {
const guides = [];
const threshold = 5; // Snap within 5px
allObjects.forEach(other => {
if (other === draggedObject) return;
// Horizontal center alignment
if (Math.abs(draggedObject.centerY - other.centerY) < threshold) {
guides.push({
type: 'horizontal',
y: other.centerY,
from: Math.min(draggedObject.left, other.left),
to: Math.max(draggedObject.right, other.right)
});
}
// Vertical center alignment
if (Math.abs(draggedObject.centerX - other.centerX) < threshold) {
guides.push({
type: 'vertical',
x: other.centerX,
from: Math.min(draggedObject.top, other.top),
to: Math.max(draggedObject.bottom, other.bottom)
});
}
// Edge alignments (top, bottom, left, right)
// ... similar checks for edges
});
return guides;
}
```
### Smart Guide Visuals
```css
/* Alignment guides */
.smart-guide {
position: absolute;
background: #f43f5e;
pointer-events: none;
z-index: 1000;
}
.smart-guide--horizontal {
height: 1px;
width: 100%;
}
.smart-guide--vertical {
width: 1px;
height: 100%;
}
/* Distance indicator */
.distance-indicator {
position: absolute;
padding: 2px 6px;
background: #f43f5e;
color: white;
font-size: 11px;
border-radius: 3px;
white-space: nowrap;
}
```
### Grid Snapping
```javascript
const GRID_SIZE = 8; // 8px grid
function snapToGrid(value) {
return Math.round(value / GRID_SIZE) * GRID_SIZE;
}
function snapPosition(x, y) {
return {
x: snapToGrid(x),
y: snapToGrid(y)
};
}
```
### Snapping Options
```
┌─────────────────────────────────────────────┐
│ Snapping (⌘;) [Toggle] │
├─────────────────────────────────────────────┤
│ ✓ Snap to grid │
│ ✓ Snap to objects │
│ ✓ Snap to guides │
│ Snap to pixel │
├─────────────────────────────────────────────┤
│ Grid size: [8px ▼] │
└─────────────────────────────────────────────┘
```
---
## Layers & Hierarchy
### Layer Panel
```
┌─────────────────────────────────────────────────────┐
│ Layers [+] [⋮] │
├─────────────────────────────────────────────────────┤
│ ▼ 📁 Header 👁 🔒 │
│ │ ├─ 📄 Logo 👁 🔓 │
│ │ ├─ 📄 Navigation 👁 🔓 │
│ │ └─ 📄 Search 👁 🔓 │
│ ▶ 📁 Main Content 👁 🔓 │
│ ▶ 📁 Footer 👁 🔓 │
│ 📄 Background 👁 🔓 │
└─────────────────────────────────────────────────────┘
```
```css
/* Layer item */
.layer-item {
display: flex;
align-items: center;
padding: 4px 8px;
cursor: pointer;
}
.layer-item:hover {
background: #f3f4f6;
}
.layer-item--selected {
background: #dbeafe;
}
.layer-item--dragging {
opacity: 0.5;
}
/* Indent for hierarchy */
.layer-item[data-depth="1"] { padding-left: 20px; }
.layer-item[data-depth="2"] { padding-left: 36px; }
.layer-item[data-depth="3"] { padding-left: 52px; }
```
### Z-Index Operations
| Shortcut | Action |
|----------|--------|
| Cmd/Ctrl + ] | Bring forward |
| Cmd/Ctrl + [ | Send backward |
| Cmd/Ctrl + Shift + ] | Bring to front |
| Cmd/Ctrl + Shift + [ | Send to back |
### Grouping
```javascript
function groupSelection() {
const group = {
type: 'group',
id: generateId(),
children: [...selectedObjects],
bounds: calculateBounds(selectedObjects)
};
// Remove from root, add to group
selectedObjects.forEach(obj => {
removeFromCanvas(obj);
obj.parent = group;
});
addToCanvas(group);
setSelection([group]);
}
function ungroupSelection() {
selectedObjects.forEach(group => {
if (group.type !== 'group') return;
group.children.forEach(child => {
child.parent = null;
addToCanvas(child);
});
removeFromCanvas(group);
});
}
```
| Shortcut | Action |
|----------|--------|
| Cmd/Ctrl + G | Group selection |
| Cmd/Ctrl + Shift + G | Ungroup selection |
| Double-click | Enter group (edit children) |
| Escape | Exit group |
---
## Canvas Elements
### Sticky Notes
Common in whiteboard apps.
```html
<div class="sticky-note" style="--sticky-color: #fef08a;">
<div class="sticky-note__content" contenteditable>
Type your idea here...
</div>
<div class="sticky-note__author">Alice</div>
</div>
```
```css
.sticky-note {
width: 200px;
min-height: 200px;
padding: 16px;
background: var(--sticky-color);
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.15);
border-radius: 2px;
font-family: 'Comic Sans MS', cursive; /* Classic sticky feel */
}
.sticky-note__content {
font-size: 16px;
line-height: 1.4;
outline: none;
}
.sticky-note__author {
position: absolute;
bottom: 8px;
right: 8px;
font-size: 11px;
opacity: 0.6;
}
```
**Sticky Note Colors:**
- Yellow (#fef08a) - General ideas
- Pink (#fbcfe8) - Questions
- Blue (#bfdbfe) - Insights
- Green (#bbf7d0) - Actions
- Orange (#fed7aa) - Concerns
### Connectors & Arrows
```javascript
// Simple line connector
function drawConnector(from, to, ctx) {
// Calculate connection points (center of objects)
const start = { x: from.centerX, y: from.centerY };
const end = { x: to.centerX, y: to.centerY };
// Draw line
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
// Draw arrowhead
const angle = Math.atan2(end.y - start.y, end.x - start.x);
drawArrowhead(end, angle, ctx);
}
```
### Shapes Library
```
┌─────────────────────────────────────────────────────┐
│ Shapes │
├─────────────────────────────────────────────────────┤
│ Basic │
│ ┌─┐ ○ ◇ △ ─ → ⬭ │
│ │
│ Flowchart │
│ ▭ ◇ ⬭ ▱ ⬡ │
│ │
│ Arrows │
│ → ⇒ ↔ ↕ ↗ │
└─────────────────────────────────────────────────────┘
```
---
## Performance Optimization
### Viewport Culling
Only render objects visible in the current viewport.
```javascript
function getVisibleObjects(camera, objects) {
const viewport = getViewportBounds(camera);
return objects.filter(obj =>
intersects(obj.bounds, viewport)
);
}
function render() {
const visibleObjects = getVisibleObjects(camera, allObjects);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
// Apply camera transform
ctx.translate(camera.x, camera.y);
ctx.scale(camera.zoom, camera.zoom);
// Only render visible objects
visibleObjects.forEach(obj => obj.render(ctx));
ctx.restore();
}
```
### Level of Detail (LOD)
Simplify rendering at low zoom levels.
```javascript
function renderObject(obj, ctx, zoom) {
if (zoom < 0.1) {
// Very zoomed out: just show bounding box
ctx.fillRect(obj.x, obj.y, obj.width, obj.height);
} else if (zoom < 0.5) {
// Moderately zoomed out: simplified render
obj.renderSimplified(ctx);
} else {
// Normal/zoomed in: full detail
obj.renderFull(ctx);
}
}
```
### Performance Guidelines
| Technique | When to Apply |
|-----------|--------------|
| Viewport culling | Always for >100 objects |
| LOD rendering | Zoom levels <50% |
| Debounced render | Rapid camera movement |
| Canvas layering | Static vs. dynamic content |
| Web Workers | Heavy calculations |
| Virtual scrolling | Layer panel with 1000+ items |
### Canvas Size Limits
```
Practical limits:
- Browser canvas max: ~16,000 x 16,000 pixels
- Virtual canvas: Unlimited (render only visible portion)
- Object count: 10,000+ with proper culling
- Performance target: 60fps during pan/zoom
```
---
## Whiteboard-Specific Patterns
### Timer / Timeboxing
```html
<div class="timer">
<div class="timer__display">5:00</div>
<div class="timer__controls">
<button class="timer__btn--start">▶</button>
<button class="timer__btn--pause">⏸</button>
<button class="timer__btn--reset">↺</button>
</div>
</div>
```
### Voting Sessions
```javascript
const votingSession = {
id: "vote-123",
topic: "Which design do we prefer?",
options: [
{ id: "a", label: "Option A", votes: [] },
{ id: "b", label: "Option B", votes: [] }
],
anonymous: true,
allowMultiple: false,
endTime: new Date("2024-12-20T14:00:00")
};
function castVote(sessionId, optionId, userId) {
const session = getSession(sessionId);
const vote = session.anonymous ? "anonymous" : userId;
session.options.find(o => o.id === optionId).votes.push(vote);
broadcastVoteUpdate(session);
}
```
### Facilitation Mode
Presenter controls for workshops.
```
┌─────────────────────────────────────────────────────┐
│ Facilitation Tools │
├─────────────────────────────────────────────────────┤
│ [Spotlight] [Timer] [Vote] [Lock Canvas] │
│ │
│ Participants: 12 active │
│ [Mute all annotations] [Bring everyone to me] │
└─────────────────────────────────────────────────────┘
```
### Clustering / Affinity Mapping
```javascript
// Auto-cluster similar sticky notes
function clusterStickies(stickies) {
const clusters = [];
stickies.forEach(sticky => {
// Find cluster by proximity or AI similarity
const nearbyCluster = findNearbyCluster(sticky, clusters);
if (nearbyCluster) {
nearbyCluster.add(sticky);
} else {
clusters.push(new Cluster([sticky]));
}
});
return clusters;
}
```
---
## Accessibility
### Keyboard Navigation
```javascript
// Full keyboard support for canvas
const keyboardControls = {
'Tab': () => selectNextObject(),
'Shift+Tab': () => selectPreviousObject(),
'Enter': () => enterEditMode(),
'Escape': () => exitEditMode(),
'Delete': () => deleteSelection(),
'ArrowUp': () => moveSelection(0, -1),
'ArrowDown': () => moveSelection(0, 1),
'ArrowLeft': () => moveSelection(-1, 0),
'ArrowRight': () => moveSelection(1, 0),
'Cmd+A': () => selectAll(),
'Cmd+Z': () => undo(),
'Cmd+Shift+Z': () => redo()
};
```
### Screen Reader Support
```html
<!-- Announce canvas state changes -->
<div role="application" aria-label="Design canvas">
<div aria-live="polite" class="sr-only" id="canvas-announcer">
<!-- Dynamic announcements -->
</div>
<div
role="img"
tabindex="0"
aria-describedby="canvas-description"
>
<canvas></canvas>
</div>
</div>
<script>
function announceSelection(objects) {
const announcer = document.getElementById('canvas-announcer');
announcer.textContent = `Selected ${objects.length} objects`;
}
</script>
```
### Drag and Drop Alternatives
Per WCAG 2.5.7, provide non-dragging alternatives:
```html
<!-- Context menu for move operations -->
<menu class="context-menu">
<button>Move to...</button>
<button>Bring to front</button>
<button>Send to back</button>
<button>Align left</button>
<button>Align center</button>
<button>Align right</button>
</menu>
```
---
## Key Metrics
| Metric | Value | Context |
|--------|-------|---------|
| Grid size | 4, 8, or 16px | Common options |
| Snap threshold | 2-8px | Distance before snapping |
| Undo stack | 100+ actions | Memory permitting |
| Object limit | 10,000+ | With culling |
---
## Anti-Patterns
1. **No keyboard navigation** - Excludes users who can't use mouse
2. **Missing undo** - Critical for experimental exploration
3. **No snapping controls** - Users need to disable snapping sometimes
4. **Invisible layers** - Hard to find/select hidden objects
5. **No minimap** - Lost on large canvases
---
## Sources
- [Figma Engineering Blog](https://www.figma.com/blog/category/engineering/)
- [Miro - Infinite Canvas](https://miro.com/online-canvas-for-design/)
- [Konva.js Documentation](https://konvajs.org/)
- [LogRocket - Drag and Drop UI](https://blog.logrocket.com/ux-design/drag-and-drop-ui-examples/)
- [Cloudscape - Drag and Drop Patterns](https://cloudscape.design/patterns/general/drag-and-drop/)
references/14-ai-ux-patterns.md
# AI & LLM UX Patterns
AI-powered interfaces are now mainstream but have unique UX challenges. This reference covers design patterns for conversational AI, copilots, agents, and generative interfaces.
---
## AI as a Respectful Copilot, Not an Autopilot
The defining 2026 framing (NN/g *State of UX 2026* and broad trend consensus): the
best AI features are a **respectful copilot**—present and helpful, but optional and
under user control—rather than an **autopilot** that hijacks the flow. As raw UI
becomes a commodity, the differentiator is judgment about *when not to act*.
**What "respectful copilot" looks like:**
- **Optional, not forced** — AI is offered (a sidebar, overlay, collapsible panel,
inline suggestion), never shoved into the critical path. The user can ignore it
and still complete the task the normal way.
- **Calm and peripheral** — it augments the current context instead of taking over
the screen; suggestions sit beside the work, not on top of it.
- **User stays in control** — the human initiates or explicitly accepts; AI
proposes, the user disposes. Every AI action is previewable, reversible, and
attributable (see "Hidden AI", "Over-automation", "No AI undo" anti-patterns).
- **Transparent** — clearly labeled as AI, honest about confidence and limits,
shows its sources/reasoning when it matters.
```
Autopilot (avoid) Respectful copilot (prefer)
───────────────── ──────────────────────────
Auto-rewrites your text Suggests an edit you can accept/dismiss
Modal hijacks the screen Quiet panel beside your work
Acts, then maybe tells you Proposes, you confirm, then it acts
"AI is doing X…" (opaque) Shows what, why, and an undo
```
Apply this lens to every pattern below: chat, copilots, and agents should all
default to *offered and reversible*, never *imposed and silent*.
---
## Conversational / Chat UI
### Message Layout
```
┌─────────────────────────────────────────┐
│ User message [14:02] │
│ Right-aligned, accent background │
├─────────────────────────────────────────┤
│ [AI avatar] │
│ AI response [14:02] │
│ Left-aligned, neutral background │
│ ┌─────────────────────────────┐ │
│ │ Source: document.pdf, p.12 │ │
│ └─────────────────────────────┘ │
│ [👍] [👎] [Copy] [Regenerate] │
├─────────────────────────────────────────┤
│ ┌───────────────────────────────────┐ │
│ │ Type a message... [Send] │ │
│ │ [Attach] [Voice] │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
### Message Bubbles
- **User messages**: Right-aligned, accent color background, rounded corners
- **AI messages**: Left-aligned, neutral/light background, distinct from user
- **System messages**: Centered, muted, no bubble (e.g., "Conversation started")
- **Max width**: 70-80% of container to maintain readability
- **Timestamps**: Show on hover or at natural breaks, not every message
### Streaming Responses
```
AI is typing...
├── Show cursor/blinking indicator at end of text
├── Render markdown incrementally (not raw then formatted)
├── Allow user to stop generation mid-stream [Stop] button
├── Smooth text appearance (word-by-word or chunk-by-chunk)
└── Don't auto-scroll if user has scrolled up to read history
```
**Key timings:**
- First token should appear within 500ms-1s (perceived responsiveness)
- Show typing indicator immediately on send
- Skeleton/placeholder for structured outputs (tables, code blocks)
### Turn-Taking
- Disable send button while AI is responding (or allow interrupt)
- Clear visual separation between turns
- Support editing previous user messages and re-generating from that point
- Show "AI is thinking..." for multi-step reasoning delays
- Multi-turn context: show thread/conversation history clearly
---
## Prompt UX
### Input Design
```html
<!-- Expandable textarea that grows with content -->
<div class="prompt-input">
<textarea
placeholder="Ask anything..."
rows="1"
aria-label="Message input"
></textarea>
<div class="input-actions">
<button aria-label="Attach file">📎</button>
<button aria-label="Send message" disabled>→</button>
</div>
</div>
```
**Best practices:**
- Auto-expanding textarea (start 1 row, grow to ~6, then scroll)
- Send on Enter, Shift+Enter for newline (document this with a tooltip)
- Enable send button only when input is non-empty
- Character/token count for limited contexts
- Support paste of images, files, and rich text
### Suggested Prompts
```
┌─────────────────────────────────────────┐
│ What can I help you with? │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Summarize │ │ Compare options │ │
│ │ this doc │ │ for deployment │ │
│ └──────────┘ └──────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Write a test │ │ Explain this │ │
│ │ for this fn │ │ error │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────┘
```
- Show 3-6 contextual suggestions on empty state
- Update suggestions based on current context (file open, selection, etc.)
- Use prompt templates for complex tasks with fill-in-the-blank slots
- Allow users to save and reuse their own prompt templates
- Provide category-based prompt libraries for discoverability
### Multimodal Inputs
- Image upload with preview and crop/annotate before sending
- File attachment with type indicators and size display
- Voice input with waveform visualization and transcription preview
- Screen/selection capture with annotation tools
- Drag-and-drop support with clear drop zone indicators
---
## AI Transparency & Trust
### Confidence Indicators
```
High confidence:
"The capital of France is Paris."
Medium confidence:
"Based on available data, revenue likely increased ~15%."
⚠️ This estimate is based on partial data
Low confidence:
"I'm not certain, but this might be related to..."
⚠️ Low confidence — verify independently
```
**Pattern options:**
- Textual hedging ("I believe", "Based on...", "I'm not sure")
- Visual indicators (confidence bars, color-coded borders)
- Explicit disclaimers for uncertain outputs
- "Verified" badges for factual claims with sources
### Source Attribution
```
┌─────────────────────────────────────────┐
│ AI Response text here with inline │
│ citations [1] and references [2]. │
│ │
│ ┌─ Sources ─────────────────────────┐ │
│ │ [1] design-system.md, line 42 │ │
│ │ [2] api-docs.md, §Authentication │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
- Inline citations linked to expandable source cards
- Distinguish between direct quotes and paraphrasing
- Show source reliability/recency metadata
- Allow click-through to original source
- Group sources by type (documents, web, code)
### "AI-Generated" Labels
- Mark AI-generated content clearly: "Generated by AI" or sparkle icon (✨)
- Distinguish AI suggestions from confirmed/human-edited content
- Persist labels through copy/paste and export
- Follow platform regulations (EU AI Act requires labeling)
---
## Hallucination Handling
### Prevention UX
- Ground responses in user-provided context (RAG patterns)
- Show "based on" indicators linking to source material
- Limit AI to domains where it has verified information
- Offer "search the web" as explicit fallback for unknown topics
### Detection & Recovery
```
┌─────────────────────────────────────────┐
│ AI: "The function `getData()` returns │
│ a Promise<User[]>." │
│ │
│ ⚠️ Could not verify this in your │
│ codebase. [Check source] [Report] │
│ │
│ Was this response helpful? │
│ [👍 Yes] [👎 No — incorrect] │
└─────────────────────────────────────────┘
```
- Provide feedback mechanisms (thumbs up/down, flag as incorrect)
- "Regenerate" button to get a fresh response
- User correction flow: allow editing AI output directly
- Track and surface known inaccuracies for similar future queries
- Show verification status for factual claims when possible
---
## Human-in-the-Loop
### Approval Workflows
```
┌─────────────────────────────────────────┐
│ AI Suggestion │
│ ┌───────────────────────────────────┐ │
│ │ Proposed changes: │ │
│ │ - Update user.email to new value │ │
│ │ - Send confirmation email │ │
│ │ - Log change in audit trail │ │
│ └───────────────────────────────────┘ │
│ │
│ [✓ Apply All] [Review Each] [✗ Cancel] │
└─────────────────────────────────────────┘
```
### Suggestion vs. Auto-Action Spectrum
```
Full manual ←────────────────────→ Full auto
Suggest → Suggest+ → Auto+ → Auto
only default apply notify silent
User User can User can No user
initiates reject review action
action suggestion after needed
```
**Best practices:**
- Default to "suggest" mode for destructive or irreversible actions
- Allow users to configure automation level per action type
- Show clear diff/preview before applying changes
- Provide undo for auto-applied actions (minimum 10 seconds)
- Log all AI-initiated actions for auditability
### Override Patterns
- Always provide manual override for AI decisions
- Make overrides discoverable (not hidden in settings)
- Don't penalize users for overriding (no "Are you sure?" friction)
- Learn from overrides to improve future suggestions
- Show "AI suggested X, you chose Y" in history for context
---
## Generative UI
### AI-Generated Interface Elements
- Render structured AI outputs as rich components (tables, charts, forms)
- Provide fallback to plain text if rendering fails
- Allow user to toggle between rendered and raw views
- Maintain accessibility in generated components
### Adaptive Layouts
```
Simple query → Single text response
Data query → Table or chart + summary
Comparison → Side-by-side cards
Multi-step → Numbered steps with progress
Code query → Syntax-highlighted block with copy button
```
- Match output format to query intent
- Let users request different output formats ("show as table")
- Remember format preferences per query type
---
## AI Copilot Patterns
### Inline Suggestions
```
┌──────────────────────────────────────────┐
│ function calculateTotal(items) { │
│ return items.reduce((sum, item) => │
│ ░░sum + item.price * item.qty, 0);░░ │ ← ghost text
│ } │
│ │
│ [Tab] to accept [Esc] to dismiss │
└──────────────────────────────────────────┘
```
**Ghost text / inline completion:**
- Show suggestion as dimmed/ghost text at cursor position
- Tab to accept, Esc to dismiss, keep typing to refine
- Accept word-by-word with Ctrl+Right (partial accept)
- Show suggestion after 300-750ms pause in typing (debounce)
- Never show during active, rapid typing
### Accept / Reject / Modify
- **Accept**: Tab or click — apply suggestion immediately
- **Reject**: Esc or keep typing — dismiss silently
- **Modify**: Accept then edit — encourage iteration
- **Partial accept**: Accept first N words/lines
- Show acceptance rate metrics to tune suggestion quality
### Suggestion Panel (Multi-Option)
```
┌─ AI Suggestions ────────────────────────┐
│ │
│ Option 1: Using array.filter() │
│ ┌────────────────────────────────────┐ │
│ │ const active = users.filter(u => │ │
│ │ u.status === 'active'); │ │
│ └────────────────────────────────────┘ │
│ │
│ Option 2: Using for...of loop │
│ ┌────────────────────────────────────┐ │
│ │ const active = []; │ │
│ │ for (const u of users) { ... │ │
│ └────────────────────────────────────┘ │
│ │
│ [Apply #1] [Apply #2] [Dismiss] │
└──────────────────────────────────────────┘
```
---
## Agent UX
### Multi-Step Task Visibility
```
┌─ Agent Task: "Deploy to staging" ───────┐
│ │
│ ✅ Step 1: Run tests (12s) │
│ ✅ Step 2: Build application (45s) │
│ 🔄 Step 3: Push to registry ... │
│ ⬚ Step 4: Update deployment │
│ ⬚ Step 5: Run health checks │
│ │
│ ├── Elapsed: 1m 23s │
│ └── [View Logs] [Pause] [Cancel] │
└──────────────────────────────────────────┘
```
### Progress & Autonomy Controls
- Show current step, total steps, and elapsed time
- Expandable logs/details for each step
- Pause/Resume to give users control over long-running tasks
- Cancel with confirmation for destructive operations
- Estimated remaining time when possible
### Tool-Use Transparency
```
┌─────────────────────────────────────────┐
│ 🔧 Agent used: file_search │
│ Searched 24 files matching "*.ts" │
│ Found 3 relevant results │
│ [Show details ▾] │
│ │
│ 🔧 Agent used: code_edit │
│ Modified: src/utils/auth.ts │
│ [View diff ▾] │
└─────────────────────────────────────────┘
```
- Show each tool invocation with name and brief description
- Collapsible detail view (inputs, outputs, duration)
- Highlight side effects (file writes, API calls, etc.)
- Permission prompts before sensitive tool use
- Full audit log accessible after task completion
---
## Ethical AI UX
### Bias Disclosure
- Acknowledge known limitations and biases upfront
- Provide model card information accessible from the UI
- Show training data recency ("Knowledge current through [date]")
- Flag outputs in sensitive domains (medical, legal, financial)
### User Control Over AI Behavior
```
┌─ AI Preferences ────────────────────────┐
│ │
│ Response style: [Concise ▾] │
│ Creativity: ████░░░░░ 40% │
│ Auto-suggest: [✓ Enabled] │
│ Data usage: [View policy] │
│ │
│ [Reset to defaults] │
└──────────────────────────────────────────┘
```
- Let users adjust tone, verbosity, creativity
- Opt-out of data collection for model training
- Clear "forget this conversation" action
- Per-conversation or global preference settings
- Transparent about what data is retained and for how long
### Opt-Out Mechanisms
- Easy toggle to disable AI features entirely
- Graceful degradation to manual workflows
- No penalty or reduced functionality for opting out
- Clear explanation of what changes when AI is disabled
- Respect opt-out across sessions and devices
---
## Key Metrics
| Metric | Target | Context |
|--------|--------|---------|
| Time to first token | < 1s | Perceived responsiveness |
| Streaming speed | 30-80 tokens/s | Natural reading pace |
| Suggestion acceptance rate | 25-35% | Copilot usefulness |
| Response rating (thumbs up) | > 80% | Quality benchmark |
| Regeneration rate | < 15% | First-response quality |
| Task completion (agents) | > 90% | Reliability |
| Hallucination rate | < 5% | Factual accuracy |
| Human override rate | Track trend | Automation calibration |
---
## Anti-Patterns
1. **Anthropomorphizing too much** — Avoid names, personalities, or claims of emotions that mislead users about AI capabilities
2. **No streaming** — Showing nothing then dumping a wall of text feels broken
3. **Hidden AI** — Users should always know when they're interacting with AI
4. **Ignoring errors** — Silently failing or making up answers instead of admitting uncertainty
5. **Over-automation** — Applying AI changes without user awareness or consent
6. **No undo for AI actions** — AI-applied changes must be reversible
7. **Prompt injection vulnerability** — Showing AI outputs that can execute code or alter UI
8. **Infinite context illusion** — Not communicating context window limits
9. **Blocking UI during generation** — Users should be able to navigate or cancel
10. **No feedback loop** — Not providing thumbs up/down or correction mechanisms
---
## Sources
- [Nielsen Norman Group: UX for AI](https://www.nngroup.com/articles/ai-ux/) — AI interaction research
- [Nielsen Norman Group: State of UX](https://www.nngroup.com/articles/) — annual UX trend analysis (respectful-copilot framing)
- [Google PAIR: People + AI Guidebook](https://pair.withgoogle.com/guidebook) — AI design patterns
- [Apple: Human Interface Guidelines for Machine Learning](https://developer.apple.com/design/human-interface-guidelines/machine-learning)
- [Microsoft: HAX Toolkit](https://www.microsoft.com/en-us/haxtoolkit/) — Human-AI interaction guidelines
- [Anthropic: Responsible AI Practices](https://www.anthropic.com/) — Safety and transparency
- [EU AI Act](https://artificialintelligenceact.eu/) — Regulatory requirements for AI interfaces
references/15-ethical-design.md
# Ethical Design & Dark Patterns
Deceptive design patterns face increasing regulatory enforcement with real financial penalties. This reference provides a taxonomy of dark patterns, the regulatory landscape, and practical guidance for ethical design.
---
## Dark Pattern Taxonomy
### 1. Roach Motel
Easy to get into, hard to get out of.
```
❌ Sign up: 1 click → Cancel: call a phone number during business hours
✅ Sign up: 1 click → Cancel: 1 click in the same place
```
- Subscription cancellation buried in settings or requiring phone calls
- Account deletion requiring email to support team
- Free trial that auto-converts with no easy cancellation
### 2. Confirmshaming
Using guilt-laden language to manipulate decisions.
```
❌ "No thanks, I don't want to save money"
❌ "I'll pass on protecting my family"
✅ "No thanks" / "Not now" / "Skip"
```
- Opt-out buttons using shame language
- Modal dismissals that imply user is making a bad choice
- Emotional manipulation in decline copy
### 3. Misdirection
Drawing attention away from important information.
```
❌ Large "Accept All" button, tiny "Manage preferences" link
✅ Equal-sized "Accept" and "Reject" buttons side by side
```
- Visual emphasis on the choice that benefits the company
- Using color, size, and position to guide toward preferred option
- Burying important notices in walls of text
### 4. Forced Continuity
Silently charging after a free trial without adequate notice.
```
❌ Free trial ends → auto-charge with no warning
✅ Free trial ends → email 7 days before → email 1 day before → charge
```
- No reminder before trial-to-paid conversion
- Making it hard to find the charge after conversion
- Requiring payment info for "free" trials without clear disclosure
### 5. Hidden Costs
Revealing unexpected charges late in the checkout process.
```
❌ $29.99 product → checkout reveals $8.99 shipping + $4.99 "service fee"
✅ Show total cost including all fees from the product page
```
- Drip pricing (adding fees throughout checkout)
- Service fees, processing fees, convenience fees added at the end
- Mandatory add-ons pre-selected in checkout
### 6. Trick Questions
Confusing wording that causes users to make unintended choices.
```
❌ "Uncheck this box if you prefer not to not receive emails"
✅ "Check this box to receive marketing emails" (unchecked by default)
```
- Double negatives in opt-out language
- Confusing toggle labels (is "on" opting in or out?)
- Swapping the expected position of yes/no buttons
### 7. Bait & Switch
Promising one thing and delivering another.
```
❌ "Free download" → requires paid subscription to access
✅ Clear pricing and feature comparison before any action
```
- Advertising features only available in premium tiers
- Changing terms after user commitment
- "Free" products that require payment for core functionality
### 8. Disguised Ads
Ads designed to look like content or navigation elements.
```
❌ "Download" buttons that are actually ads
✅ Clearly labeled "Advertisement" or "Sponsored" with visual distinction
```
- Native ads without clear sponsorship labels
- Fake download buttons or system alerts
- Content recommendation widgets that mix ads with real content
### 9. Friend Spam
Using access to contacts for unauthorized messaging.
```
❌ "Find friends" → sends invites to entire address book without review
✅ "Find friends" → shows contact list for user to select individually
```
- Auto-importing and messaging contacts
- "Invite friends" defaulting to select-all
- Social sharing without clear preview of what will be posted
### 10. Privacy Zuckering
Making privacy settings confusing to encourage maximum data sharing.
```
❌ 47 pages of privacy settings across 8 different menus
✅ Single privacy dashboard with clear toggles and plain-language explanations
```
- Default to maximum data sharing
- Privacy controls scattered across multiple screens
- Confusing privacy policy language
### 11. Sneak into Basket
Adding extra items to the shopping cart without user consent.
```
❌ Pre-selected insurance, warranties, or add-ons in checkout
✅ All add-ons presented as opt-in with clear pricing
```
- Pre-checked donation or insurance boxes
- Default-selected premium shipping
- Mandatory bundles disguised as individual products
---
## Regulatory Landscape
### FTC (United States)
- **Click-to-Cancel Rule (2024)**: Must be as easy to cancel as to subscribe
- **Amazon settlement ($25M, 2023)**: Deceptive Prime enrollment/cancellation
- **Fortnite/Epic ($245M, 2022)**: Dark patterns in children's purchases
- Enforcement focus: subscriptions, negative option marketing, COPPA violations
### EU Digital Services Act (DSA)
- Prohibits dark patterns on online platforms
- Bans: deceptive UI, manipulated choices, repeated nagging, obstruction of cancellation
- Applies to: all online intermediaries, with stricter rules for very large platforms
- Effective: February 2024
### EU Digital Fairness Act (Upcoming)
- Extends dark pattern protections to all digital services
- Covers addictive design, virtual currencies, influencer marketing
- Expected proposal: 2025-2026
### GDPR (EU)
- Consent must be freely given, specific, informed, and unambiguous
- Pre-checked consent boxes are invalid
- Withdrawing consent must be as easy as giving it
- Maximum penalty: 4% of annual global turnover or €20M
### California CPRA
- Extends CCPA with additional consent requirements
- "Do Not Sell or Share My Personal Information" link required
- Equal prominence for opt-out as opt-in
- Dark pattern consent is not valid consent
### COPPA (US Children's Privacy)
- Verifiable parental consent for under-13 data collection
- No behavioral advertising to children
- FTC actively enforcing against manipulative design targeting minors
---
## Consent Design
### Button Parity
```css
/* ❌ Dark pattern: asymmetric buttons */
.accept-btn {
background: #0066cc;
color: white;
font-size: 16px;
padding: 12px 24px;
}
.reject-link {
color: #999;
font-size: 12px;
text-decoration: underline;
}
/* ✅ Ethical: equal prominence */
.consent-btn {
font-size: 16px;
padding: 12px 24px;
border-radius: 6px;
}
.consent-btn.accept {
background: #0066cc;
color: white;
}
.consent-btn.reject {
background: white;
color: #0066cc;
border: 2px solid #0066cc;
}
```
### Consent Best Practices
- Accept and reject buttons must have **equal visual weight**
- No pre-checked boxes for optional data processing
- Clear, plain-language explanation of what users are consenting to
- Granular control: let users accept some and reject others
- Withdrawing consent must be as easy as giving it (same number of clicks)
- No "consent walls" that block access unless all is accepted (unless strictly necessary)
- Record consent with timestamp and version for compliance
---
## Cookie / Privacy UX
### Banner Design
```
┌─────────────────────────────────────────────────────┐
│ We use cookies to improve your experience. │
│ [Learn more] │
│ │
│ [Accept All] [Reject All] [Manage Preferences] │
└─────────────────────────────────────────────────────┘
```
**Requirements:**
- Show on first visit only (persist choice)
- No page interaction tracking before consent
- "Reject All" must be as prominent as "Accept All"
- Brief, clear description of cookie purposes
- Link to full privacy policy
### Preference Center
```
┌─ Cookie Preferences ────────────────────┐
│ │
│ Necessary [Always on] │
│ Authentication, security, basic │
│ functionality │
│ │
│ Analytics [○ Off] │
│ Anonymous usage statistics │
│ │
│ Marketing [○ Off] │
│ Personalized ads and content │
│ │
│ Social Media [○ Off] │
│ Social sharing and embeds │
│ │
│ [Save Preferences] [Reject All] │
└──────────────────────────────────────────┘
```
- Default all optional categories to OFF
- Clear descriptions for each category
- Easy toggle interface
- "Reject All" shortcut available
- Accessible from footer link at all times (not just the banner)
---
## Subscription & Cancellation
### FTC Click-to-Cancel Rule
The cancellation process must be as simple as the sign-up process.
```
Sign-up: Product page → Click "Subscribe" → Enter payment → Done (3 steps)
Cancellation: Settings → Click "Cancel" → Confirm → Done (3 steps max)
❌ Cancel: Settings → Account → Billing → "Contact support" → Phone wait →
Agent pitch → Second pitch → "Are you sure?" → "Last chance offer" → Cancel
```
### Ethical Subscription Patterns
- Cancel button in the same location as the subscribe button
- No retention dark patterns (forced phone calls, multi-step cancellation)
- Show clear summary of what will happen on cancellation (access end date, data retention)
- Offer pause/downgrade as alternatives, not barriers
- Send cancellation confirmation immediately
- Allow easy resubscription if the user changes their mind
---
## Pricing Transparency
### Drip Pricing Avoidance
```
❌ Drip pricing:
Product: $99 → Cart: $99 + $15 shipping → Checkout: $99 + $15 + $5.99 service fee
✅ Transparent pricing:
Product: $119.99 (includes shipping & all fees)
— or —
Product: $99 + Shipping: $15 + Fees: $5.99 = Total: $119.99 (shown from start)
```
- Show total price including all mandatory fees as early as possible
- Break down fees clearly if itemized
- No surprise charges at checkout
- Currency and tax handling clear for international users
- Comparison pricing (strikethrough) must reference genuine previous prices
---
## Children & Vulnerable Users
### Age-Appropriate Design (AADC / ICO Code)
- Age verification at entry points (not just a "I am over 13" checkbox)
- Default to maximum privacy for minors
- No nudge techniques to encourage more data sharing
- No reward loops or variable reward schedules (addictive patterns)
- Parental controls accessible but not bypassable by children
- No profiling of children for personalization or advertising
### Addictive Pattern Avoidance
```
❌ Addictive patterns:
- Infinite scroll with no natural stopping point
- Pull-to-refresh with variable rewards
- Streak counters that penalize breaks
- Autoplay with no natural stopping point
- Social approval metrics (like counts) as primary feedback
✅ Ethical alternatives:
- "You're all caught up" markers
- Daily digest summaries
- "Time spent" dashboards
- Intentional session boundaries
- Content-focused rather than metric-focused design
```
---
## Ethical Design Checklist
### Pre-Launch Review
- [ ] **Symmetry**: Is it equally easy to opt out as to opt in?
- [ ] **Clarity**: Would a non-technical person understand what they're agreeing to?
- [ ] **Button parity**: Are accept/reject options equally prominent?
- [ ] **No pre-selection**: Are optional choices defaulted to off/unchecked?
- [ ] **No confirmshaming**: Is decline language neutral and respectful?
- [ ] **Pricing transparency**: Are all fees visible before the final step?
- [ ] **Cancellation ease**: Can users cancel in the same number of steps as subscribing?
- [ ] **Privacy defaults**: Are privacy settings defaulted to the most protective option?
- [ ] **Consent validity**: Is consent freely given, specific, informed, and unambiguous?
- [ ] **Children's safety**: Are there protections for underage users?
- [ ] **No misdirection**: Is the visual hierarchy honest about what options do?
- [ ] **Audit trail**: Are consent records stored with timestamps and versions?
### Ongoing Monitoring
- Track unsubscribe/cancellation completion rates (should be near 100%)
- Monitor complaint rates related to billing or consent
- Review A/B tests for dark pattern creep (optimization that exploits users)
- Regular accessibility audit of consent interfaces
- Legal review of consent flows against current regulations
---
## Key Metrics
| Metric | Target | Context |
|--------|--------|---------|
| Consent rejection rate | Monitor (not minimize) | High rejection is honest UX |
| Cancel completion rate | > 95% | Easy cancellation |
| Complaint rate | < 0.1% | Billing/consent complaints |
| Cookie preference usage | Track | Users exercising choice |
| Time to cancel | ≤ sign-up time | Click-to-cancel compliance |
---
## Sources
- [FTC Dark Patterns Report (2022)](https://www.ftc.gov/reports/bringing-dark-patterns-light) — Regulatory guidance
- [Deceptive Design](https://www.deceptive.design/) — Harry Brignull's dark pattern catalog
- [EU Digital Services Act](https://digital-strategy.ec.europa.eu/en/policies/digital-services-act-package) — Platform regulation
- [GDPR consent guidelines](https://edpb.europa.eu/our-work-tools/documents/public-consultations/2020/guidelines-052020-consent-under-regulation_en) — EDPB guidance
- [ICO Age Appropriate Design Code](https://ico.org.uk/for-organisations/childrens-code-hub/) — Children's privacy
- [Nielsen Norman Group: Dark Patterns](https://www.nngroup.com/articles/dark-patterns/) — UX research
- [CPRA regulations](https://cppa.ca.gov/) — California privacy enforcement
references/16-onboarding.md
# Onboarding & User Activation
Onboarding is the critical bridge between sign-up and engaged usage. This reference covers patterns for first-run experiences, progressive disclosure, activation metrics, and re-engagement.
---
## Onboarding Patterns
### Product Tours
```
┌─────────────────────────────────────────┐
│ ┌──────────────────────────────┐ │
│ │ Step 2 of 4 │ │
│ │ ───────────── │ │
│ │ Create your first project │ │
│ │ │ │
│ │ Click the + button to get │ │
│ │ started with a new project. │ │
│ │ │ │
│ │ [Back] [Next] [Skip tour] │ │
│ └──────────────────────────────┘ │
│ ▼ │
│ [ + New Project ] │
└─────────────────────────────────────────┘
```
**Best practices:**
- Keep tours to 3-5 steps maximum
- Highlight one feature per step with a spotlight/overlay
- Always allow skipping ("Skip tour" visible on every step)
- Point to the actual UI element being described
- Show progress indicator (step N of M)
- Trigger contextually, not on every login
### Tooltip Walkthroughs
```
┌──────────────────────┐
│ 💡 Quick tip │
│ Use / to search │
│ commands quickly. │
│ │
│ [Got it] [Show more] │
└───────┬──────────────┘
▼
┌────────────────────────────────────────┐
│ [Search commands... /] │
└────────────────────────────────────────┘
```
- Appear in context when user first encounters a feature
- One tooltip at a time (never stack)
- Dismissible and don't block interaction with the feature
- "Got it" dismisses permanently; "Show more" opens full docs
- Respect dismissals (don't re-show on next session)
### Onboarding Checklists
```
┌─ Getting Started ───────────────────────┐
│ │
│ ✅ Create your account │
│ ✅ Set up your profile │
│ 🔲 Invite a team member │
│ 🔲 Create your first project │
│ 🔲 Connect an integration │
│ │
│ ████████████░░░░░░░░ 60% complete │
│ │
│ [Dismiss checklist] │
└──────────────────────────────────────────┘
```
**Best practices:**
- 4-6 items (not overwhelming)
- Pre-check completed items (account creation counts!)
- Progress bar/percentage for motivation
- Link each item directly to the relevant action
- Dismissible but recoverable from settings
- Celebrate completion (confetti, congratulations message)
- Order items by value delivered, not complexity
### Empty States as Onboarding
```
┌─────────────────────────────────────────┐
│ │
│ 📁 │
│ No projects yet │
│ │
│ Create your first project to start │
│ collaborating with your team. │
│ │
│ [+ Create Project] │
│ │
│ Or try a template: │
│ [Marketing Plan] [Sprint Board] [Wiki] │
│ │
└─────────────────────────────────────────┘
```
- Show helpful illustration (not just "no data")
- Clear call-to-action for the logical next step
- Offer templates or sample data to reduce blank-page anxiety
- Brief explanation of what this area is for
- Never show a completely empty screen with no guidance
### Sample Data / Sandbox
- Pre-populated example data that demonstrates product value
- Clearly labeled as sample/demo data ("This is example data — delete anytime")
- Easy one-click removal when user is ready
- Shows real functionality, not just screenshots
- Avoids cluttering the user's actual workspace
---
## Progressive Disclosure
### Layered Complexity
```
Level 1 — Basic (default view):
┌────────────────────────────────┐
│ Name: [ ] │
│ Email: [ ] │
│ │
│ [Save] │
└────────────────────────────────┘
Level 2 — Show more:
┌────────────────────────────────┐
│ Name: [ ] │
│ Email: [ ] │
│ │
│ ▾ Advanced settings │
│ ┌──────────────────────────┐ │
│ │ Role: [Admin ▾] │ │
│ │ Team: [Engineering ▾] │ │
│ │ Timezone: [Auto ▾] │ │
│ └──────────────────────────┘ │
│ │
│ [Save] │
└────────────────────────────────┘
```
**Principles:**
- Show the most common options by default
- Hide advanced options behind expandable sections
- Use sensible defaults so advanced options are rarely needed
- Label advanced sections clearly ("Advanced settings", "More options")
- Persist user's disclosure preference per section
### Feature Revelation Strategies
- **Usage-based**: Unlock features as users demonstrate readiness
- **Time-based**: Introduce features over first week of usage
- **Role-based**: Show features relevant to user's selected role
- **Need-based**: Surface features when context suggests need
- Avoid gating features artificially — if a user seeks them out, show them
---
## User Activation
### Defining Activation Metrics
```
Activation = user reached the "aha moment"
Examples:
- Slack: Sent first message in a channel
- Dropbox: Uploaded first file
- GitHub: Made first commit
- Figma: Created first design frame
- Notion: Created and shared first page
```
### Time-to-Value (TTV)
The time between sign-up and the user experiencing core product value.
**Optimization strategies:**
- Remove unnecessary sign-up steps (defer profile completion)
- Auto-configure based on sign-up context (role, company size)
- Pre-populate with relevant templates or data
- Guide users to the core action immediately
- Measure TTV and optimize for reduction
### The "Aha Moment"
```
Identify by analyzing:
1. What do retained users have in common?
2. What action separates retained from churned users?
3. How quickly do successful users reach this action?
Design toward it:
- Make the path to the aha moment as short as possible
- Remove friction between sign-up and first value
- Celebrate when users reach it
```
---
## Personalization
### Role-Based Onboarding
```
┌─────────────────────────────────────────┐
│ Welcome! What's your role? │
│ │
│ ┌───────────┐ ┌──────────────┐ │
│ │ 🎨 │ │ 💻 │ │
│ │ Designer │ │ Developer │ │
│ └───────────┘ └──────────────┘ │
│ ┌───────────┐ ┌──────────────┐ │
│ │ 📊 │ │ 👤 │ │
│ │ Manager │ │ Other │ │
│ └───────────┘ └──────────────┘ │
│ │
│ This helps us customize your setup. │
└─────────────────────────────────────────┘
```
- Ask 1-2 personalization questions max during sign-up
- Customize dashboard, default views, and feature highlights based on role
- Allow changing selection later without losing work
- Make it clear how this choice affects their experience
- Skip if irrelevant (single-purpose tools don't need role selection)
### Skill-Level Adaptation
- Detect usage patterns to infer expertise level
- Offer "beginner" vs. "experienced" onboarding paths
- Show keyboard shortcuts to power users, guided tours to beginners
- Progressively reduce hand-holding as usage increases
- Allow manual skill-level toggle in settings
---
## Sign-Up Flow
### Reducing Friction
```
Minimal sign-up (3 fields max):
┌────────────────────────────────┐
│ Get started │
│ │
│ Email: [ ] │
│ Password: [ ] │
│ │
│ [Create Account] │
│ │
│ ── or continue with ── │
│ [Google] [GitHub] [SSO] │
│ │
│ Already have an account? │
│ Sign in │
└────────────────────────────────┘
```
**Best practices:**
- Social login / SSO options reduce friction significantly
- Defer non-essential fields (name, company) to post-sign-up
- Email-first flow: collect email, then ask for password on next step
- Magic link option for password-free sign-up
- Show password requirements inline, not after failed submission
- Single-field entry: email only, then decide sign-in vs. sign-up
### Progressive Profiling
Collect user information over time, not all at once.
```
Sign-up: Email + password only
First use: "What's your name?" (in-context)
Day 3: "What's your team size?" (when relevant)
Day 7: "Connect your calendar?" (when value is clear)
```
- Each request provides clear value to the user
- Ask in context (team size when inviting first member)
- Never block core features behind profile completion
- Show profile completion percentage (optional, gamification)
### Deferred Registration
- Let users try the product before requiring sign-up
- Save anonymous work and transfer to account on registration
- Show value before asking for commitment
- "Continue as guest" option where applicable
---
## First-Run Experience
### Blank Slate Design
Every empty screen is an opportunity to onboard:
| Screen | Instead of "No data" | Show |
|--------|---------------------|------|
| Dashboard | Empty widgets | Pre-configured widgets with sample data |
| Project list | "No projects" | Templates + "Create first project" CTA |
| Inbox | "No messages" | "Invite your team to start conversations" |
| Analytics | "No data yet" | "Connect a data source" + preview |
### Starter Templates
- Offer 3-5 templates relevant to user's role/industry
- Preview template before selecting
- "Start from scratch" always available
- Templates should be fully functional, not just layouts
- Allow customization immediately after selection
### Guided Setup Wizards
```
Step 1 Step 2 Step 3 Done!
[Profile] ──→ [Team] ──→ [Integrations] ──→ [🎉]
● ○ ○ ○
```
- Maximum 3-5 steps
- Show progress and allow skipping each step
- Save progress if user abandons mid-wizard
- Offer "Do this later" for optional steps
- Each step should deliver immediate value
---
## Re-Engagement
### Return User Flows
```
┌─────────────────────────────────────────┐
│ Welcome back, Alex! │
│ │
│ Since you were last here: │
│ • 3 new comments on "Q4 Report" │
│ • Sarah shared "Design Brief" with you │
│ • Your trial ends in 5 days │
│ │
│ [Go to Q4 Report] [View all updates] │
└─────────────────────────────────────────┘
```
- Show relevant updates since last visit
- Deep-link to unfinished work or pending actions
- Don't re-show completed onboarding steps
- Feature announcements for major changes only
### What's New Patterns
- Changelog accessible from a "What's new" menu item or badge
- Brief inline announcements for features relevant to user's workflow
- "New" badges on navigation items (auto-dismiss after first visit)
- Major updates: one-time modal with clear benefit statement
- Never block user workflow for feature announcements
---
## Measuring Success
### Key Metrics
| Metric | Target | How to measure |
|--------|--------|---------------|
| Sign-up completion | > 85% | Users who complete sign-up / started |
| Onboarding completion | > 65% | Users who finish checklist / total |
| Time to first value | < 5 min | Time from sign-up to activation event |
| Day 1 retention | > 40% | Users who return the day after sign-up |
| Day 7 retention | > 25% | Users who return within first week |
| Activation rate | > 30% | Users who reach aha moment / total sign-ups |
| Tour skip rate | Monitor | High skip may indicate poor tour design |
| Checklist item completion | Track per item | Identifies friction points |
### Drop-Off Analysis
- Funnel visualization for each onboarding step
- Identify where users abandon (highest drop-off = biggest opportunity)
- Segment by user source, role, and device
- A/B test onboarding variations against activation rate
- Qualitative: user session recordings of first-run experience
---
## Anti-Patterns
1. **Mandatory lengthy tours** — Forcing users through 10+ steps before they can use the product
2. **Information overload** — Teaching everything at once instead of progressively
3. **No skip option** — Trapping users in onboarding flows
4. **Ignoring return users** — Showing the same onboarding to returning users
5. **Empty states with no guidance** — "No data" with no next step
6. **Asking too much at sign-up** — 10-field registration forms
7. **Irrelevant personalization questions** — Asking role when it doesn't affect the experience
8. **Celebrating too early** — Confetti when user hasn't done anything meaningful yet
9. **Feature dumping** — Showing every feature in tooltips before user needs them
10. **No measurement** — Not tracking where users drop off in onboarding
---
## Sources
- [UserOnboard](https://www.useronboard.com/) — Real product onboarding teardowns
- [ProductLed: User Activation](https://productled.com/) — Activation frameworks
- [Appcues: State of User Onboarding](https://www.appcues.com/) — Onboarding benchmarks
- [Nielsen Norman Group: Onboarding](https://www.nngroup.com/articles/onboarding/) — UX research
- [Intercom: Onboarding](https://www.intercom.com/blog/onboarding/) — SaaS onboarding best practices
- [Reforge: Activation](https://www.reforge.com/) — Growth frameworks
references/18-data-visualization.md
# Data Visualization & Dashboard UX
Data visualization and dashboard design are specialized UX disciplines critical for enterprise products and analytics interfaces. This reference covers chart selection, dashboard layout, data tables, and accessibility for data-heavy interfaces.
---
## Chart Selection
### When to Use Each Chart Type
| Data Relationship | Recommended Chart | Avoid |
|-------------------|------------------|-------|
| **Part of whole** | Pie (≤6 slices), donut, stacked bar, treemap | Pie with >6 slices |
| **Comparison** | Bar (vertical/horizontal), grouped bar | 3D bars, radar (hard to read) |
| **Trend over time** | Line, area, sparkline | Pie (no time axis) |
| **Distribution** | Histogram, box plot, violin plot | Pie or bar |
| **Correlation** | Scatter plot, bubble chart | Line (implies time) |
| **Ranking** | Horizontal bar (sorted), lollipop chart | Vertical bar (hard to label) |
| **Geographic** | Choropleth map, dot map | Bar chart for location data |
| **Hierarchy** | Treemap, sunburst | Nested pie charts |
| **Flow/process** | Sankey diagram, funnel | Bar chart |
| **Single value** | KPI card, gauge, big number | Chart with one data point |
### Chart Decision Tree
```
What are you showing?
├── Single number → KPI card / big number display
├── Change over time?
│ ├── Few series (1-3) → Line chart
│ ├── Many series → Small multiples or highlight key lines
│ └── Cumulative → Stacked area chart
├── Comparing categories?
│ ├── Few categories (≤6) → Vertical bar chart
│ ├── Many categories → Horizontal bar chart (easier labels)
│ └── Two variables → Grouped or stacked bar
├── Part of a whole?
│ ├── Few parts (≤5) → Pie or donut chart
│ ├── Many parts → Treemap or stacked bar
│ └── Hierarchical → Sunburst or treemap
├── Relationship between variables?
│ ├── Two variables → Scatter plot
│ └── Three variables → Bubble chart (size = 3rd variable)
└── Distribution?
├── Single variable → Histogram
└── Multiple groups → Box plot or violin plot
```
---
## Dashboard Layout
### Information Hierarchy
```
┌─────────────────────────────────────────────────────┐
│ KPI Cards (most important metrics at a glance) │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ $42K │ │ 1.2K │ │ 89% │ │ 4.2s │ │
│ │ Rev │ │Users │ │ Sat │ │ Load │ │
│ │ ↑12% │ │ ↑8% │ │ ↓2% │ │ ↑0.3 │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
├─────────────────────────────────────────────────────┤
│ Primary chart (main trend or most viewed data) │
│ ┌─────────────────────────────────────────────┐ │
│ │ 📈 Revenue Over Time │ │
│ │ ▁▂▃▅▆▇█▇▆▅▆▇██▇▅▆▇█ │ │
│ └─────────────────────────────────────────────┘ │
├─────────────────────┬───────────────────────────────┤
│ Secondary chart 1 │ Secondary chart 2 │
│ ┌───────────────┐ │ ┌───────────────────────┐ │
│ │ 🥧 By Source │ │ │ 📊 By Region │ │
│ └───────────────┘ │ └───────────────────────┘ │
├─────────────────────┴───────────────────────────────┤
│ Data table (details on demand) │
│ ┌─────────────────────────────────────────────┐ │
│ │ Name │ Revenue │ Users │ Conversion │ │
│ │ ... │ ... │ ... │ ... │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
```
### Layout Principles
- **Top**: KPI summary cards (3-6 metrics)
- **Middle**: Primary visualizations (1-2 charts)
- **Bottom**: Secondary charts and detail tables
- **Shneiderman's mantra**: Overview first, zoom and filter, details on demand
- Use consistent grid (12-column for flexibility)
- Group related metrics visually
### KPI Card Design
```
┌────────────────────────┐
│ Revenue ⓘ │ ← Label + info tooltip
│ $42,350 │ ← Big number (primary)
│ ↑ 12.3% vs last month │ ← Trend with comparison
│ ▁▂▃▅▆▇█▇▆▅ │ ← Sparkline (optional)
└────────────────────────┘
Colors:
↑ Green = positive trend (contextual — could be red for cost metrics)
↓ Red = negative trend
→ Gray = no change
```
- Display the most important number prominently
- Include comparison period and trend direction
- Add sparkline for quick trend visualization
- Info icon for metric definition/calculation
- Contextual coloring (up isn't always good — e.g., error rate)
### Customizable Dashboards
- Drag-and-drop widget rearrangement
- Resizable panels with snap-to-grid
- Add/remove widgets from a library
- Save multiple dashboard layouts
- Share dashboards with team members
- Reset to default layout option
---
## Color in Data Visualization
### Palette Types
```
Sequential (low → high):
□ → ■ Light blue → Dark blue
Use for: continuous data, heatmaps, density
Diverging (negative ← neutral → positive):
■ ← □ → ■ Red ← White → Blue
Use for: data with a meaningful midpoint (profit/loss, deviation)
Categorical (distinct groups):
■ ■ ■ ■ ■ Blue, Orange, Green, Red, Purple
Use for: comparing distinct categories, legend items
Maximum: 6-8 distinct colors before confusion
```
### Colorblind-Safe Palettes
```
❌ Red vs. Green (indistinguishable for ~8% of males)
✅ Blue vs. Orange (safe for most colorblind types)
Recommended categorical palette (colorblind-safe):
#4477AA #EE6677 #228833 #CCBB44 #66CCEE #AA3377
Blue Rose Green Gold Cyan Purple
```
**Rules:**
- Never use color as the only differentiator
- Add patterns, shapes, or labels as secondary encoding
- Test with colorblind simulation tools
- Provide alternative text descriptions for all charts
- Use 3:1 minimum contrast between adjacent data colors
---
## Interaction Patterns
### Hover Tooltips
```
┌──────────────────────┐
│ January 2025 │
│ Revenue: $42,350 │
│ Users: 1,234 │
│ ↑ 12% vs Dec 2024 │
└──────────┬───────────┘
│
▁▂▃▅▆▇█▇▆▅▆▇██
▲ cursor position
```
- Show on hover (desktop) or tap (mobile)
- Include exact values, context, and comparison
- Position near cursor without obscuring the data point
- Follow cursor smoothly (no jittering)
- Dismiss on mouse-out or tap elsewhere
### Drill-Down
```
Overview: Revenue by Region
┌─────────────────────────┐
│ 📊 Americas | EMEA │ ← Click "Americas"
└─────────────────────────┘
↓
Detail: Revenue by Country (Americas)
┌─────────────────────────┐
│ 📊 US | CA | BR | MX │ ← Click "US"
└─────────────────────────┘
↓
Granular: Revenue by State (US)
┌─────────────────────────┐
│ 📊 CA | TX | NY | FL │
└─────────────────────────┘
Breadcrumb: All Regions > Americas > US
[← Back to Americas]
```
- Clear visual affordance that elements are clickable
- Breadcrumb navigation showing drill-down path
- "Back" button to return to previous level
- Maintain filters and time range across drill-downs
### Filtering & Time Range
```
┌─────────────────────────────────────────────────────┐
│ Time: [Last 30 days ▾] Region: [All ▾] [⟳] │
│ [Custom range...] Segment: [All ▾] │
├─────────────────────────────────────────────────────┤
│ Active filters: [Americas ✕] [Enterprise ✕] [Clear]│
└─────────────────────────────────────────────────────┘
```
- Persistent filter bar visible at all times
- Show active filters as removable chips
- Preset time ranges (Today, 7d, 30d, 90d, YTD, Custom)
- Filters apply to all dashboard widgets simultaneously
- "Reset filters" to return to default view
- Date range picker with calendar and quick presets
### Zoom & Pan (Time-Series)
- Click-drag to select a time range (brush selection)
- Mouse wheel to zoom in/out on time axis
- Reset zoom button always visible when zoomed
- Mini-timeline showing selected range in context
---
## Responsive Charts
### Mobile Considerations
```
Desktop: Full chart with legend, axis labels, gridlines
Tablet: Simplified chart, condensed legend
Mobile: Sparkline or KPI card, tap for full chart
Breakpoints:
> 1024px: Full dashboard grid
768-1024: 2-column grid, condensed charts
< 768px: Single column, KPI cards + expandable charts
```
- Prioritize KPI cards on mobile (most information-dense)
- Collapse charts to sparklines with "Expand" option
- Horizontal scrolling for wide data tables (with shadow indicators)
- Stack dashboard columns vertically on narrow screens
- Touch-friendly tooltips (tap instead of hover)
### Small Multiples
```
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│US ▁▅█│ │UK ▁▃▅│ │DE ▁▂▃│ │JP ▅▃▁│
└──────┘ └──────┘ └──────┘ └──────┘
```
- Same scale across all multiples for fair comparison
- Consistent axes (shared y-axis range)
- Label each clearly (top-left corner)
- Effective for comparing trends across many categories
---
## Data Tables
### Core Features
```
┌──┬──────────┬──────────┬─────────┬────────┬──────┐
│☐ │ Name ↕ │ Revenue ↓│ Users ↕ │ Conv. │ ··· │
├──┼──────────┼──────────┼─────────┼────────┼──────┤
│☐ │ Acme Inc │ $42,350 │ 1,234 │ 3.4% │ ··· │
│☑ │ Beta Co │ $38,200 │ 987 │ 4.1% │ ··· │
│☐ │ Gamma │ $35,100 │ 856 │ 2.8% │ ··· │
├──┴──────────┴──────────┴─────────┴────────┴──────┤
│ ☑ 1 selected [Export] [Delete] │
├──────────────────────────────────────────────────┤
│ Showing 1-25 of 342 [← 1 2 3 ... 14 →] │
└──────────────────────────────────────────────────┘
```
### Table Interaction Patterns
| Feature | Implementation |
|---------|---------------|
| **Sorting** | Click column header, toggle asc/desc, show arrow indicator |
| **Filtering** | Per-column filter dropdowns or global search |
| **Pagination** | 10/25/50/100 rows per page, page numbers, prev/next |
| **Infinite scroll** | For exploration (prefer pagination for data analysis) |
| **Row selection** | Checkbox column, shift-click for range, bulk actions bar |
| **Column resizing** | Drag column borders, double-click to auto-fit |
| **Column reordering** | Drag column headers |
| **Pinned columns** | Pin first 1-2 columns on horizontal scroll |
| **Inline editing** | Click to edit cell, Enter to save, Esc to cancel |
| **Row expansion** | Expand row for nested detail without leaving the table |
| **Export** | CSV, Excel, PDF — selected rows or all |
### Pagination vs. Infinite Scroll
| Use Pagination | Use Infinite Scroll |
|----------------|-------------------|
| Data analysis tasks | Content browsing |
| Need to return to specific position | Feed/timeline content |
| Large datasets (1000+ rows) | Progressive loading |
| Printable/exportable views | Mobile-first interfaces |
---
## Empty & Error States
### Data States
```
Loading:
┌─────────────────────────────┐
│ ░░░░░░░░░░░░░░░░░░░░░░░░ │ ← Skeleton chart
│ ░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░ │
└─────────────────────────────┘
No data:
┌─────────────────────────────┐
│ 📊 │
│ No data for this period │
│ Try a different date range │
│ or adjust your filters. │
│ [Reset filters] │
└─────────────────────────────┘
Error:
┌─────────────────────────────┐
│ ⚠️ │
│ Unable to load chart data │
│ [Retry] [Report issue] │
└─────────────────────────────┘
Partial data:
┌─────────────────────────────┐
│ 📈 Revenue (partial data) │
│ ▁▂▃▅▆▇█▇▆ ░░░ │
│ ℹ️ Data available through │
│ Jan 15. Processing... │
└─────────────────────────────┘
```
### Stale Data Indicators
- Show "Last updated: X minutes ago" for live dashboards
- Auto-refresh indicator with countdown
- Manual refresh button
- Visual indicator (dimmed chart, banner) for stale data
- Alert when data is significantly outdated
---
## Accessibility
### Chart Accessibility
- **Alt text**: Descriptive summary of what the chart shows
- **Data table alternative**: Provide raw data table for every chart
- **Keyboard navigation**: Tab through data points, arrow keys within chart
- **Screen reader**: Announce data points, trends, and outliers
- **High contrast**: Ensure chart elements meet 3:1 contrast minimum
- **Pattern fills**: Use patterns in addition to colors for categories
```html
<figure role="img" aria-label="Revenue trend showing 12% growth from January to December 2025, with a peak of $52K in November">
<canvas id="revenue-chart"></canvas>
<figcaption>
Monthly revenue, Jan-Dec 2025.
<a href="#revenue-data-table">View as data table</a>
</figcaption>
</figure>
```
### Data Table Accessibility
- Use semantic `<table>`, `<thead>`, `<tbody>`, `<th scope>` markup
- `aria-sort` on sortable column headers
- `aria-selected` on selected rows
- `aria-describedby` for column filter descriptions
- Announce row count and current position to screen readers
- Keyboard navigation: Tab between cells, Enter to activate
---
## Key Metrics
| Metric | Target | Context |
|--------|--------|---------|
| Dashboard load time | < 3s | Including data fetch |
| Chart render time | < 500ms | After data available |
| Tooltip response | < 100ms | On hover/tap |
| Data refresh interval | 15-60s | Live dashboards |
| Color contrast (data) | ≥ 3:1 | Adjacent data colors |
| Max chart categories | 6-8 | Before readability drops |
| Max KPI cards | 3-6 | Above fold visibility |
---
## Anti-Patterns
1. **3D charts** — Add visual noise without informational value
2. **Pie charts with 10+ slices** — Impossible to compare small slices
3. **Dual y-axes** — Misleading correlation; use small multiples instead
4. **Truncated y-axis** — Starting y-axis above zero exaggerates differences
5. **Rainbow color schemes** — Colorblind-unfriendly and visually noisy
6. **Chart junk** — Decorative gridlines, borders, and backgrounds
7. **No loading states** — Blank areas while data fetches
8. **Auto-refresh without warning** — Dashboard changes while user is analyzing
9. **Tooltip that obscures data** — Covering the very thing user is trying to read
10. **Color-only encoding** — No patterns, labels, or shapes as redundant encoding
---
## Sources
- [Edward Tufte: The Visual Display of Quantitative Information](https://www.edwardtufte.com/) — Data visualization theory
- [Material Design: Data Visualization](https://m3.material.io/styles/color/dynamic/choosing-a-scheme) — Google guidelines
- [D3.js Gallery](https://d3-graph-gallery.com/) — Implementation patterns
- [Chartability](https://chartability.fizz.studio/) — Chart accessibility audit toolkit
- [ColorBrewer](https://colorbrewer2.org/) — Colorblind-safe palettes
- [Stephen Few: Information Dashboard Design](https://www.perceptualedge.com/) — Dashboard principles
- [WAI: Images Tutorial](https://www.w3.org/WAI/tutorials/images/complex/) — Complex image accessibility
references/17-notifications.md
# Notification & Attention Design
Notifications are a cross-cutting concern that affects every product. This reference covers notification types, delivery channels, attention management, and permission patterns for responsible notification design.
---
## Notification Types
### By Purpose
| Type | Description | Priority | Example |
|------|-------------|----------|---------|
| **Transactional** | Result of a user action | High | "Order confirmed", "Payment received" |
| **Informational** | System updates or status | Medium | "Maintenance scheduled", "Export ready" |
| **Social** | Activity from other users | Medium | "Alex commented on your post" |
| **Promotional** | Marketing or upsell | Low | "Try our new feature", "Upgrade plan" |
| **Alert** | Urgent system or security | Critical | "Unusual sign-in detected", "Server down" |
### By Urgency
```
Critical ████████████ Requires immediate action (security, data loss)
High █████████░░░ Important, time-sensitive (mentions, assignments)
Medium ██████░░░░░░ Relevant but not urgent (comments, updates)
Low ███░░░░░░░░░ Nice to know (tips, promotions)
Info █░░░░░░░░░░░ Background context (changelog, analytics)
```
---
## Severity & Visual Hierarchy
### Standard Severity Levels
```css
/* Critical — Blocking issue, requires immediate attention */
.alert-critical {
background: #fef2f2;
border-left: 4px solid #dc2626;
color: #991b1b;
}
/* Warning — Potential issue, user should be aware */
.alert-warning {
background: #fffbeb;
border-left: 4px solid #f59e0b;
color: #92400e;
}
/* Success — Positive confirmation */
.alert-success {
background: #f0fdf4;
border-left: 4px solid #16a34a;
color: #166534;
}
/* Info — Neutral information */
.alert-info {
background: #eff6ff;
border-left: 4px solid #3b82f6;
color: #1e40af;
}
```
### Visual Priority
| Element | Critical | Warning | Info | Success |
|---------|----------|---------|------|---------|
| Icon | ⛔ / 🔴 | ⚠️ / 🟡 | ℹ️ / 🔵 | ✅ / 🟢 |
| Color | Red | Yellow/Amber | Blue | Green |
| Position | Modal/top banner | Top banner | Inline/toast | Toast |
| Sound | Optional alert | None | None | None |
| Persistence | Until resolved | Until dismissed | Auto-dismiss | Auto-dismiss |
---
## Delivery Channels
### Channel Selection Guide
```
Urgency
High ──────────── Low
│ │
Sync │ Modal Banner │
(blocking) │ Alert Inline │
│ │
│ Push Email │
Async │ Toast Badge │
(non-block) │ SMS In-app feed │
│ │
```
### In-App Notifications
#### Banners (Persistent)
```
┌───────────────────────────────────────────────────┐
│ ⚠️ Your trial expires in 3 days. [Upgrade now] [✕] │
└───────────────────────────────────────────────────┘
┌─────────────────── Page Content ──────────────────┐
```
- Fixed at top or bottom of viewport
- Persist until dismissed or condition resolved
- Use for: system-wide announcements, degraded service, account alerts
- Include clear action button and dismiss option
- Maximum 1-2 banners visible at a time
#### Toasts / Snackbars
```
┌──────────────────────────┐
│ ✅ Document saved │
│ [Undo] [✕] │
└──────────────────────────┘
```
**Duration guidelines:**
- Short messages (no action): 4 seconds
- Messages with action button: 6-8 seconds
- Error messages: 8-10 seconds or until dismissed
- Never auto-dismiss critical errors
**Placement:**
- Bottom-center or bottom-right (most common)
- Top-center for important alerts
- Consistent position throughout the app
- Away from primary interaction areas
**Stacking:**
```
┌──────────────────────────────┐
│ ✅ File uploaded │ ← newest
└──────────────────────────────┘
┌──────────────────────────────┐
│ ℹ️ 2 team members online │ ← older
└──────────────────────────────┘
Max stack: 3 toasts visible
Older toasts slide out or collapse
```
- Stack newest on top
- Maximum 3 visible at once
- Older toasts auto-dismiss to make room
- All toasts dismissible with close button or swipe
#### Badges
```
┌────────────────────────────────────────┐
│ [🔔 3] [📨 12] [👤] │
└────────────────────────────────────────┘
```
**Design rules:**
- Numeric badge: 1-99, then "99+" for overflow
- Dot badge: for "something new" without specific count
- Position: top-right of the icon
- Color: red for requiring attention, blue/gray for informational
- Clear on interaction (opening the relevant section)
- Animate on increment (subtle scale pulse)
#### Modals / Dialogs
Reserve for critical interruptions that require immediate user decision.
```
┌─────────────────────────────────────────┐
│ ⚠️ Unsaved Changes │
│ │
│ You have unsaved changes that will be │
│ lost if you leave this page. │
│ │
│ [Discard Changes] [Save & Continue] │
└──────────────────────────────────────────┘
```
- Only for actions that require a decision
- Never for informational messages (use toast/banner instead)
- Always provide a clear way to dismiss (Esc, close button, outside click)
- Maximum 1 modal at a time (never stack modals)
- Include a clear primary action
#### Inline Notifications
```
┌─────────────────────────────────────────┐
│ Email: [user@example.com ] │
│ ⚠️ This email is already in use. │
│ [Sign in instead?] │
└─────────────────────────────────────────┘
```
- Placed directly next to the relevant content
- Contextual and specific to the user's action
- Best for: form validation, field-level errors, contextual tips
- Don't require dismissal (disappear when issue is resolved)
### External Channels
#### Push Notifications
```
┌─────────────────────────────────────────┐
│ 🔔 AppName now │
│ Alex commented on "Q4 Report" │
│ "Great analysis! Can we discuss the..." │
└─────────────────────────────────────────┘
```
- Ask permission contextually (not on first visit)
- Group related notifications (3+ from same thread → summary)
- Deep-link to the relevant content
- Rich notifications: image previews, action buttons
- Respect quiet hours (system settings or app-level)
#### Email Notifications
- Batch non-urgent notifications (daily/weekly digest)
- Include unsubscribe link in every email (CAN-SPAM, GDPR)
- One-click unsubscribe header (RFC 8058)
- Clear sender name and subject line
- Deep-link to specific content in the app
---
## Notification Center
### Inbox Pattern
```
┌─ Notifications ─────────────────────────┐
│ [All] [Unread (5)] [Mentions] │
│ │
│ Today │
│ ┌────────────────────────────────────┐ │
│ │ 🔵 Alex commented on "Q4 Report" │ │
│ │ "Looks great! One question..." │ │
│ │ 2 hours ago [···] │ │
│ ├────────────────────────────────────┤ │
│ │ 🔵 Build #142 passed │ │
│ │ main branch — all tests green │ │
│ │ 3 hours ago [···] │ │
│ ├────────────────────────────────────┤ │
│ │ Sarah shared "Design Brief" │ │
│ │ 5 hours ago [···] │ │
│ └────────────────────────────────────┘ │
│ │
│ Yesterday │
│ ┌────────────────────────────────────┐ │
│ │ Invoice #1234 paid │ │
│ │ Yesterday at 4:32 PM [···] │ │
│ └────────────────────────────────────┘ │
│ │
│ [Mark all as read] │
└──────────────────────────────────────────┘
```
**Features:**
- Read/unread visual distinction (dot, bold, background color)
- Grouping by time (Today, Yesterday, This week, Older)
- Filter tabs (All, Unread, Mentions, specific categories)
- Bulk actions: mark all as read, clear all
- Individual actions: mark read/unread, mute thread, delete
- Clicking notification navigates to relevant content
- Infinite scroll with loading states
- "You're all caught up" message when empty
---
## Attention Management
### Interruption Budget
Not all notifications deserve equal interruption.
```
Daily interruption budget (suggested maximums):
├── Critical alerts: No limit (safety/security)
├── High-priority (mentions, assignments): 10-15
├── Medium (comments, updates): Batched 2-3x/day
├── Low (tips, promotions): 1/day maximum
└── Passive (badges, feed): Zero interruption
```
### Do Not Disturb
```
┌─ Focus Mode ────────────────────────────┐
│ │
│ [🔕 Do Not Disturb] Until: [1 hour ▾] │
│ │
│ Allow through: │
│ [✓] Direct mentions │
│ [✓] Critical system alerts │
│ [ ] Comments on my items │
│ [ ] Team updates │
│ │
│ Schedule: │
│ [ ] Auto-enable outside work hours │
│ Work hours: [9:00] - [17:00] │
│ │
│ [Save] │
└──────────────────────────────────────────┘
```
### Notification Preferences
```
┌─ Notification Settings ─────────────────┐
│ │
│ Channel In-app Push Email │
│ ─────────────────────────────────────── │
│ Mentions ✓ ✓ ✓ │
│ Comments ✓ ✓ ○ │
│ Updates ✓ ○ ○ │
│ Marketing ○ ○ ○ │
│ │
│ ✓ = enabled ○ = disabled │
│ │
│ Email digest: [Weekly ▾] │
│ │
│ [Save preferences] │
└──────────────────────────────────────────┘
```
- Per-category, per-channel control
- Sensible defaults (not everything on)
- Email digest option for batching
- Quick "mute all" toggle
- Accessible from notification center and settings
### Batching & Grouping
```
Instead of 5 separate notifications:
"Alex commented on Project X"
"Alex commented on Project X"
"Sarah commented on Project X"
"Alex commented on Project X"
"Jordan commented on Project X"
Send 1 grouped notification:
"4 new comments on Project X from Alex, Sarah, and Jordan"
```
- Group by thread/topic, not by user
- Show count and participant summary
- Batch non-urgent notifications (5-15 minute window)
- Expand to individual items on click
---
## Permission Requests
### Just-in-Time Permissions
```
❌ On first visit:
"AppName wants to send you notifications"
[Don't Allow] [Allow]
✅ In context, when relevant:
┌─────────────────────────────────────────┐
│ Want to know when someone replies? │
│ │
│ Get notified when team members │
│ respond to your comments. │
│ │
│ [Enable Notifications] [Not now] │
└─────────────────────────────────────────┘
→ Then triggers the browser permission prompt
```
### Pre-Permission Pattern
Before triggering the system permission dialog:
1. **Explain value**: Why the user benefits from this permission
2. **Show in context**: When the permission is relevant to what they're doing
3. **Custom prompt first**: Your own UI before the system dialog
4. **Respect "not now"**: Don't ask again for at least 30 days
5. **Never on first visit**: Wait until user has experienced product value
### Permission Denied Recovery
```
When user denied permission:
┌─────────────────────────────────────────┐
│ ℹ️ Notifications are disabled │
│ │
│ To enable notifications: │
│ 1. Click the lock icon in your browser │
│ 2. Find "Notifications" │
│ 3. Change to "Allow" │
│ │
│ [Show me how] │
└─────────────────────────────────────────┘
```
- Provide clear instructions to re-enable in browser/OS settings
- Don't repeatedly nag about denied permissions
- Offer email as a fallback channel
- Show relevant instructions per browser/platform
---
## Key Metrics
| Metric | Target | Context |
|--------|--------|---------|
| Push opt-in rate | > 50% | Permission acceptance |
| Notification open rate | > 15% | Push engagement |
| Email unsubscribe rate | < 0.5%/email | List health |
| Toast dismissal rate | Track | Too many = too noisy |
| Notification → action | > 25% | Relevance indicator |
| Permission ask timing | After activation | Not on first visit |
| DND usage | Monitor | High = too noisy overall |
---
## Anti-Patterns
1. **Permission on first visit** — Asking for push permission before user sees value
2. **Notification carpet bombing** — Sending every event as a push notification
3. **No notification preferences** — All-or-nothing notification control
4. **Stacking modals** — Multiple dialogs requiring sequential dismissal
5. **Auto-dismissing errors** — Critical errors that disappear before user reads them
6. **Same urgency for everything** — Treating promotions like security alerts
7. **No grouping** — 10 individual notifications for 10 comments on the same thread
8. **Notification-only features** — Actions only accessible through notification center
9. **Dark pattern nudging** — "You have 0 notifications" as a growth hack
10. **Ignoring quiet hours** — Sending push notifications at 3 AM
---
## Sources
- [Material Design: Notifications](https://m3.material.io/foundations/content-design/notifications) — Component patterns
- [Apple HIG: Notifications](https://developer.apple.com/design/human-interface-guidelines/notifications) — Platform guidelines
- [Nielsen Norman Group: Notification UX](https://www.nngroup.com/articles/push-notification/) — Research
- [Intercom: Notification Design](https://www.intercom.com/blog/) — SaaS patterns
- [Web Push Protocol](https://web.dev/articles/push-notifications-overview) — Technical implementation
- [RFC 8058](https://datatracker.ietf.org/doc/html/rfc8058) — One-click unsubscribe
references/23-internationalization.md
# Internationalization & Localization
Internationalization (**i18n**) is designing and building a product so it *can* be
adapted to different languages, regions, and cultures without re-engineering.
Localization (**l10n**) is the adaptation itself—translating strings, swapping
formats, adjusting layout. Get i18n wrong up front and every locale becomes a
rewrite; get it right and adding a language is a content task, not a code task.
> Rule of thumb: assume your UI will run right-to-left, in a language whose words
> are 40% longer than English, with dates and numbers formatted by the user's
> locale—*before* you have a single translation.
---
## RTL & Bidirectional Layout
Arabic, Hebrew, Persian, and Urdu read right-to-left (RTL). RTL is not "flip the
CSS"—it's a directional mirror of the *layout*, while some content stays unchanged.
### Use logical CSS properties, not physical ones
Physical properties (`left`, `right`, `margin-left`) hard-code LTR assumptions.
Logical properties resolve against the document/element direction automatically.
```css
/* ❌ Physical — breaks in RTL */
.card { margin-left: 16px; text-align: left; border-left: 2px solid; }
/* ✅ Logical — mirrors automatically with dir="rtl" */
.card {
margin-inline-start: 16px;
text-align: start;
border-inline-start: 2px solid;
padding-inline: 12px; /* start + end */
inset-inline-start: 0; /* replaces left/right for positioning */
}
```
| Physical | Logical equivalent |
|----------|-------------------|
| `margin-left` / `margin-right` | `margin-inline-start` / `margin-inline-end` |
| `padding-left` / `padding-right` | `padding-inline-start` / `padding-inline-end` |
| `left` / `right` | `inset-inline-start` / `inset-inline-end` |
| `text-align: left` | `text-align: start` |
| `border-left` | `border-inline-start` |
### Set direction at the root, let it cascade
```html
<html lang="ar" dir="rtl">
```
Use `dir="auto"` on user-generated content fields so a single input handles either
direction based on the first strong character. For mixed-direction strings (an
English brand name inside Arabic text), rely on the Unicode Bidirectional Algorithm
and isolate embedded runs with `<bdi>` or CSS `unicode-bidi: isolate`.
### Mirror these — but not those
**Mirror** (they imply direction of reading/progress):
- Layout: navigation, sidebars, alignment, the whole grid
- Directional icons: back/forward arrows, chevrons, breadcrumb separators
- Progress bars, sliders, carousels, "next step" affordances
**Do NOT mirror** (direction is intrinsic, not reading-relative):
- Logos and brand marks
- Media playback controls (play ▶ still points "forward in time", not in reading direction)
- Clocks, musical notation, and most diagrams
- Numbers themselves (digits are written left-to-right even within RTL text)
- Checkmarks, and icons of real-world objects whose orientation is fixed
```css
/* Mirror a directional icon only */
[dir="rtl"] .icon-chevron { transform: scaleX(-1); }
/* Logo and play button: leave alone */
```
### What a mirror actually looks like
```
LTR (English) RTL (Arabic)
┌───────────────────────────┐ ┌───────────────────────────┐
│ [Logo] Home About [☰] │ │ [☰] About Home [Logo] │
├───────────────────────────┤ ├───────────────────────────┤
│ ‹ Back Title │ │ Title Back › │
│ │ │ │
│ • List item │ │ List item • │
│ [Avatar] Name │ │ Name [Avatar] │
│ [Next ›] │ │ [‹ Next] │
└───────────────────────────┘ └───────────────────────────┘
▶ 0:12 ──────●──── 3:45 ▶ 0:12 ──────●──── 3:45
(media controls do NOT mirror — time still flows left→right)
```
Note: the nav, alignment, list bullets, avatars, and the Back/Next chevrons all
flip; the logo and the media scrubber do not.
---
## Text Expansion & Contraction
Translated text changes length. UI laid out tightly around English will overflow,
truncate, or wrap badly. German, Finnish, and Russian commonly run **30–40% longer**
than English; short strings can expand far more.
### IBM's rule of thumb for expansion
| English length | Expect up to |
|----------------|-------------|
| ≤ 10 chars | +100–200% |
| 11–20 chars | +80–100% |
| 21–30 chars | +60–80% |
| 31–50 chars | +40% |
| > 50 chars | +30% |
### Design for it
- **No fixed-width text containers.** Let buttons, tabs, labels, and badges grow.
- **Avoid truncation on essential UI.** A clipped menu item is a broken menu item.
- **Test with pseudo-localization** early: replace `Settings` with
`[Ŝéttîng§ ──────]` to surface overflow and hard-coded strings before real
translation exists.
- **CJK contracts**—Chinese/Japanese/Korean are often *shorter and taller*; don't
assume single-line height either.
### Never concatenate translated fragments
Word order differs across languages, so string-gluing produces nonsense.
```js
// ❌ Grammatically broken in most languages
const msg = "You have " + count + " new " + itemType + " messages";
// ✅ One translatable message with named placeholders + ICU plurals
t('inbox.summary', { count, itemType });
// en: "{count, plural, one {# new message} other {# new messages}}"
// de: "{count, plural, one {# neue Nachricht} other {# neue Nachrichten}}"
```
---
## Pluralization & Gender
English has two plural forms (1 / many). Many languages have more—Arabic has **six**
CLDR plural categories (zero, one, two, few, many, other); Polish and Russian have
distinct "few" vs. "many" forms. The naive `count + "s"` is wrong almost everywhere.
Use **ICU MessageFormat** (backed by CLDR plural rules), supported by `i18next`,
`react-intl`/FormatJS, `Intl.PluralRules`, and most modern libraries.
```
{count, plural,
=0 {No items}
one {# item}
few {# items}
many {# items}
other {# items}
}
```
> English resolves only to `one`/`other`; `few`/`many` (and `=0`) are listed here
> for the languages that need them (Polish, Russian, Arabic…). A locale picks the
> matching category automatically.
```js
// Pick the right category programmatically when needed
new Intl.PluralRules('pl').select(2); // → "few"
new Intl.PluralRules('en').select(2); // → "other"
```
**Gender & grammatical agreement:** adjectives, articles, and past tense can change
with the gender of a noun or the user. Use ICU `select` rather than building
sentences from parts, and avoid embedding the user's name mid-sentence where
grammar would need to agree with it.
```
{gender, select,
female {She updated her profile}
male {He updated his profile}
other {They updated their profile}
}
```
---
## Locale-Aware Formatting
Dates, numbers, currency, and units are **locale data**, not strings. Use the
built-in `Intl` APIs (or platform equivalents) instead of hand-rolling formats.
```js
// Dates — never assume MM/DD/YYYY (that's US-only)
new Intl.DateTimeFormat('en-US').format(date); // 3/13/2026 (M/D)
new Intl.DateTimeFormat('en-GB').format(date); // 13/03/2026 (D/M)
new Intl.DateTimeFormat('ja-JP').format(date); // 2026/3/13
new Intl.DateTimeFormat('ar-EG').format(date); // ١٣/٣/٢٠٢٦
// Numbers — decimal/grouping separators vary
new Intl.NumberFormat('de-DE').format(1234567.89); // "1.234.567,89"
new Intl.NumberFormat('en-US').format(1234567.89); // "1,234,567.89"
// Currency — symbol, placement, and spacing are locale-specific
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' })
.format(99.5); // "99,50 €"
// Units, relative time, lists
new Intl.NumberFormat('en', { style: 'unit', unit: 'kilometer' }).format(5);
new Intl.RelativeTimeFormat('en').format(-1, 'day'); // "1 day ago"
new Intl.ListFormat('en').format(['a','b','c']); // "a, b, and c"
```
### Beyond format strings
| Concern | Watch out for |
|---------|---------------|
| **Name order** | Many cultures put family name first (China, Japan, Korea, Hungary). Don't label fields "First / Last"—use "Given / Family" and don't force a split. |
| **Address format** | Field order, postal-code position, and required fields differ; some countries have no postal codes or states. Use a flexible/freeform model. |
| **Calendars & weeks** | Week may start Sunday, Monday, or Saturday; non-Gregorian calendars exist (Islamic, Hebrew, Japanese eras). |
| **Time zones** | Store UTC, display in the user's zone; show the zone for events. |
| **Phone numbers** | Vary in length and grouping; validate per-region (libphonenumber), not with one regex. |
| **Measurement units** | Metric vs. imperial; convert, don't just relabel. |
### Forms that survive every locale
```
❌ Locale-blind form ✅ Locale-flexible form
───────────────────── ──────────────────────────────────
First name [______] Given name(s) [__________]
Last name [______] Family name [__________]
(don't require a split; allow one
State [__▼] full-name field for cultures that
ZIP [_____] don't separate them)
Country [__▼] ← drives the rest
Address [__________]
(reveal region/postal fields based on
country; some have neither)
```
- Drive address fields from the selected **country**, not a fixed US template.
- Don't mark a postal code or "state/province" as required globally—many places
have neither.
- Validate phone numbers per region; store names as entered (don't force casing or
ASCII—accept Unicode, including non-Latin scripts).
---
## Translation-Friendly UI
The UI must give translators what they need and tolerate what they produce.
- **No text baked into images.** Text in raster images can't be translated, scaled,
or read by screen readers. Use real text over CSS, or SVG with translatable `<text>`.
- **Externalize every string.** No hard-coded user-facing text in components.
Reference keys (`t('checkout.payNow')`), not English literals scattered in code.
- **Provide context for translators.** A key named `submit` is ambiguous—"submit a
form" vs. "submit to authority" translate differently. Add descriptions/comments
and, ideally, screenshots so translators see where the string appears.
- **Allow for missing translations.** New strings ship before translations land—fall
back gracefully to a base locale, never to a blank or a raw key like
`checkout.payNow` shown to the user.
- **Avoid idioms, humor, and culture-bound metaphors** ("hit it out of the park",
"piece of cake")—they don't translate and waste translator effort.
- **Leave room in the layout** (see Text Expansion) and avoid embedding UI controls
mid-sentence, which forces translators to break grammar around them.
- **Colors, icons, and imagery carry cultural meaning.** Red = danger/luck/celebration
depending on region; a mailbox or trash-can icon may not be universally recognized.
Prefer well-established universal icons and don't rely on one alone.
---
## Language Switching UX
- **List languages in their own name (endonyms):** "Deutsch", "Français", "日本語",
"العربية"—not "German", "French", "Japanese", "Arabic". A user who needs another
language can't necessarily read the current one.
- **Don't use flags to represent languages.** Languages ≠ countries: one flag can't
represent Spanish (20+ countries) and one country may have many languages. Flags
are also politically loaded. Use language names.
- **Detection vs. explicit choice:** you may default from `Accept-Language`/device
locale, but always offer an explicit, easy-to-find switch and **persist** the
user's choice (per account and/or `localStorage`/cookie). Never trap users in a
language they didn't pick.
- **Reflect the choice in everything:** `lang`/`dir` attributes, formatting, and the
URL/route where relevant (good for SEO: `/de/`, `hreflang` tags).
- Put the switcher somewhere conventional (header or footer) and reachable without
understanding the current language—an icon + endonym works well.
---
## Quick Checklist
- [ ] All user-facing strings externalized (no hard-coded text)
- [ ] Logical CSS properties; layout verified in `dir="rtl"`
- [ ] Directional icons mirror; logos/media/numerals do not
- [ ] Containers tolerate 30–40%+ text expansion; no fixed-width labels/buttons
- [ ] ICU MessageFormat for plurals/gender (not `count + "s"`)
- [ ] Dates, numbers, currency, units via `Intl` / locale data
- [ ] No text baked into images
- [ ] Translator context (descriptions/screenshots) provided
- [ ] Graceful fallback for missing translations (never raw keys)
- [ ] Language switcher uses endonyms, not flags; choice persists
- [ ] Pseudo-localization run in CI to catch overflow and hard-coded strings
---
## Common Mistakes
1. **Hard-coded strings** — English literals in components; impossible to translate.
2. **Assuming LTR** — physical CSS, manual `left`/`right` everywhere; RTL breaks.
3. **Flags as languages** — wrong, ambiguous, and politically fraught.
4. **Fixed-width text containers** — translated text overflows or truncates.
5. **Concatenating fragments** — string-gluing ignores word order and grammar.
6. **English plural rules** — `count + "s"` fails in most languages.
7. **Text in images** — untranslatable, inaccessible, doesn't scale.
8. **Hard-coded date/number formats** — `MM/DD/YYYY` and `,`/`.` are locale-specific.
9. **"First name / Last name" assumptions** — name order and structure vary widely.
10. **No translation fallback** — users see blank UI or raw keys for new strings.
11. **Idioms and humor** — don't survive translation; confuse translators.
12. **No translator context** — ambiguous keys produce wrong translations.
---
## Sources
- [W3C Internationalization (i18n) Activity](https://www.w3.org/International/) — specs and best practices
- [MDN: CSS Logical Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)
- [MDN: Intl object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
- [Unicode CLDR](https://cldr.unicode.org/) — locale data and plural rules
- [ICU MessageFormat](https://unicode-org.github.io/icu/userguide/format_parse/messages/) — plurals, gender, formatting
- [Material Design: Bidirectionality](https://m2.material.io/design/usability/bidirectionality.html)
- [Nielsen Norman Group: Translation & localization](https://www.nngroup.com/topic/international-users/)
- [Smashing Magazine: RTL Styling 101](https://www.smashingmagazine.com/2017/11/right-to-left-mobile-design/)
references/19-search-ux.md
# Search UX
Search is a critical navigation pattern that goes beyond basic text matching. This reference covers search input design, autocomplete, faceted filtering, results display, and emerging AI-powered search patterns.
---
## Search Box Design
### Placement & Sizing
```
Top navigation (most common):
┌─────────────────────────────────────────────────────┐
│ Logo [🔍 Search... ] [User]│
└─────────────────────────────────────────────────────┘
Centered hero (landing pages / search-first apps):
┌─────────────────────────────────────────────────────┐
│ │
│ What are you looking for? │
│ [🔍 Search products, brands, categories... ] │
│ │
└─────────────────────────────────────────────────────┘
```
**Guidelines:**
- Minimum width: 300px (desktop), full-width on mobile
- Visible by default (not hidden behind an icon on desktop)
- Magnifying glass icon on the left (universal affordance)
- Clear/reset button (✕) appears when text is entered
- Search on Enter or with a submit button
- Consistent location across all pages
### Input Behavior
```html
<div class="search-container" role="search">
<label for="search" class="sr-only">Search</label>
<span class="search-icon" aria-hidden="true">🔍</span>
<input
id="search"
type="search"
placeholder="Search..."
autocomplete="off"
aria-expanded="false"
aria-controls="search-results"
aria-autocomplete="list"
/>
<button class="clear-btn" aria-label="Clear search" hidden>✕</button>
</div>
```
- `type="search"` for native clear button on some browsers
- `role="search"` on the container for screen readers
- Focus on keyboard shortcut (/ or Ctrl+K is common)
- Show shortcut hint in the placeholder: "Search... (⌘K)"
- Debounce input: 200-300ms before triggering search
- Preserve query on back navigation
### Placeholder Text
```
❌ "Search..." (too vague)
❌ "Type here to search for products, articles, help topics, and more..." (too long)
✅ "Search products and articles..." (specific, concise)
✅ "Search by name, email, or ID..." (tells user what's searchable)
```
- Indicate what types of content are searchable
- Keep under 40 characters
- Don't rely on placeholder alone for labeling (accessibility)
---
## Autocomplete
### Suggestion Types
```
┌─────────────────────────────────────────┐
│ 🔍 "react hook" [✕] │
├─────────────────────────────────────────┤
│ Recent searches │
│ 🕐 react hooks tutorial │
│ 🕐 react context api │
├─────────────────────────────────────────┤
│ Suggestions │
│ 🔍 react hooks best practices │
│ 🔍 react hook form │
│ 🔍 react hooks vs classes │
├─────────────────────────────────────────┤
│ Products │
│ 📦 React Hook Form (library) 4.9★ │
│ 📦 useHooks (collection) 4.7★ │
└─────────────────────────────────────────┘
```
**Suggestion categories:**
- **Recent searches**: User's own history (with clear history option)
- **Popular/trending**: Common searches from all users
- **Predictive**: Completions based on typing so far
- **Entity matches**: Direct links to specific results (products, people, pages)
- **Category suggestions**: "Search in Documentation" or "Search in Products"
### Keyboard Navigation
```
Tab / ↓ Move to suggestions dropdown
↑ / ↓ Navigate between suggestions
Enter Select highlighted suggestion / submit search
Esc Close dropdown, keep query text
→ Complete highlighted suggestion in input (Google-style)
```
- Highlight active suggestion visually
- Arrow keys should not move cursor in input while dropdown is open
- First suggestion can be auto-highlighted (or not — be consistent)
- Screen reader announcement of current suggestion
### Highlighting Matches
```
Searching for "des"
┌──────────────────────────┐
│ 🔍 **des**ign system │ ← Bold matching portion
│ 🔍 **des**ktop app │
│ 🔍 ui **des**ign patterns │
└──────────────────────────┘
```
- Bold or highlight the matching text portion
- Works with mid-word and multi-word matches
- Match highlighting in both suggestion text and description
### Performance
- Show suggestions after 1-2 characters minimum
- Debounce: 150-300ms after last keystroke
- Show loading indicator if search takes >300ms
- Cache recent suggestion results
- Maximum 7-10 suggestions visible (scroll for more)
---
## Faceted Search & Filters
### Filter Panel Layout
```
Desktop — Side panel: Mobile — Bottom sheet:
┌──────────┬──────────────────┐ ┌──────────────────────┐
│ Filters │ Results │ │ Results (23) │
│ │ │ │ [🔽 Filter & Sort] │
│ Category │ │ └──────────────────────┘
│ ☑ Books │ 23 results │ ↓ (tap)
│ ☐ Video │ │ ┌──────────────────────┐
│ ☐ Audio │ ┌──────────────┐ │ │ ── Filters ── │
│ │ │ Result 1 │ │ │ Category ▾ │
│ Price │ │ Result 2 │ │ │ Price Range ▾ │
│ $0-$25 │ │ Result 3 │ │ │ Rating ▾ │
│ $25-$50 │ └──────────────┘ │ │ │
│ │ │ │ [Reset] [Apply (23)] │
└──────────┴──────────────────┘ └──────────────────────┘
```
### Active Filter Chips
```
┌─────────────────────────────────────────────────────┐
│ Active: [Category: Books ✕] [Price: $0-$25 ✕] │
│ [Rating: 4+ stars ✕] [Clear all filters] │
├─────────────────────────────────────────────────────┤
│ 23 results for "design patterns" │
```
- Show all active filters as removable chips above results
- Individual remove (✕) on each chip
- "Clear all filters" link when multiple are active
- Update result count in real-time as filters change
- Persist filter selections on back navigation
### Filter UX Patterns
| Filter Type | UI Pattern | Best For |
|-------------|-----------|----------|
| **Single select** | Radio buttons / dropdown | Category, sort order |
| **Multi select** | Checkboxes | Tags, sizes, colors |
| **Range** | Slider or min/max inputs | Price, date range |
| **Boolean** | Toggle switch | "In stock", "Free shipping" |
| **Text** | Search within filter | Long category lists |
| **Color** | Color swatches | Product colors |
| **Rating** | Star row (clickable) | Minimum rating |
### Filter Counts
```
Category
☐ Books (156)
☐ Video (43)
☐ Audio (12)
☐ Courses (0) ← Gray out or hide zero-count options
```
- Show result count per filter option
- Update counts as other filters are applied
- Gray out (don't hide) zero-count options to prevent layout shift
- Option: hide zero-count in secondary/collapsed filters
---
## Search Results
### Result Layout
```
┌─────────────────────────────────────────────────────┐
│ About 23 results for "design patterns" (0.12s) │
│ Showing results from: [All ▾] │
├─────────────────────────────────────────────────────┤
│ │
│ 📄 Design Patterns: Elements of Reusable Software │
│ www.example.com/books/design-patterns │
│ The definitive guide to **design** **patterns** │
│ in object-oriented programming. Covers 23 │
│ fundamental **patterns**... │
│ ⭐⭐⭐⭐⭐ (2,341 reviews) • Books • $45.99 │
│ │
│ 📄 UI Design Patterns — Best Practices │
│ www.example.com/articles/ui-patterns │
│ A collection of common UI **design** **patterns** │
│ for web and mobile applications... │
│ Articles • Free • Updated 2 days ago │
│ │
└─────────────────────────────────────────────────────┘
```
**Result item components:**
- Title (linked, keyword-highlighted)
- URL/breadcrumb path
- Snippet with highlighted matching terms
- Metadata (rating, category, date, price)
- Thumbnail image (when available)
### Result Sorting
```
Sort by: [Relevance ▾]
• Relevance (default)
• Newest first
• Oldest first
• Price: Low to High
• Price: High to Low
• Rating: High to Low
• Most Popular
```
- Default to relevance for text searches
- Default to newest for browsing/feed contexts
- Show current sort prominently
- Changing sort should not clear filters
### Result Grouping
```
All (23) | Articles (12) | Products (8) | People (3)
─── Articles ───────────────────
📄 UI Design Patterns...
📄 Design Thinking...
─── Products ──────────────────
📦 Design Patterns Book...
📦 Pattern Library Tool...
```
- Group by content type when searching across multiple types
- Tab navigation between groups
- Show count per group
- "All" tab shows mixed results with type indicators
---
## Zero Results
### Helpful Messaging
```
┌─────────────────────────────────────────┐
│ No results for "dezign paterns" │
│ │
│ Did you mean: design patterns? │
│ │
│ Suggestions: │
│ • Check your spelling │
│ • Try more general keywords │
│ • Try fewer keywords │
│ │
│ Popular searches: │
│ [UX design] [Typography] [Color theory]│
│ │
│ Or browse categories: │
│ [Books] [Articles] [Courses] │
└─────────────────────────────────────────┘
```
**Zero results checklist:**
- [ ] Spelling suggestion ("Did you mean...?")
- [ ] Actionable tips (fewer keywords, check spelling)
- [ ] Alternative queries or popular searches
- [ ] Category browsing links
- [ ] Contact support or request option
- [ ] Never show a completely blank page
---
## Advanced Search
### Query Syntax
```
Common query operators (if supported, document them):
"exact phrase" — Match exact phrase
term1 AND term2 — Both terms required
term1 OR term2 — Either term
-excluded — Exclude term
field:value — Search specific field
type:article — Filter by content type
```
- Don't require advanced syntax — it should enhance, not replace basic search
- Show syntax guide as a help tooltip or link
- Parse common natural language queries intelligently
### Saved Searches
```
┌─ Saved Searches ────────────────────────┐
│ 🔖 "react hooks" in:docs │
│ 🔖 "bugs" type:issue status:open │
│ 🔖 author:me modified:last-week │
│ │
│ [+ Save current search] │
└──────────────────────────────────────────┘
```
- Allow saving complex filter + query combinations
- Accessible from search interface
- Named searches with optional notifications (alert on new results)
- Shareable search links
---
## Voice Search
### Microphone Button
```
┌─────────────────────────────────────────┐
│ 🔍 Search... [🎤] │
└─────────────────────────────────────────┘
↓ (tap microphone)
┌─────────────────────────────────────────┐
│ 🎤 Listening... │
│ 🔴 ∿∿∿∿∿∿∿∿∿∿ │
│ [Cancel] │
└─────────────────────────────────────────┘
↓ (speech detected)
┌─────────────────────────────────────────┐
│ 🔍 "best design patterns for" [✕] │
│ Transcribing... │
└─────────────────────────────────────────┘
```
- Show waveform or animation during listening
- Display transcription in real-time
- Allow editing before submitting
- "Cancel" to stop without searching
- Handle no-speech-detected timeout gracefully
---
## AI-Powered Search
### Natural Language Queries
```
Traditional search: "return policy days limit"
AI-powered search: "How many days do I have to return an item?"
↓
┌─────────────────────────────────────────┐
│ 📍 Quick Answer │
│ You have 30 days from purchase to │
│ return most items for a full refund. │
│ Exceptions apply for electronics (15 │
│ days) and final sale items. │
│ │
│ Source: Return Policy FAQ │
│ [View full policy] │
├─────────────────────────────────────────┤
│ Related results: │
│ 📄 Return Policy... │
│ 📄 How to Start a Return... │
│ 📄 Refund Processing Times... │
└─────────────────────────────────────────┘
```
### AI Search Patterns
- **Direct answer**: Synthesized answer at top, traditional results below
- **Source attribution**: Every AI answer links to source documents
- **Confidence**: Only show direct answer when confidence is high
- **Follow-up**: "Ask a follow-up question" for conversational refinement
- **Fallback**: If AI can't answer, show traditional search results gracefully
- **Transparency**: "AI-generated answer" label clearly visible
### Semantic Search UX
- Understand synonyms and related concepts automatically
- "Showing results for X" when query is reinterpreted
- Allow explicit override: "Search instead for [exact query]"
- Explain why results are relevant when not keyword-obvious
---
## Key Metrics
| Metric | Target | Context |
|--------|--------|---------|
| Search success rate | > 70% | Users who find what they need |
| Zero results rate | < 10% | Queries returning nothing |
| Click-through rate | > 40% | Clicks on search results |
| Time to first click | < 10s | Speed of finding relevant result |
| Search exit rate | < 30% | Users who leave after searching |
| Refinement rate | < 30% | Users who modify their query |
| Autocomplete usage | > 30% | Suggestions selected |
| Queries per session | < 2 | Finding it on first try |
---
## Anti-Patterns
1. **Hidden search** — Search behind an icon on content-heavy sites
2. **No autocomplete** — Missing the most basic search enhancement
3. **Search that ignores typos** — No fuzzy matching or spell correction
4. **Results with no snippets** — Just titles with no context
5. **No empty state guidance** — "No results" with no help
6. **Filter clearing on new search** — Losing filter context on new query
7. **Slow autocomplete** — Suggestions that appear after the user finishes typing
8. **Non-persistent queries** — Search term cleared on back navigation
9. **Search that only matches titles** — Missing full-text search
10. **No result count** — Users can't gauge if their query is too broad or narrow
---
## Sources
- [Nielsen Norman Group: Search UX](https://www.nngroup.com/topic/search/) — Usability research
- [Baymard Institute: Search UX](https://baymard.com/blog/categories/search) — E-commerce search patterns
- [Algolia: Search UX Best Practices](https://www.algolia.com/blog/) — Search implementation
- [Google Search Central: Search Guidelines](https://developers.google.com/search) — Search quality
- [WAI-ARIA: Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) — Accessible autocomplete
- [Elasticsearch: Search UI](https://www.elastic.co/search-labs) — Search technology patterns
references/21-data-tables.md
# Data Tables & Lists
Data tables are one of the most common UI patterns in enterprise and admin applications. Well-designed tables help users scan, compare, sort, filter, and act on structured data efficiently.
---
## Table Layout Patterns
### Basic Table Structure
```html
<table class="data-table" role="grid" aria-label="User accounts">
<thead>
<tr>
<th scope="col" aria-sort="ascending">
<button class="sort-button">
Name <span class="sort-icon">↑</span>
</button>
</th>
<th scope="col">Email</th>
<th scope="col">Role</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice Smith</td>
<td>alice@example.com</td>
<td><span class="badge badge--admin">Admin</span></td>
<td>
<button class="btn-icon" aria-label="Edit Alice Smith">✏️</button>
<button class="btn-icon" aria-label="Delete Alice Smith">🗑️</button>
</td>
</tr>
</tbody>
</table>
```
### Fixed Headers
Keep headers visible while scrolling long tables.
```css
.data-table-container {
max-height: 600px;
overflow-y: auto;
}
.data-table thead th {
position: sticky;
top: 0;
z-index: 1;
background: white;
box-shadow: 0 1px 0 #e5e7eb;
}
```
### Fixed Columns
Keep identifier columns visible during horizontal scroll.
```css
.data-table td:first-child,
.data-table th:first-child {
position: sticky;
left: 0;
z-index: 2;
background: white;
box-shadow: 1px 0 0 #e5e7eb;
}
/* Corner cell needs highest z-index */
.data-table thead th:first-child {
z-index: 3;
}
```
### Column Resizing
```javascript
function initColumnResize(table) {
const headers = table.querySelectorAll('th');
headers.forEach(header => {
const resizer = document.createElement('div');
resizer.className = 'column-resizer';
header.appendChild(resizer);
let startX, startWidth;
resizer.addEventListener('mousedown', (e) => {
startX = e.clientX;
startWidth = header.offsetWidth;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
});
function handleMouseMove(e) {
const newWidth = Math.max(60, startWidth + (e.clientX - startX));
header.style.width = `${newWidth}px`;
}
function handleMouseUp() {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
}
});
}
```
```css
.column-resizer {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 4px;
cursor: col-resize;
background: transparent;
}
.column-resizer:hover,
.column-resizer:active {
background: #3b82f6;
}
```
### Responsive Table Strategies
| Strategy | When to Use |
|----------|-------------|
| Horizontal scroll | Many columns, data must stay tabular |
| Column hiding | Secondary columns can be hidden on mobile |
| Card layout | Reflow rows into stacked cards below breakpoint |
| Priority columns | Show only high-priority columns, expand for more |
#### Card Reflow Pattern
```css
@media (max-width: 768px) {
.data-table thead {
display: none;
}
.data-table tr {
display: block;
margin-bottom: 12px;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 12px;
}
.data-table td {
display: flex;
justify-content: space-between;
padding: 4px 0;
border: none;
}
.data-table td::before {
content: attr(data-label);
font-weight: 600;
color: #6b7280;
}
}
```
---
## Sorting
### Sort Interaction
```
┌──────────────────────────────────────────────────────────────┐
│ Name ↑ │ Email │ Created ↕ │ Status │
├──────────────────────────────────────────────────────────────┤
│ Alice Smith │ alice@ex.com │ 2025-01-15 │ Active │
│ Bob Jones │ bob@ex.com │ 2025-02-01 │ Active │
│ Carol Lee │ carol@ex.com │ 2024-12-20 │ Inactive │
└──────────────────────────────────────────────────────────────┘
↑ = Ascending (currently active)
↕ = Sortable but not active
```
#### Sort State Indicators
| Symbol | Meaning |
|--------|---------|
| ↑ | Sorted ascending |
| ↓ | Sorted descending |
| ↕ | Sortable (click to sort) |
| (none) | Not sortable |
#### Best Practices
- Click column header to sort ascending, click again for descending, third click to clear
- Always show sort direction indicator on the active column
- Use `aria-sort="ascending"` / `aria-sort="descending"` for screen readers
- Default sort should match the most common use case
- Support multi-column sort with Shift+Click (show sort priority numbers)
---
## Filtering
### Filter Bar Pattern
```
┌──────────────────────────────────────────────────────────────┐
│ 🔍 Search users... [Status ▼] [Role ▼] [Date range ▼] │
│ │
│ Active filters: Status: Active ✕ │ Role: Admin ✕ [Clear] │
├──────────────────────────────────────────────────────────────┤
│ Showing 24 of 156 users │
```
### Filter Types by Data
| Data Type | Filter UI |
|-----------|-----------|
| Text | Search input (debounced, 300ms) |
| Enum/Category | Dropdown or checkbox group |
| Boolean | Toggle switch |
| Date | Date range picker |
| Number | Range slider or min/max inputs |
| Tags | Multi-select with typeahead |
### Filter Placement
```
Filter placement decision:
├── Few filters (1-3)?
│ └── → Inline filter bar above table
├── Many filters (4-8)?
│ └── → Collapsible filter panel (sidebar or above table)
└── Complex filters with dependencies?
└── → Dedicated filter modal or side panel
```
### Active Filter Display
- Show active filters as dismissible chips/tags
- Include "Clear all filters" action when any filter is active
- Show result count that updates as filters change
- Persist filters in URL params for shareable filtered views
---
## Pagination vs. Infinite Scroll vs. Virtual Scrolling
### Decision Tree
```
How should users navigate through data?
├── User needs to reach specific pages or find exact position?
│ └── → Pagination
├── Content is browsable/exploratory (social feed, catalog)?
│ ├── Total items < 1,000?
│ │ └── → Infinite scroll
│ └── Total items > 1,000?
│ └── → Infinite scroll with "jump to" option
├── All data must be in DOM for client-side operations?
│ └── → Virtual scrolling (windowed rendering)
└── Data set is very large (10,000+ rows)?
└── → Server-side pagination OR virtual scrolling
```
### Pagination
```
┌──────────────────────────────────────────────────────────────┐
│ Showing 1-25 of 156 Rows per page: [25 ▼] │
│ │
│ [← Prev] 1 2 3 ... 6 7 [Next →] │
└──────────────────────────────────────────────────────────────┘
```
**Pagination best practices:**
- Show current range ("Showing 1-25 of 156")
- Allow page size selection (10, 25, 50, 100)
- Show first/last page links for long ranges
- Use ellipsis for large page counts
- Keyboard accessible (arrow keys between pages)
### Infinite Scroll
```javascript
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
loadNextPage();
}
},
{ rootMargin: '200px' } // Trigger 200px before reaching end
);
observer.observe(document.querySelector('.load-trigger'));
```
**Infinite scroll best practices:**
- Show loading indicator at bottom during fetch
- Preserve scroll position on back navigation
- Provide "Back to top" button after scrolling
- Show total count if known ("Showing 75 of ~500")
- Combine with a "Load more" button as fallback
### Virtual Scrolling
Only render rows visible in the viewport.
```javascript
// Virtual scroll concept
function getVisibleRows(scrollTop, viewportHeight, rowHeight, totalRows) {
const startIndex = Math.floor(scrollTop / rowHeight);
const endIndex = Math.min(
startIndex + Math.ceil(viewportHeight / rowHeight) + 1,
totalRows
);
return {
startIndex,
endIndex,
offsetY: startIndex * rowHeight,
totalHeight: totalRows * rowHeight
};
}
```
**Virtual scrolling best practices:**
- Keep row height consistent or measure dynamically
- Render buffer rows above and below viewport (5-10 rows)
- Show scroll position indicator for long lists
- Support keyboard navigation (arrow keys, Page Up/Down)
---
## Bulk Selection & Actions
### Selection Patterns
```
┌──────────────────────────────────────────────────────────────┐
│ ☑ Select all (24 on this page) [Select all 156 →] │
│ 3 selected [Delete] [Export] [Change status ▼] [✕ Clear] │
├──────────────────────────────────────────────────────────────┤
│ ☑ │ Alice Smith │ alice@ex.com │ Active │ │
│ ☐ │ Bob Jones │ bob@ex.com │ Active │ │
│ ☑ │ Carol Lee │ carol@ex.com │ Inactive │ │
│ ☑ │ Dave Park │ dave@ex.com │ Active │ │
└──────────────────────────────────────────────────────────────┘
```
### Selection Interactions
| Action | Behavior |
|--------|----------|
| Click checkbox | Toggle individual row |
| Shift + Click | Select range from last selected |
| Header checkbox | Select/deselect all on current page |
| "Select all N" | Select across all pages |
### Bulk Action Bar
```css
.bulk-action-bar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: #eff6ff;
border-bottom: 1px solid #bfdbfe;
}
```
**Best practices:**
- Show bulk action bar only when items are selected
- Display count of selected items
- Require confirmation for destructive bulk actions
- Show progress indicator for long-running bulk operations
- Allow cancellation of in-progress bulk operations
---
## Row Expansion & Detail
### Expandable Rows
```
┌──────────────────────────────────────────────────────────────┐
│ ▶ │ Order #1234 │ Alice Smith │ $149.00 │ Shipped │
│ ▼ │ Order #1235 │ Bob Jones │ $89.50 │ Processing │
│ ├──────────────────────────────────────────────────────────┤
│ │ Items: Widget A (×2), Gadget B (×1) │
│ │ Tracking: 1Z999AA10123456784 │
│ │ Estimated delivery: Jan 20, 2025 │
│ ├──────────────────────────────────────────────────────────┤
│ ▶ │ Order #1236 │ Carol Lee │ $250.00 │ Delivered │
└──────────────────────────────────────────────────────────────┘
```
**Best practices:**
- Use chevron (▶/▼) to indicate expandability
- Expand only one row at a time (accordion) or allow multiple
- Keep expanded content visually connected to the parent row
- Indent or inset expanded content for visual hierarchy
### Inline Editing
```javascript
function enableInlineEdit(cell) {
const currentValue = cell.textContent;
const input = document.createElement('input');
input.value = currentValue;
input.className = 'inline-edit-input';
cell.replaceChildren(input);
input.focus();
input.select();
input.addEventListener('blur', () => saveEdit(cell, input.value, currentValue));
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') input.blur();
if (e.key === 'Escape') {
input.value = currentValue;
input.blur();
}
});
}
```
```css
.inline-edit-input {
width: 100%;
padding: 4px 8px;
border: 2px solid #3b82f6;
border-radius: 4px;
font: inherit;
background: #eff6ff;
}
```
**Inline edit guidelines:**
- Double-click or click edit icon to activate
- Show clear visual change (border, background) when in edit mode
- Save on blur or Enter, cancel on Escape
- Validate before saving, show errors inline
- Show saving indicator (spinner) during async save
---
## Empty States & Loading
### Empty State Patterns
```
┌──────────────────────────────────────────────────────────────┐
│ │
│ 📋 │
│ No results found │
│ │
│ Your search "xyz" didn't match any users. │
│ Try adjusting your filters or search terms. │
│ │
│ [Clear filters] [Add new user] │
│ │
└──────────────────────────────────────────────────────────────┘
```
| Empty State Type | Message Pattern |
|-----------------|-----------------|
| No data yet | Explain what will appear + CTA to create first item |
| No search results | Acknowledge query + suggest adjustments |
| Filtered to empty | Show active filters + "Clear filters" button |
| Error loading | Explain what happened + "Retry" button |
### Loading Skeletons
```css
.skeleton-row {
display: flex;
gap: 16px;
padding: 12px 16px;
animation: skeleton-pulse 1.5s ease-in-out infinite;
}
.skeleton-cell {
height: 16px;
background: #e5e7eb;
border-radius: 4px;
}
.skeleton-cell--name { width: 150px; }
.skeleton-cell--email { width: 200px; }
.skeleton-cell--status { width: 80px; }
@keyframes skeleton-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
```
**Loading best practices:**
- Show skeleton that matches table structure (column widths)
- Show 5-10 skeleton rows regardless of expected count
- Transition smoothly from skeleton to real data
- Keep headers visible during loading
- Show spinner only for subsequent page loads, not initial
---
## Table Toolbar
### Standard Toolbar Layout
```
┌──────────────────────────────────────────────────────────────┐
│ Users (156) │
│ │
│ 🔍 Search... [Status ▼] [Role ▼] │ [Export ▼] [+ Add] │
│ │
│ Active filters: Status: Active ✕ │ Showing 1-25 of 156 │
├──────────────────────────────────────────────────────────────┤
│ ☐ │ Name ↕ │ Email │ Role │ Status │ ⋮ │
```
### Column Visibility Toggle
```
┌─────────────────────────────┐
│ Columns │
├─────────────────────────────┤
│ ☑ Name │
│ ☑ Email │
│ ☑ Role │
│ ☐ Created date │
│ ☐ Last login │
│ ☑ Status │
├─────────────────────────────┤
│ [Reset to default] │
└─────────────────────────────┘
```
---
## Accessibility
### Table Accessibility Checklist
- [ ] Use semantic `<table>`, `<thead>`, `<tbody>`, `<th>`, `<td>` elements
- [ ] Add `scope="col"` to column headers, `scope="row"` to row headers
- [ ] Use `aria-sort` on sortable column headers
- [ ] Provide `aria-label` or `aria-labelledby` on the table
- [ ] Ensure keyboard navigation works (Tab between interactive elements)
- [ ] Row actions accessible via keyboard (Enter/Space to activate)
- [ ] Bulk selection announced to screen readers ("3 rows selected")
- [ ] Pagination controls are keyboard accessible
- [ ] Sort changes announced via `aria-live` region
### Keyboard Navigation
| Key | Action |
|-----|--------|
| Tab | Move to next interactive element |
| Shift + Tab | Move to previous interactive element |
| Enter / Space | Activate button, toggle checkbox, start inline edit |
| Escape | Cancel inline edit, close dropdown |
| Arrow keys | Navigate within dropdowns or between rows (if using `role="grid"`) |
---
## Key Metrics
| Metric | Value | Context |
|--------|-------|---------|
| Rows per page default | 25 | Balance between scanning and loading |
| Max visible columns | 7-10 | Before horizontal scroll or hiding |
| Search debounce | 300ms | Delay before triggering search |
| Skeleton rows | 5-10 | During initial load |
| Column min width | 60px | Minimum resizable width |
| Row height | 40-52px | Comfortable click/tap target |
| Bulk action confirm | Always | For destructive operations |
---
## Anti-Patterns
1. **No sort indicators** - Users can't tell which column is sorted or in which direction
2. **Client-side sort on server-paginated data** - Sorts only the visible page, not the full dataset
3. **Pagination without count** - Users don't know how much data exists
4. **Tiny action buttons** - Hard to click, especially on touch devices
5. **No empty state** - Blank table with headers but no guidance
6. **Inline editing without undo** - Users can't recover from accidental changes
7. **Checkbox column without bulk actions** - Selection exists but does nothing
8. **Filter resets on navigation** - Losing filter state when leaving and returning
9. **No loading state between pages** - Content jumps, users lose context
10. **Forced horizontal scroll on mobile** - Use card reflow instead
---
## Sources
- [Baymard Institute - Data Table Usability](https://baymard.com/blog/data-table-design)
- [Nielsen Norman Group - Data Tables](https://www.nngroup.com/articles/data-tables/)
- [Smashing Magazine - Design Patterns for Complex Data Tables](https://www.smashingmagazine.com/2019/02/complex-web-tables/)
- [TanStack Table](https://tanstack.com/table/) - Headless table library
- [AG Grid - Best Practices](https://www.ag-grid.com/javascript-data-grid/) - Enterprise grid reference
- [Ant Design - Table Component](https://ant.design/components/table) - Pattern reference
references/20-emotional-design.md
# Emotional Design & Trust
Emotional design goes beyond usability to create meaningful, trustworthy, and delightful experiences. This reference covers Don Norman's three levels of design, trust-building patterns, microinteraction delight, error empathy, and brand personality in UI.
---
## Don Norman's Three Levels of Design
### 1. Visceral Level — "I want it"
The immediate, instinctive reaction to appearance before any interaction.
**Design for first impressions:**
- Clean, polished visual design creates instant credibility
- Color, imagery, and typography set emotional tone
- Animation and motion create sense of quality
- Professional aesthetic increases perceived trustworthiness
**Implementation:**
```
First 50ms: User forms aesthetic impression
First 3s: User decides to stay or leave
First 30s: User evaluates overall credibility
Priority:
1. Visual cleanliness (whitespace, alignment)
2. Professional typography
3. High-quality imagery
4. Consistent color palette
5. Smooth loading experience (skeleton screens, not spinners)
```
### 2. Behavioral Level — "I can use it"
The experience of using the product — functionality, usability, and feel.
**Design for confidence:**
- Predictable, responsive interactions build trust
- Clear feedback on every action
- Forgiving design that handles mistakes gracefully
- Performance that matches expectations
**Implementation:**
```
Every interaction should feel:
✅ Responsive — Feedback within 100ms
✅ Predictable — Consistent behavior across contexts
✅ Forgiving — Easy to undo, hard to make irreversible errors
✅ Efficient — Minimum steps to accomplish goals
✅ Clear — No ambiguity about what happened or what to do next
```
### 3. Reflective Level — "I love it"
The conscious assessment — does this product reflect my identity and values?
**Design for meaning:**
- Brand storytelling through design decisions
- Personalization that feels thoughtful, not creepy
- Social proof and community belonging
- Sustainable and ethical design choices
**Implementation:**
```
Reflective design elements:
- "About" pages that share mission and values
- Sustainability indicators (carbon-neutral badge, etc.)
- Customization that lets users express identity
- Community features that create belonging
- Transparent business practices visible in UI
```
---
## Building Trust
### Trust Signals by Context
| Context | Trust Signals |
|---------|--------------|
| **E-commerce** | Secure checkout badge, return policy, real reviews, price guarantee |
| **SaaS** | Uptime status, security certifications, data location, customer logos |
| **Healthcare** | Credentials display, HIPAA badge, privacy assurance, medical review |
| **Financial** | Encryption indicators, regulatory compliance, insurance/FDIC badges |
| **Social** | Verification badges, community guidelines, moderation transparency |
### Social Proof Patterns
```
Customer logos (B2B):
┌──────────────────────────────────────────┐
│ Trusted by 10,000+ teams including: │
│ [Logo] [Logo] [Logo] [Logo] [Logo] │
└──────────────────────────────────────────┘
Testimonials:
┌──────────────────────────────────────────┐
│ ⭐⭐⭐⭐⭐ │
│ "This tool transformed our workflow." │
│ — Alex Chen, CTO at Acme Inc │
│ [photo] │
└──────────────────────────────────────────┘
Activity indicators:
┌──────────────────────────────────────────┐
│ 🟢 1,234 people using this right now │
│ 📊 50,000+ projects created this month │
└──────────────────────────────────────────┘
Reviews:
┌──────────────────────────────────────────┐
│ 4.8/5 ⭐⭐⭐⭐⭐ (2,341 reviews) │
│ ████████████████████░ 5★ (1,890) │
│ ████░░░░░░░░░░░░░░░░ 4★ (312) │
│ █░░░░░░░░░░░░░░░░░░░ 3★ (89) │
│ ░░░░░░░░░░░░░░░░░░░░ 2★ (31) │
│ ░░░░░░░░░░░░░░░░░░░░ 1★ (19) │
└──────────────────────────────────────────┘
```
**Best practices:**
- Use real names, photos, and company names (not "User123")
- Show negative reviews too — 4.8 stars is more credible than 5.0
- Include specific details ("saved 40% on costs" > "great product")
- Verified purchase/user badges
- Recent reviews weighted more heavily in display
### Security Indicators
```
Checkout:
┌──────────────────────────────────────────┐
│ 🔒 Secure checkout │
│ Your payment info is encrypted │
│ │
│ Card: [ ] │
│ │
│ [Pay $42.00] │
│ │
│ [🔒 SSL] [PCI DSS] [Visa] [Mastercard]│
└──────────────────────────────────────────┘
```
- Lock icon near sensitive input fields
- "Secure" or "encrypted" labels during data entry
- Security certification badges (SSL, PCI, SOC 2)
- Payment provider logos (Stripe, PayPal)
- Don't overdo it — too many badges reduces credibility
### Transparent Pricing
- Show all costs upfront (no hidden fees)
- Clear feature comparison between plans
- "No credit card required" for free trials
- Money-back guarantee prominently displayed
- Annual vs. monthly pricing clearly compared
---
## Delight Through Microinteractions
### When to Add Delight
```
Good moments for delight:
✅ Task completion (confetti on milestone)
✅ First-time achievement (badge unlock)
✅ Loading waits (playful animation)
✅ Empty states (friendly illustration)
✅ Easter eggs (hidden surprises for power users)
Bad moments for delight:
❌ During errors (not the time for jokes)
❌ In critical workflows (checkout, payment)
❌ Repeated actions (animation fatigue)
❌ When it slows the user down
```
### Celebration Animations
```
Achievement unlocked:
┌──────────────────────────────────────────┐
│ │
│ 🎉 Congratulations! │
│ │
│ You completed your first project │
│ │
│ [🏆 View Badge] │
│ │
└──────────────────────────────────────────┘
+ confetti particle animation (1.5s)
+ Scale-up entrance (300ms, ease-out)
+ Respect prefers-reduced-motion
```
- Use sparingly — delight loses effect through repetition
- Always respect `prefers-reduced-motion`
- Keep animations short (< 2 seconds)
- Make them dismissible (click anywhere to close)
- Tie to meaningful achievements, not trivial actions
### Personality in Details
```
Pull-to-refresh: Custom animation matching brand
Loading state: Playful tip or quote instead of generic spinner
404 page: Illustrated, on-brand, with helpful navigation
Empty inbox: "All caught up! 🎉" instead of "No messages"
Hover states: Subtle, satisfying micro-animations
Sound design: Optional, subtle interaction sounds
```
### Easter Eggs
- Hidden features discovered through exploration (Konami code, etc.)
- Fun but never affect core functionality
- Discoverable through word-of-mouth and community
- Must not interfere with accessibility or usability
---
## Error Empathy
### Blame-Free Language
```
❌ "Error 403: Forbidden. You do not have permission."
❌ "Invalid input. Try again."
❌ "You entered an incorrect password."
✅ "You don't have access to this page. Contact your admin for access."
✅ "That didn't match what we expected. Here's what we're looking for..."
✅ "That password doesn't match our records. Reset your password?"
```
**Principles:**
- Never blame the user ("you failed", "invalid", "wrong")
- Explain what happened in plain language
- Suggest a clear next step
- Use "we" language ("We couldn't process..." not "You failed to...")
- Maintain the same brand voice even in errors
### Error Recovery Assistance
```
┌──────────────────────────────────────────┐
│ ⚠️ We couldn't save your changes │
│ │
│ Your internet connection was lost. │
│ Your changes are saved locally and │
│ will sync when you're back online. │
│ │
│ [Try again now] [Work offline] │
└──────────────────────────────────────────┘
```
- Preserve user's work (never lose data due to errors)
- Offer specific recovery actions
- Auto-retry where appropriate (with indication)
- Provide alternative paths ("Save locally", "Try different method")
- Show progress recovery: "You were on step 3 of 5. Continue?"
### 404 / Error Pages
```
┌──────────────────────────────────────────┐
│ │
│ 🗺️ Page Not Found │
│ │
│ We couldn't find what you're looking │
│ for. It may have been moved or │
│ removed. │
│ │
│ [Go to Homepage] [Search] │
│ │
│ Popular pages: │
│ • Dashboard │
│ • Documentation │
│ • Support │
│ │
└──────────────────────────────────────────┘
```
- On-brand illustration (not generic)
- Clear explanation without technical jargon (no "404" as primary message)
- Navigation options (home, search, popular pages)
- Report option if user expects the page to exist
---
## Brand Personality in UI
### Voice Consistency
| Brand Trait | UI Voice Examples |
|-------------|-------------------|
| **Professional** | "Your report is ready for download." |
| **Friendly** | "All done! Your report is ready." |
| **Playful** | "Ta-da! Fresh report, hot off the press." |
| **Technical** | "Report generated. 42 pages, 3.2MB." |
**Voice should be consistent across:**
- Button labels and CTAs
- Error messages and empty states
- Onboarding copy and tooltips
- Email notifications
- Loading and success messages
### Illustration Style
```
Consistent illustration system:
├── Same color palette as brand
├── Consistent line weight and style
├── Diverse, inclusive character representation
├── Used at key moments (empty states, onboarding, errors)
└── Not overused (illustration fatigue)
Usage:
✅ Empty states, error pages, onboarding, feature highlights
❌ Every single screen, decorative-only placements
```
### Animation Character
- Motion should match brand energy (corporate = subtle, consumer = playful)
- Consistent easing curves across all animations
- Interaction sounds (if any) match brand tone
- Loading animations can carry brand personality
- All motion must respect `prefers-reduced-motion`
---
## Key Metrics
| Metric | Target | Context |
|--------|--------|---------|
| Net Promoter Score (NPS) | > 50 | User sentiment |
| Customer satisfaction (CSAT) | > 4.2/5 | Post-interaction rating |
| Trust score | > 80% | Survey: "I trust this product" |
| Error recovery rate | > 85% | Users who recover from errors |
| Brand recognition | Track | Consistent design = recall |
| Return visitor rate | > 40% | Users who come back voluntarily |
| Support ticket rate | < 2% | Low = fewer frustrations |
---
## Anti-Patterns
1. **Forced delight** — Animation or personality where users expect efficiency
2. **Inconsistent voice** — Professional in one area, casual in another
3. **Trust badge overload** — 15 security badges that look like spam
4. **Fake social proof** — Made-up testimonials or inflated numbers
5. **Error blame** — "You did something wrong" instead of helping
6. **Animation fatigue** — Same celebration on every save
7. **Dark delight** — Using personality to distract from deceptive patterns
8. **Ignoring negative emotions** — Only designing for happy paths
9. **Generic stock imagery** — Breaking brand consistency with off-brand photos
10. **Humor in critical moments** — Jokes during payment failures or data loss
---
## Sources
- Norman, D. (2004). "Emotional Design: Why We Love (or Hate) Everyday Things"
- Walter, A. (2011). "Designing for Emotion" — A Book Apart
- [Nielsen Norman Group: Trust in UX](https://www.nngroup.com/topic/trust/) — Trust research
- [Baymard Institute: Trust & Credibility](https://baymard.com/blog/categories/trust) — E-commerce trust
- [Lottie Animations](https://lottiefiles.com/) — Microinteraction library
- [Laws of UX: Peak-End Rule](https://lawsofux.com/peak-end-rule/) — Emotional experience design
- [Material Design: Motion](https://m3.material.io/styles/motion/overview) — Animation personality
references/22-performance-ux.md
# Performance & Loading UX
Perceived performance is often more important than actual performance. Users judge an app's speed not by stopwatch measurements but by how fast it *feels*. This reference covers techniques for making interfaces feel instant, responsive, and reliable.
---
## Perceived Performance
### Response Time Thresholds
| Duration | Perception | UX Treatment |
|----------|-----------|--------------|
| < 100ms | Instant | No indicator needed |
| 100-300ms | Slight delay | Subtle state change (button press) |
| 300ms-1s | Noticeable | Show spinner or progress hint |
| 1-5s | Waiting | Skeleton screen or progress bar |
| 5-10s | Frustrated | Progress bar with estimate + cancel |
| > 10s | Abandonment risk | Progress steps, background processing option |
### Techniques for Feeling Fast
1. **Optimistic updates** — Apply changes immediately, sync later
2. **Skeleton screens** — Show content shape before data arrives
3. **Progressive loading** — Show most important content first
4. **Prefetching** — Load likely-next content before user requests it
5. **Instant navigation** — Use client-side routing with prefetch
6. **Animation as distraction** — Meaningful transitions mask load time
---
## Loading Indicator Decision Tree
```
How long will the operation take?
├── < 100ms (instant)?
│ └── → No indicator (just update the UI)
├── 100ms - 1s (brief)?
│ ├── User triggered the action?
│ │ └── → Button loading state (spinner replaces label or inline spinner)
│ └── Background/automatic?
│ └── → No indicator (avoid flicker)
├── 1s - 5s (noticeable)?
│ ├── Loading content for a region/page?
│ │ └── → Skeleton screen (matches target layout)
│ ├── Processing user action?
│ │ └── → Inline spinner with status text ("Saving...")
│ └── Unknown duration?
│ └── → Indeterminate progress bar
├── 5s - 30s (long)?
│ ├── Duration is predictable?
│ │ └── → Determinate progress bar with percentage
│ └── Duration is unpredictable?
│ └── → Indeterminate progress with stage labels
└── > 30s (very long)?
└── → Background task with notification on completion
("We'll email you when your export is ready")
```
---
## Skeleton Screens
Skeleton screens show a low-fidelity preview of the content layout before data loads. They reduce perceived wait time and prevent layout shifts.
### Basic Skeleton
```css
.skeleton {
background: #e5e7eb;
border-radius: 4px;
position: relative;
overflow: hidden;
}
/* Shimmer animation */
.skeleton::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.4) 50%,
transparent 100%
);
animation: skeleton-shimmer 1.5s infinite;
}
@keyframes skeleton-shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
```
### Skeleton Variants
```html
<!-- Text skeleton -->
<div class="skeleton skeleton--text" style="width: 80%; height: 16px;"></div>
<div class="skeleton skeleton--text" style="width: 60%; height: 16px;"></div>
<!-- Avatar skeleton -->
<div class="skeleton skeleton--circle" style="width: 40px; height: 40px;"></div>
<!-- Image skeleton -->
<div class="skeleton skeleton--image" style="width: 100%; aspect-ratio: 16/9;"></div>
<!-- Card skeleton -->
<div class="skeleton-card">
<div class="skeleton skeleton--image" style="height: 200px;"></div>
<div style="padding: 16px;">
<div class="skeleton skeleton--text" style="width: 70%; height: 20px;"></div>
<div class="skeleton skeleton--text" style="width: 100%; height: 14px; margin-top: 8px;"></div>
<div class="skeleton skeleton--text" style="width: 40%; height: 14px; margin-top: 8px;"></div>
</div>
</div>
```
### Skeleton Best Practices
| Do | Don't |
|----|-------|
| Match the actual content layout | Use generic shapes unrelated to content |
| Use subtle animation (shimmer) | Use pulsing that draws too much attention |
| Show for > 300ms loads | Show for instant loads (causes flicker) |
| Transition smoothly to real content | Pop content in abruptly |
| Keep skeleton visible while data loads | Replace skeleton with spinner |
### Skeleton Transition
```javascript
function showContent(container, data) {
const skeleton = container.querySelector('.skeleton-wrapper');
const content = container.querySelector('.content-wrapper');
// Populate content using safe DOM methods (e.g., textContent,
// createElement, or a framework's reactive rendering)
renderDataToDOM(content, data);
// Cross-fade transition
skeleton.style.opacity = '0';
content.style.opacity = '1';
setTimeout(() => skeleton.remove(), 300);
}
```
```css
.skeleton-wrapper,
.content-wrapper {
transition: opacity 300ms ease;
}
.content-wrapper {
opacity: 0;
}
```
---
## Optimistic Updates
Apply changes to the UI immediately before server confirmation, then reconcile.
### Pattern
```javascript
async function toggleFavorite(itemId) {
// 1. Optimistic: update UI immediately
const previousState = getItem(itemId).isFavorite;
updateItemUI(itemId, { isFavorite: !previousState });
try {
// 2. Send to server
await api.toggleFavorite(itemId);
// Success: UI already correct, nothing to do
} catch (error) {
// 3. Rollback on failure
updateItemUI(itemId, { isFavorite: previousState });
showToast('Failed to update. Please try again.', { type: 'error' });
}
}
```
### When to Use Optimistic Updates
| Use Optimistic | Don't Use |
|---------------|-----------|
| Toggles (like, favorite, bookmark) | Financial transactions |
| Status changes (read/unread) | Destructive actions (delete) |
| Form field auto-save | Multi-step workflows |
| Reordering lists | Actions requiring server validation |
| Adding/removing tags | External system integrations |
### Optimistic Update UX Requirements
- Always have a rollback path
- Show subtle "saving" indicator (not blocking)
- On failure: revert UI + show clear error message
- On conflict: show resolution UI (merge dialog)
- Debounce rapid changes (typing, slider adjustments)
---
## Progress Indicators
### Indeterminate Progress
For operations where duration is unknown.
```css
/* Indeterminate progress bar */
.progress-bar--indeterminate {
height: 4px;
background: #e5e7eb;
border-radius: 2px;
overflow: hidden;
}
.progress-bar--indeterminate::after {
content: '';
display: block;
height: 100%;
width: 40%;
background: #3b82f6;
border-radius: 2px;
animation: indeterminate 1.5s infinite ease-in-out;
}
@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(350%); }
}
```
### Determinate Progress
For operations where progress can be measured.
```html
<div class="progress-container">
<div class="progress-bar" role="progressbar"
aria-valuenow="65" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar__fill" style="width: 65%"></div>
</div>
<span class="progress-label">65% — Uploading 3 of 5 files</span>
</div>
```
### Multi-Step Progress
```
┌──────────────────────────────────────────────────────────────┐
│ Importing data... │
│ │
│ ✓ Validating file format │
│ ✓ Parsing 1,234 records │
│ ⟳ Importing to database (847 of 1,234) │
│ ○ Running post-import checks │
│ │
│ ████████████████████░░░░░░░░░░ 68% [Cancel import] │
└──────────────────────────────────────────────────────────────┘
```
### Button Loading States
```css
.btn--loading {
position: relative;
color: transparent; /* Hide text */
pointer-events: none;
}
.btn--loading::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
top: 50%;
left: 50%;
margin: -8px 0 0 -8px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
border-radius: 50%;
animation: button-spin 0.6s linear infinite;
}
@keyframes button-spin {
to { transform: rotate(360deg); }
}
```
**Button loading best practices:**
- Disable the button to prevent double-submit
- Keep button dimensions constant (no layout shift)
- Show spinner inside the button, not replacing it
- Optionally change label: "Save" → "Saving..."
---
## Image Loading Strategies
### Lazy Loading
Only load images when they enter (or approach) the viewport.
```html
<!-- Native lazy loading -->
<img src="photo.jpg" loading="lazy" alt="Description"
width="800" height="600">
```
### Blur-Up / LQIP (Low Quality Image Placeholder)
Show a tiny, blurred preview that transitions to the full image.
```html
<div class="image-wrapper">
<img class="image-placeholder" src="photo-20px.jpg"
style="filter: blur(20px); transform: scale(1.1);"
aria-hidden="true">
<img class="image-full" src="photo-800px.jpg" loading="lazy"
alt="Description"
onload="this.classList.add('loaded')">
</div>
```
```css
.image-wrapper {
position: relative;
overflow: hidden;
}
.image-placeholder {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.image-full {
opacity: 0;
transition: opacity 300ms ease;
}
.image-full.loaded {
opacity: 1;
}
```
### Responsive Images
```html
<img
srcset="photo-400w.jpg 400w,
photo-800w.jpg 800w,
photo-1200w.jpg 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1000px) 800px,
1200px"
src="photo-800w.jpg"
alt="Description"
loading="lazy"
>
```
### Image Loading Decision Tree
```
What type of image is it?
├── Above the fold (hero, logo, critical UI)?
│ └── → Eager load, preload via <link>, use srcset
├── Below the fold (content images)?
│ └── → loading="lazy", use LQIP/blur-up for large images
├── Thumbnail in a list/grid?
│ └── → loading="lazy", fixed dimensions, skeleton placeholder
├── Background/decorative?
│ └── → CSS background with lazy class, low priority
└── User avatar?
└── → Eager load if visible, fixed dimensions, fallback initial
```
---
## Content Layout Shift Prevention
Layout shifts are disorienting and frustrating. They happen when content dimensions change after initial render.
### Core Web Vitals: CLS
- **Good:** CLS < 0.1
- **Needs improvement:** 0.1 - 0.25
- **Poor:** CLS > 0.25
### Prevention Techniques
#### Reserve Space for Images
```css
/* Always specify dimensions */
img {
width: 100%;
height: auto;
aspect-ratio: 16 / 9; /* Reserve space before load */
}
/* Container with aspect ratio */
.image-container {
position: relative;
width: 100%;
aspect-ratio: 4 / 3;
background: #f3f4f6;
}
```
#### Reserve Space for Fonts
```css
/* Use font-display to control FOIT/FOUT */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* Show fallback immediately, swap when loaded */
}
/* Match fallback font metrics to reduce shift */
body {
font-family: 'CustomFont', system-ui, sans-serif;
font-size-adjust: 0.5; /* Normalize x-height */
}
```
#### Reserve Space for Dynamic Content
```css
/* Fixed-height containers for dynamic content */
.ad-slot {
min-height: 250px;
background: #f3f4f6;
}
.notification-area {
min-height: 0;
transition: min-height 300ms ease; /* Smooth expansion */
}
```
#### Avoid CLS-Causing Patterns
| Pattern | Problem | Solution |
|---------|---------|----------|
| Images without dimensions | Jump when loaded | Always set width/height or aspect-ratio |
| Injected content above viewport | Pushes content down | Insert below or use overlay |
| Web fonts without fallback | Text reflows | Use font-display: swap + size-adjust |
| Dynamic ads/banners | Layout shift on load | Reserve fixed space |
| Late-loading components | Layout reflowing | Use skeleton placeholders |
---
## Offline-First & Cached State UX
### Connection Status
```css
/* Offline indicator */
.offline-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 8px 16px;
background: #fef3c7;
color: #92400e;
text-align: center;
font-size: 14px;
z-index: 9999;
transform: translateY(100%);
transition: transform 300ms ease;
}
.offline-banner--visible {
transform: translateY(0);
}
```
### Stale Content Indicators
```html
<div class="stale-notice">
<span>Last updated 5 minutes ago</span>
<button class="refresh-btn">Refresh</button>
</div>
```
**Stale data guidelines:**
- Show "last updated" timestamp for cached data
- Provide manual refresh option
- Auto-refresh when connection restores
- Differentiate between "cached" and "live" data visually
- Never silently show outdated data as if it were current
### Offline-Capable Actions
| Action Type | Offline Behavior |
|-------------|-----------------|
| Read cached data | Show with "offline" indicator |
| Create new items | Queue locally, sync when online |
| Edit existing items | Queue changes, show "pending sync" |
| Delete items | Mark as "pending delete", hide from view |
| Search | Search local cache only, note limitation |
### Sync Status Indicators
```
┌──────────────────────────────────────────┐
│ ✓ All changes saved │ — Everything synced
│ ⟳ Saving... │ — Sync in progress
│ ⚠ Offline — changes will sync later │ — Queued changes
│ ✕ Save failed — click to retry │ — Error state
└──────────────────────────────────────────┘
```
---
## Prefetching & Preloading
### Link Prefetching
```html
<!-- Prefetch likely next pages -->
<link rel="prefetch" href="/dashboard">
<link rel="prefetch" href="/api/user-data.json">
<!-- Preload critical resources -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/hero.jpg" as="image">
```
### Hover Prefetch
```javascript
// Prefetch on hover (before click)
document.querySelectorAll('a[data-prefetch]').forEach(link => {
let prefetched = false;
link.addEventListener('mouseenter', () => {
if (prefetched) return;
prefetched = true;
const prefetchLink = document.createElement('link');
prefetchLink.rel = 'prefetch';
prefetchLink.href = link.href;
document.head.appendChild(prefetchLink);
});
});
```
### Prefetch Strategy Decision
| Signal | Action |
|--------|--------|
| User hovers over link | Prefetch page |
| User types in search | Prefetch top results |
| User reaches 80% of list | Prefetch next page |
| User is on step 2 of 4 | Prefetch step 3 resources |
| Viewport approaches lazy content | Prefetch images |
---
## Key Metrics
| Metric | Value | Context |
|--------|-------|---------|
| Instant response | < 100ms | No loading indicator needed |
| Skeleton threshold | > 300ms | Show skeleton after this delay |
| Toast error display | >= 5s | Longer than success toasts |
| CLS target | < 0.1 | Core Web Vitals "good" |
| LCP target | < 2.5s | Largest Contentful Paint |
| FID target | < 100ms | First Input Delay |
| INP target | < 200ms | Interaction to Next Paint |
| Optimistic rollback | < 5s | Show error if no confirmation |
| Prefetch hover delay | 65ms | Wait before prefetching |
---
## Anti-Patterns
1. **Spinner for everything** - Use skeletons for content, spinners for actions
2. **Full-page loader** - Block only the region that's loading, not the whole page
3. **No minimum display time** - Skeleton that flashes for 50ms is worse than nothing
4. **Layout shifts on load** - Always reserve space for async content
5. **Silent failures** - User doesn't know their action failed
6. **Optimistic without rollback** - Showing success then silently losing data
7. **Blocking UI during save** - Use background save with status indicator
8. **No offline indication** - Users assume they're connected
9. **Stale data shown as current** - Always indicate data freshness
10. **Retry without feedback** - Show what's happening during retry attempts
---
## Sources
- [web.dev - Core Web Vitals](https://web.dev/vitals/) - Google's performance metrics
- [web.dev - Optimize CLS](https://web.dev/optimize-cls/) - Layout shift prevention
- [Nielsen Norman Group - Response Times](https://www.nngroup.com/articles/response-times-3-important-limits/) - Perception thresholds
- [Smashing Magazine - Skeleton Screens](https://www.smashingmagazine.com/2020/04/skeleton-screens-react/) - Implementation patterns
- [Luke Wroblewski - Mobile Input](https://www.lukew.com/ff/entry.asp?1950) - Perceived performance research
- [Instant.page](https://instant.page/) - Prefetch on hover library
references/24-voice-and-multimodal.md
# Voice & Multimodal Input
Most interfaces still assume one input channel (a pointer or a touchscreen) and one
output channel (a screen). Real usage is **multimodal**: people speak, type, tap,
glance, and switch devices mid-task. This reference treats input modality as a
first-class design concern—voice, touch, pointer, keyboard, and their combinations—
across the range of contexts a product runs in (phone, desktop, watch, TV, car).
> This file is about *input modality in general*. For conversational AI chat
> patterns specifically, see [14-ai-ux-patterns.md](14-ai-ux-patterns.md).
> The two overlap (a voice assistant is often LLM-backed) but the concerns here—
> discoverability, recognition feedback, modality fallback—apply to any voice or
> multimodal UI, AI or not.
---
## Voice as a First-Class Input
Voice fails most often not because recognition is bad, but because the UX around it
is an afterthought: a tiny mic icon, no feedback, no recovery. Treat voice like any
other primary input—discoverable, with clear state, confirmation, and error paths.
### Discoverability
- Don't bury voice behind an unlabeled mic glyph. Surface what it can do—suggested
utterances ("Try: *set a timer for 10 minutes*"), an onboarding hint, or visible
example commands.
- Voice's weakness is that **the command space is invisible**. Unlike a menu, users
can't see their options. Show examples in context and after errors.
- Make the entry point obvious and reachable; size the affordance like a real button,
not a 16px icon.
### Feedback: show every state
Users must always know whether the system is **idle, listening, processing, or
speaking**. Silence is ambiguous—did it hear me? is it broken?
```
States to make visible:
● Idle — mic available, not capturing
◉ Listening — actively capturing (waveform / pulsing animation)
⋯ Processing — recognized, working on it
▸ Responding — speaking / showing the result
✕ Error — didn't catch that, with a way forward
```
- Show **live transcription** of what was heard so the user can catch misrecognition
immediately ("I said *Karen* not *Aaron*").
- Pair audio output with a **visual transcript** of what the system said.
### Confirmation & error recovery
- **Confirm consequential actions** before executing ("Delete all photos from
June—are you sure?"), but don't nag for trivial, reversible ones.
- Always show what was *recognized*, not just what was done, so errors are traceable.
- On failure, **offer a path forward**: re-prompt with an example, fall back to a
visual chooser, or let the user edit the transcribed text—never a dead-end
"Sorry, I didn't understand."
- **Support barge-in:** let users interrupt a long spoken response or prompt and
start their next command without waiting for it to finish.
### A complete voice interaction, end to end
```
┌──────────────────────────────────────────────────────────┐
│ ● Tap to speak (idle — clear, button-sized entry) │
└──────────────────────────────────────────────────────────┘
│ user taps / says wake word
▼
┌──────────────────────────────────────────────────────────┐
│ ◉ Listening… ∿∿∿∿∿∿ (waveform reacts to voice) │
│ "set a timer for ten minutes" ← live transcript │
└──────────────────────────────────────────────────────────┘
│ endpoint detected (or user taps stop)
▼
┌──────────────────────────────────────────────────────────┐
│ ⋯ Working on it… │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ ▸ Timer set for 10:00. [⏸ Pause] [✕ Cancel] │
│ (spoken aloud AND shown as text + a visual control) │
└──────────────────────────────────────────────────────────┘
│ low confidence? → don't guess silently:
▼
┌──────────────────────────────────────────────────────────┐
│ ✕ Did you mean a timer or an alarm? [Timer] [Alarm] │
│ (recover with a visual chooser, not a dead end) │
└──────────────────────────────────────────────────────────┘
```
The result is **multimodal by default**: voice in, but the response is spoken,
shown as text, *and* leaves behind tappable controls so the user can continue by
whichever modality suits the moment.
### Activation: wake words vs. push-to-talk
- **Push-to-talk** (tap/hold to speak) is explicit, privacy-friendly, and reliable
in noisy/public settings—prefer it for on-screen UIs and anything sensitive.
- **Wake words** ("Hey …") enable hands-free use but require always-listening, which
raises privacy stakes—be explicit about on-device detection and show a clear
live-mic indicator. Offer a way to disable the wake word entirely.
- Whichever you use, provide an **unambiguous stop**, and never begin acting on audio
captured before the explicit trigger.
### Recognition feedback in code
```js
// Web Speech API — surface interim results so users see misrecognition live
const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.interimResults = true; // stream partial transcripts → live feedback
rec.lang = navigator.language; // honor the user's locale (see i18n reference)
rec.onstart = () => setState('listening'); // show waveform / pulsing mic
rec.onresult = (e) => {
const t = Array.from(e.results).map(r => r[0].transcript).join('');
showTranscript(t); // user can catch "Aaron" vs "Karen"
};
rec.onerror = (e) => recoverWithVisualFallback(e.error); // never a dead end
rec.onend = () => setState('idle'); // always return to a known state
```
### Privacy is part of voice UX
- Make it unambiguous when the mic is **live** (persistent indicator, not just at start).
- Be explicit about whether audio is processed on-device or sent to a server, and
whether it's retained. Don't capture ambient audio before an explicit trigger.
- Offer a visible, one-tap way to stop listening and to review/delete history.
---
## Voice Copy & Timing
Spoken language is linear and unscannable—a user can't skim a sentence they hear the
way they skim text. Voice copy and timing need their own discipline.
- **Be brief and front-load the answer.** Say the result first, details after
("Done—your timer's set for 10 minutes" not a 20-word preamble). Long spoken
responses are painful; offer "want the details?" rather than dumping them.
- **Write for the ear.** Short sentences, no nested clauses, no jargon, no
"click here" (there's nothing to click). Numbers and dates should be speakable.
- **One question at a time.** Don't ask "what, when, and where?" in one prompt—the
user can only hold so much, and won't remember which to answer first.
- **Tune endpointing (silence timeout).** Cut off too early and you clip the user;
wait too long and it feels broken. Allow for natural pauses and "thinking" silence,
and let users signal they're done (a tap, or a stop word).
- **Set expectations on latency.** If processing will take more than ~1s, fill the
gap with a clear "working on it" state (visual + optional audio cue), not silence.
```
❌ "I have located three results matching your query. The first result is…"
✅ "Found three. Top one: … Want the others?"
```
---
## Multimodal Interaction
Multimodal means combining input channels so each does what it's best at—and letting
users move fluidly between them. The classic example: "put **that** [point] **there**
[point]" combines voice with pointing; neither alone is as fast.
### Combine voice + touch + pointer + keyboard
- Let modalities **complement**, not compete: voice for intent ("make this blue"),
pointing/selection for the target.
- Don't force a single modality for a whole task. A user dictating a message should
still be able to tap to fix one word instead of re-recording the sentence.
- Keep state shared across modalities—what you select by touch is the referent for
the next voice command.
### Modality-appropriate design per context
The right primary modality depends on the device and the user's situation. Design
the *same capability* to suit each.
| Context | Constraints | Lean on |
|---------|-------------|---------|
| **Phone (on the go)** | One hand, glancing, noisy/quiet | Touch primary; voice for hands-busy |
| **Desktop** | Keyboard + precise pointer, focused | Keyboard shortcuts, pointer; voice optional |
| **Watch** | Tiny screen, brief glances | Voice + a few taps; minimal text entry |
| **TV / 10-foot UI** | Distance, D-pad/remote, no keyboard | Voice search >> on-screen keyboard; large focus targets |
| **Car** | Eyes/hands occupied, safety-critical | Voice primary; minimal-glance visuals, large touch zones |
| **Kiosk / public** | Shared, may be loud, privacy concerns | Touch primary; voice often inappropriate |
- **"Eyes-free / hands-free" contexts** (driving, cooking) make voice essential and
visual-only UIs unusable—but a noisy or public context can make voice useless.
Design for the situation, not the gadget.
### Graceful degradation when a modality is unavailable
- A mic may be denied, broken, muted, or socially inappropriate; a screen may be off;
a keyboard may be absent. **Every core task must be completable in more than one way.**
- If voice is unavailable, the same action must be reachable by touch/pointer/keyboard,
and vice versa. Detect capability and adapt the offered affordances; don't show a
dead voice button when there's no mic permission.
---
## Cross-Device Continuity
Users start a task on one device and finish on another. Continuity is a multimodal
problem because the *input* changes across devices, not just the layout.
- **Handoff:** let a task move between devices with state intact (a started form, a
half-written message, a playback position). Surface "continue on this device".
- **Responsive *input*, not just responsive *layout*:** a layout that reflows but
still assumes hover, precise clicks, or a physical keyboard fails on touch/TV/voice.
Adapt interactions (tap targets, focus order, voice entry) to the device's modality.
- **Context preservation:** carry selection, scroll position, undo history, and auth
across the handoff so the user resumes rather than restarts.
- Keep identity and sync trustworthy—show what synced and when (see
[12b-conflict-resolution-sync.md](12b-conflict-resolution-sync.md)).
---
## Accessibility Intersection
Voice and multimodal design overlap heavily with accessibility (see
[03-accessibility.md](03-accessibility.md)). They help some users and can
exclude others—design for both directions.
- **Voice as an assistive input:** for users with motor or dexterity impairments,
voice can be the *primary* way to operate an interface. Voice control
(Voice Control, Voice Access) needs every actionable element to be reliably named
and reachable—the same semantics screen readers need.
- **Captions and visual alternatives for audio output:** anything the system *says*
must also be available as text for Deaf and hard-of-hearing users, and for anyone
in a sound-off context.
- **Never make voice the only path.** Speech-impaired users, people with strong
accents, non-native speakers, and those in quiet/public/noisy settings need a
non-voice route to the same outcome. Voice is an *addition*, not a replacement.
- Visual recognition feedback (live transcript) doubles as an accessibility feature.
---
## Spatial & AR (Emerging — Low Priority)
Spatial computing (headsets, AR overlays) adds gaze, hand-tracking, and 3D gesture
as input modalities. It's worth acknowledging but, for most products in 2026, **not
worth investing in yet**—the audience is small and patterns are unsettled. Keep two
durable principles in mind in case it becomes relevant:
- **Gaze ≠ intent.** Looking at something isn't selecting it; require a deliberate
confirm (pinch, dwell-with-feedback, voice) to avoid the "Midas touch" problem.
- **Comfort and fatigue are UX constraints.** "Gorilla arm" from mid-air gestures and
motion discomfort are real; favor small, low-effort gestures and respect reduced-motion.
Treat AR/spatial as a place to watch, not a default investment.
---
## Quick Checklist
- [ ] Voice entry is discoverable, with example commands (not just a bare mic icon)
- [ ] Idle / listening / processing / responding / error states are all visible
- [ ] Live transcription shows what was recognized
- [ ] Consequential voice actions are confirmed; trivial ones are not
- [ ] Errors offer a path forward (re-prompt, edit, or fall back to visual)
- [ ] Barge-in supported; mic-live state and privacy are clear
- [ ] Every core task is completable in ≥ 2 modalities (graceful degradation)
- [ ] Interactions—not just layout—adapt to device modality
- [ ] Spoken output has a text/caption equivalent
- [ ] Voice is never the only path to any outcome
---
## Common Mistakes
1. **Voice-only flows** — no fallback excludes speech-impaired, accented, non-native,
and silent-context users.
2. **Hidden mic / no discoverability** — users can't find voice or guess its commands.
3. **No recognition feedback** — no live transcript, so misrecognition goes unnoticed.
4. **Ambiguous state** — user can't tell if the system is listening, thinking, or dead.
5. **Dead-end errors** — "Sorry, I didn't understand" with no way forward.
6. **No confirmation for destructive voice commands** — easy to delete by mishearing.
7. **Ignoring privacy** — unclear mic-live state, ambient capture, no history controls.
8. **Responsive layout, non-responsive input** — reflows but still assumes hover/keyboard.
9. **Audio-only output** — no captions/transcript for spoken responses.
10. **Forcing one modality per task** — can't tap to fix a single dictated word.
---
## Sources
- [Nielsen Norman Group: Voice Interfaces & Usability](https://www.nngroup.com/topic/voice-interaction/)
- [Google: Conversation Design Guidelines](https://developers.google.com/assistant/conversation-design/welcome)
- [Apple HIG: Siri & Voice](https://developer.apple.com/design/human-interface-guidelines/siri)
- [Apple HIG: Multimodal & Inputs](https://developer.apple.com/design/human-interface-guidelines/inputs)
- [W3C: Voice & Multimodal Interaction](https://www.w3.org/standards/) — standards activity
- [MDN: Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API)
- [Microsoft: Multimodal interaction guidance](https://learn.microsoft.com/en-us/windows/apps/design/input/)
- [Bolt, R. (1980). "Put-That-There"](https://dl.acm.org/doi/10.1145/800250.807503) — foundational multimodal research