Initial commit — Singular Particular Space v1
Homepage (site/index.html): integration-v14 promoted, Writings section integrated with 33 pieces clustered by type (stories/essays/miscellany), Writings welcome lightbox, content frame at 98% opacity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,507 @@
|
||||
# Accessibility Deep Dive
|
||||
|
||||
Comprehensive WCAG 2.1 AA compliance checklist with practical implementation guidance.
|
||||
|
||||
## Accessibility Philosophy
|
||||
|
||||
Accessibility isn't just compliance—it improves UX for everyone:
|
||||
- Keyboard navigation helps power users
|
||||
- Good contrast helps in bright sunlight
|
||||
- Clear focus states help everyone understand what's selected
|
||||
- Proper headings help screen readers AND SEO
|
||||
|
||||
---
|
||||
|
||||
## WCAG 2.1 AA Checklist
|
||||
|
||||
### Perceivable
|
||||
|
||||
Users must be able to perceive content.
|
||||
|
||||
#### 1.1 Text Alternatives
|
||||
|
||||
**All images need alt text:**
|
||||
|
||||
```html
|
||||
<!-- Decorative image (no alt) -->
|
||||
<img src="divider.svg" alt="" role="presentation">
|
||||
|
||||
<!-- Informative image -->
|
||||
<img src="chart.png" alt="Sales increased 40% from Q1 to Q2">
|
||||
|
||||
<!-- Functional image (button/link) -->
|
||||
<button>
|
||||
<img src="search.svg" alt="Search">
|
||||
</button>
|
||||
|
||||
<!-- Complex image (needs long description) -->
|
||||
<figure>
|
||||
<img src="diagram.png" alt="System architecture overview" aria-describedby="diagram-desc">
|
||||
<figcaption id="diagram-desc">Detailed description of the system architecture...</figcaption>
|
||||
</figure>
|
||||
```
|
||||
|
||||
**Icons:**
|
||||
```html
|
||||
<!-- Icon with visible text (icon is decorative) -->
|
||||
<button>
|
||||
<svg aria-hidden="true">...</svg>
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
|
||||
<!-- Icon-only button -->
|
||||
<button aria-label="Settings">
|
||||
<svg aria-hidden="true">...</svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
#### 1.3 Adaptable
|
||||
|
||||
**Semantic HTML structure:**
|
||||
|
||||
```html
|
||||
<!-- Use semantic elements -->
|
||||
<header>...</header>
|
||||
<nav>...</nav>
|
||||
<main>
|
||||
<article>
|
||||
<h1>Page Title</h1>
|
||||
<section>
|
||||
<h2>Section Title</h2>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
<footer>...</footer>
|
||||
|
||||
<!-- Not this -->
|
||||
<div class="header">...</div>
|
||||
<div class="nav">...</div>
|
||||
```
|
||||
|
||||
**Heading hierarchy:**
|
||||
- One `<h1>` per page
|
||||
- Don't skip levels (h1 → h3)
|
||||
- Headings describe content structure
|
||||
|
||||
**Form labels:**
|
||||
```html
|
||||
<!-- Explicit label -->
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email">
|
||||
|
||||
<!-- Implicit label -->
|
||||
<label>
|
||||
Email
|
||||
<input type="email">
|
||||
</label>
|
||||
|
||||
<!-- Hidden label (for visual designs without labels) -->
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
<input id="search" type="search" placeholder="Search...">
|
||||
```
|
||||
|
||||
#### 1.4 Distinguishable
|
||||
|
||||
**Color contrast requirements:**
|
||||
|
||||
| Content Type | Minimum Ratio | Tool |
|
||||
|--------------|---------------|------|
|
||||
| Normal text (<18px) | 4.5:1 | WebAIM Contrast Checker |
|
||||
| Large text (≥18px or ≥14px bold) | 3:1 | |
|
||||
| UI components & graphics | 3:1 | |
|
||||
|
||||
**Common contrast fixes:**
|
||||
|
||||
```css
|
||||
/* Too light - fails */
|
||||
.text-light { color: #9ca3af; } /* gray-400: 3.1:1 on white */
|
||||
|
||||
/* Passes AA */
|
||||
.text-muted { color: #6b7280; } /* gray-500: 4.6:1 on white */
|
||||
|
||||
/* Passes AAA */
|
||||
.text-strong { color: #374151; } /* gray-700: 9.1:1 on white */
|
||||
```
|
||||
|
||||
**Don't rely on color alone:**
|
||||
|
||||
```html
|
||||
<!-- Bad: only color indicates error -->
|
||||
<input class="border-red-500">
|
||||
|
||||
<!-- Good: color + icon + text -->
|
||||
<input class="border-red-500" aria-invalid="true" aria-describedby="error">
|
||||
<p id="error" class="text-red-600">
|
||||
<svg aria-hidden="true">⚠️</svg>
|
||||
Email is required
|
||||
</p>
|
||||
```
|
||||
|
||||
**Text resize:**
|
||||
- Content must be readable at 200% zoom
|
||||
- Use relative units (rem, em) not px for text
|
||||
- Test by zooming browser to 200%
|
||||
|
||||
---
|
||||
|
||||
### Operable
|
||||
|
||||
Users must be able to operate the interface.
|
||||
|
||||
#### 2.1 Keyboard Accessible
|
||||
|
||||
**All functionality must work with keyboard:**
|
||||
|
||||
| Key | Expected Behavior |
|
||||
|-----|-------------------|
|
||||
| Tab | Move to next focusable element |
|
||||
| Shift+Tab | Move to previous focusable element |
|
||||
| Enter | Activate links, buttons |
|
||||
| Space | Activate buttons, toggle checkboxes |
|
||||
| Arrows | Navigate within components (tabs, menus, radios) |
|
||||
| Escape | Close modals, dropdowns, cancel actions |
|
||||
|
||||
**Focus must be visible:**
|
||||
|
||||
```css
|
||||
/* Don't remove focus outlines */
|
||||
:focus {
|
||||
outline: none; /* ❌ Never do this without replacement */
|
||||
}
|
||||
|
||||
/* Do provide visible focus */
|
||||
:focus-visible {
|
||||
outline: 2px solid #3b82f6;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Or use ring utility */
|
||||
.focusable:focus-visible {
|
||||
@apply ring-2 ring-blue-500 ring-offset-2;
|
||||
}
|
||||
```
|
||||
|
||||
**Keyboard traps:**
|
||||
- Modal dialogs should trap focus inside
|
||||
- But must have a way to exit (Escape key, close button)
|
||||
|
||||
```jsx
|
||||
// Focus trap for modals
|
||||
function Modal({ isOpen, onClose, children }) {
|
||||
const modalRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Focus first focusable element
|
||||
const firstFocusable = modalRef.current.querySelector('button, input, a');
|
||||
firstFocusable?.focus();
|
||||
|
||||
// Trap focus inside
|
||||
const handleTab = (e) => {
|
||||
if (e.key === 'Tab') {
|
||||
// ... trap logic
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleTab);
|
||||
return () => document.removeEventListener('keydown', handleTab);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
// Return focus on close
|
||||
}
|
||||
```
|
||||
|
||||
**Skip links:**
|
||||
|
||||
```html
|
||||
<body>
|
||||
<a href="#main-content" class="sr-only focus:not-sr-only">
|
||||
Skip to main content
|
||||
</a>
|
||||
<nav>...</nav>
|
||||
<main id="main-content">...</main>
|
||||
</body>
|
||||
```
|
||||
|
||||
#### 2.4 Navigable
|
||||
|
||||
**Page titles:**
|
||||
- Unique, descriptive page titles
|
||||
- Format: `Page Name | Site Name`
|
||||
|
||||
**Focus order:**
|
||||
- Must follow logical reading order
|
||||
- Don't use positive `tabindex` values (messes up order)
|
||||
- Only use `tabindex="0"` (make focusable) or `tabindex="-1"` (programmatically focusable)
|
||||
|
||||
**Link purpose:**
|
||||
```html
|
||||
<!-- Bad -->
|
||||
<a href="/article">Click here</a>
|
||||
<a href="/article">Read more</a>
|
||||
|
||||
<!-- Good -->
|
||||
<a href="/article">Read more about accessibility best practices</a>
|
||||
|
||||
<!-- Or with context -->
|
||||
<a href="/article" aria-describedby="article-title">Read more</a>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Understandable
|
||||
|
||||
Users must be able to understand content and operation.
|
||||
|
||||
#### 3.1 Readable
|
||||
|
||||
**Language declaration:**
|
||||
```html
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>This is English.</p>
|
||||
<p lang="fr">Ceci est français.</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### 3.2 Predictable
|
||||
|
||||
**Consistent navigation:**
|
||||
- Same navigation in same location across pages
|
||||
- Same elements behave the same way
|
||||
|
||||
**No unexpected changes:**
|
||||
- Form inputs don't auto-submit on change
|
||||
- No unexpected pop-ups
|
||||
- Focus doesn't move unexpectedly
|
||||
|
||||
```html
|
||||
<!-- Bad: changes page on select -->
|
||||
<select onchange="window.location = this.value">...</select>
|
||||
|
||||
<!-- Good: requires explicit action -->
|
||||
<select id="region">...</select>
|
||||
<button onclick="navigate()">Go</button>
|
||||
```
|
||||
|
||||
#### 3.3 Input Assistance
|
||||
|
||||
**Error identification:**
|
||||
```html
|
||||
<label for="email">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
aria-invalid="true"
|
||||
aria-describedby="email-error"
|
||||
>
|
||||
<p id="email-error" class="error">
|
||||
Please enter a valid email address (e.g., name@example.com)
|
||||
</p>
|
||||
```
|
||||
|
||||
**Required fields:**
|
||||
```html
|
||||
<label for="name">
|
||||
Name <span aria-hidden="true">*</span>
|
||||
<span class="sr-only">(required)</span>
|
||||
</label>
|
||||
<input id="name" required aria-required="true">
|
||||
```
|
||||
|
||||
**Error prevention for critical actions:**
|
||||
- Confirm destructive actions
|
||||
- Allow review before submission
|
||||
- Provide undo capability
|
||||
|
||||
---
|
||||
|
||||
### Robust
|
||||
|
||||
Content must work with current and future technologies.
|
||||
|
||||
#### 4.1 Compatible
|
||||
|
||||
**Valid HTML:**
|
||||
- Unique IDs
|
||||
- Complete start/end tags
|
||||
- Proper nesting
|
||||
|
||||
**ARIA usage:**
|
||||
```html
|
||||
<!-- If you use ARIA, use it correctly -->
|
||||
|
||||
<!-- Roles -->
|
||||
<div role="button" tabindex="0" onclick="...">Fake Button</div>
|
||||
<!-- Better: just use <button> -->
|
||||
|
||||
<!-- States -->
|
||||
<button aria-pressed="true">Bold</button>
|
||||
<button aria-expanded="false" aria-controls="menu">Menu</button>
|
||||
|
||||
<!-- Live regions -->
|
||||
<div aria-live="polite" aria-atomic="true">
|
||||
<!-- Screen reader announces changes here -->
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Focus Management
|
||||
|
||||
### When to Manage Focus
|
||||
|
||||
| Scenario | Focus Action |
|
||||
|----------|--------------|
|
||||
| Modal opens | Focus first element inside modal |
|
||||
| Modal closes | Return focus to trigger element |
|
||||
| Error occurs | Focus error message or first invalid field |
|
||||
| New content loads | Focus heading or first new element |
|
||||
| Item deleted | Focus previous/next item or container |
|
||||
|
||||
### Implementation
|
||||
|
||||
```jsx
|
||||
// Store trigger reference
|
||||
const triggerRef = useRef();
|
||||
|
||||
function openModal() {
|
||||
triggerRef.current = document.activeElement;
|
||||
setIsOpen(true);
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
setIsOpen(false);
|
||||
// Return focus after state update
|
||||
setTimeout(() => triggerRef.current?.focus(), 0);
|
||||
}
|
||||
```
|
||||
|
||||
### Roving Tabindex (for component groups)
|
||||
|
||||
```jsx
|
||||
// Tab panels, menu items, radio groups
|
||||
function Tabs({ tabs }) {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'ArrowRight') {
|
||||
setActiveIndex((activeIndex + 1) % tabs.length);
|
||||
}
|
||||
if (e.key === 'ArrowLeft') {
|
||||
setActiveIndex((activeIndex - 1 + tabs.length) % tabs.length);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div role="tablist" onKeyDown={handleKeyDown}>
|
||||
{tabs.map((tab, i) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
role="tab"
|
||||
tabIndex={i === activeIndex ? 0 : -1}
|
||||
aria-selected={i === activeIndex}
|
||||
ref={i === activeIndex ? (el) => el?.focus() : null}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Screen Reader Considerations
|
||||
|
||||
### Announce Dynamic Changes
|
||||
|
||||
```html
|
||||
<!-- Live region for status messages -->
|
||||
<div aria-live="polite" class="sr-only" id="status"></div>
|
||||
|
||||
<script>
|
||||
function showSuccess(message) {
|
||||
document.getElementById('status').textContent = message;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Hide Decorative Content
|
||||
|
||||
```html
|
||||
<!-- Hidden from screen readers -->
|
||||
<svg aria-hidden="true">...</svg>
|
||||
<span aria-hidden="true">•</span>
|
||||
```
|
||||
|
||||
### Provide Context
|
||||
|
||||
```html
|
||||
<!-- Ambiguous button -->
|
||||
<button>Delete</button>
|
||||
|
||||
<!-- Clear button -->
|
||||
<button aria-label="Delete comment by John">Delete</button>
|
||||
|
||||
<!-- Or use aria-describedby -->
|
||||
<button aria-describedby="comment-123-author">Delete</button>
|
||||
<span id="comment-123-author" class="sr-only">comment by John</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Tools
|
||||
|
||||
### Automated Testing
|
||||
|
||||
| Tool | What It Catches |
|
||||
|------|-----------------|
|
||||
| axe DevTools | ~30% of WCAG issues |
|
||||
| WAVE | Similar to axe, visual overlay |
|
||||
| Lighthouse | Basic accessibility audit |
|
||||
| ESLint a11y plugin | Catches issues in JSX |
|
||||
|
||||
### Manual Testing Required
|
||||
|
||||
Automated tools miss ~70% of issues. Manual testing needed for:
|
||||
- Keyboard navigation flow
|
||||
- Screen reader experience
|
||||
- Focus management
|
||||
- Meaningful alt text
|
||||
- Logical heading structure
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
- [ ] Navigate entire page with keyboard only
|
||||
- [ ] Test with screen reader (VoiceOver, NVDA)
|
||||
- [ ] Check color contrast with tool
|
||||
- [ ] Zoom to 200% and verify usability
|
||||
- [ ] Test with high contrast mode
|
||||
- [ ] Verify focus indicators visible
|
||||
- [ ] Check heading structure with outline tool
|
||||
- [ ] Run axe DevTools audit
|
||||
- [ ] Test forms with validation errors
|
||||
|
||||
---
|
||||
|
||||
## Common Fixes Quick Reference
|
||||
|
||||
| Issue | Fix |
|
||||
|-------|-----|
|
||||
| Missing alt text | Add descriptive alt or `alt=""` for decorative |
|
||||
| Low contrast | Use gray-600+ for text on white |
|
||||
| Missing focus style | Add `focus-visible` ring/outline |
|
||||
| Click-only interaction | Add keyboard handler + focusability |
|
||||
| Missing form labels | Add `<label>` with `for` attribute |
|
||||
| Heading skip | Use h1→h2→h3 in order |
|
||||
| Color-only indicator | Add icon/text alongside color |
|
||||
| Modal focus trap | Trap focus, allow Escape to close |
|
||||
| Auto-playing media | Add pause control, don't autoplay |
|
||||
| Motion | Respect `prefers-reduced-motion` |
|
||||
@@ -0,0 +1,472 @@
|
||||
# Advanced Design Patterns
|
||||
|
||||
Extended reference for complex scenarios. Load only when deeper guidance needed.
|
||||
|
||||
## Table of Contents
|
||||
1. [Empty States](#empty-states)
|
||||
2. [Form Design](#form-design)
|
||||
3. [Image Treatment](#image-treatment)
|
||||
4. [Icon Usage](#icon-usage)
|
||||
5. [Interaction States](#interaction-states)
|
||||
6. [Color Psychology](#color-psychology)
|
||||
7. [Border Radius System](#border-radius-system)
|
||||
|
||||
---
|
||||
|
||||
## Empty States
|
||||
|
||||
Empty states are opportunities, not afterthoughts.
|
||||
|
||||
**Good empty states include:**
|
||||
- Illustration or icon (not generic)
|
||||
- Clear explanation of what goes here
|
||||
- Primary action to remedy emptiness
|
||||
- Optional secondary actions
|
||||
|
||||
```
|
||||
❌ "No items"
|
||||
✅ "No projects yet. Create your first project to get started."
|
||||
[+ Create Project]
|
||||
```
|
||||
|
||||
**Match the tone.** A todo app empty state can be playful; an enterprise dashboard should be professional.
|
||||
|
||||
---
|
||||
|
||||
## Form Design
|
||||
|
||||
### Input Sizing
|
||||
|
||||
Match input width to expected content:
|
||||
- Email/URL: Full width or ~400px
|
||||
- Phone: ~200px
|
||||
- ZIP code: ~100px
|
||||
- Street address: Full width
|
||||
- City: ~200px
|
||||
- State dropdown: ~150px
|
||||
|
||||
### Placeholder vs. Label
|
||||
|
||||
**Never use placeholder as the only label.** Placeholders disappear on focus. Always have a visible label.
|
||||
|
||||
Placeholders work for:
|
||||
- Format hints: "MM/DD/YYYY"
|
||||
- Examples: "e.g., john@example.com"
|
||||
- Optional clarification
|
||||
|
||||
### Input States
|
||||
|
||||
```
|
||||
Default: border-gray-300
|
||||
Focus: border-blue-500 ring-2 ring-blue-200
|
||||
Error: border-red-500 ring-2 ring-red-200
|
||||
Disabled: bg-gray-100 text-gray-400
|
||||
Success: border-green-500 (sparingly)
|
||||
```
|
||||
|
||||
### Button Hierarchy
|
||||
|
||||
One primary action per view. Everything else is secondary or tertiary.
|
||||
|
||||
```
|
||||
Primary: Solid color, high contrast (bg-blue-600 text-white)
|
||||
Secondary: Outlined or muted (border border-gray-300)
|
||||
Tertiary: Text only (text-blue-600 hover:underline)
|
||||
Danger: Red but not screaming (bg-red-600 for confirm, text-red-600 for trigger)
|
||||
```
|
||||
|
||||
### Form Layout
|
||||
|
||||
- One column for simple forms
|
||||
- Two columns ONLY when inputs are related (First/Last name, City/State)
|
||||
- Labels above inputs on mobile, beside on desktop (optional)
|
||||
- Group related fields with subtle boundaries or spacing
|
||||
|
||||
---
|
||||
|
||||
## Image Treatment
|
||||
|
||||
### Background Images
|
||||
|
||||
**Problem:** Text over images is often unreadable.
|
||||
|
||||
**Solutions:**
|
||||
1. Semi-transparent overlay: `bg-black/50`
|
||||
2. Gradient overlay: `bg-gradient-to-t from-black/80 to-transparent`
|
||||
3. Text shadow: `text-shadow: 0 2px 4px rgba(0,0,0,0.5)`
|
||||
4. Solid color box behind text
|
||||
5. Choose images with natural dark/simple areas for text
|
||||
|
||||
### User Avatars
|
||||
|
||||
- Always have a fallback (initials, generic icon)
|
||||
- Consistent size per context (32px list, 48px card, 96px profile)
|
||||
- Round for people, square with border-radius for companies/products
|
||||
- Border adds polish: `ring-2 ring-white` for overlapping avatars
|
||||
|
||||
### Hero Images
|
||||
|
||||
- Don't stretch—use `object-cover`
|
||||
- Consider `aspect-ratio` for consistency
|
||||
- Compress appropriately (WebP, quality 80%)
|
||||
|
||||
---
|
||||
|
||||
## Icon Usage
|
||||
|
||||
### Sizing
|
||||
|
||||
Icons should feel balanced with adjacent text:
|
||||
- 12-14px text: 16px icon
|
||||
- 16px text: 20px icon
|
||||
- 18-20px text: 24px icon
|
||||
|
||||
### Icon + Text Pairing
|
||||
|
||||
Always align icon center with text baseline or center. Add consistent gap (8px typical).
|
||||
|
||||
```html
|
||||
<span class="flex items-center gap-2">
|
||||
<IconSettings class="w-5 h-5" />
|
||||
<span>Settings</span>
|
||||
</span>
|
||||
```
|
||||
|
||||
### When to Use Icons
|
||||
|
||||
- Navigation items
|
||||
- Common actions (edit, delete, share)
|
||||
- Status indicators
|
||||
- Feature lists (with caution—don't overdo)
|
||||
|
||||
### When NOT to Use Icons
|
||||
|
||||
- Don't add icons just to fill space
|
||||
- Skip icons on buttons with clear text ("Submit", "Continue")
|
||||
- Avoid decorative-only icons that add no meaning
|
||||
|
||||
---
|
||||
|
||||
## Interaction States
|
||||
|
||||
Every interactive element needs visible state changes:
|
||||
|
||||
### Hover
|
||||
- Subtle background change
|
||||
- Slight shadow increase
|
||||
- Color shift (darken primary by 10%)
|
||||
|
||||
### Active/Pressed
|
||||
- Darker than hover
|
||||
- Slight scale down (`scale-95`)
|
||||
- Reduce shadow
|
||||
|
||||
### Focus
|
||||
- Obvious ring (critical for accessibility)
|
||||
- Don't rely on color alone
|
||||
- `focus-visible` for keyboard-only focus
|
||||
|
||||
### Loading
|
||||
- Disable interaction
|
||||
- Show spinner or skeleton
|
||||
- Maintain layout size (prevent shift)
|
||||
|
||||
```css
|
||||
/* Example button states */
|
||||
.btn {
|
||||
@apply bg-blue-600 hover:bg-blue-700 active:bg-blue-800;
|
||||
@apply focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2;
|
||||
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Color Psychology
|
||||
|
||||
Use color purposefully:
|
||||
|
||||
| Color | Association | Use for |
|
||||
|-------|-------------|---------|
|
||||
| Blue | Trust, calm, professional | Primary actions, links, corporate |
|
||||
| Green | Success, growth, go | Success states, positive actions |
|
||||
| Red | Error, danger, urgency | Errors, destructive actions, alerts |
|
||||
| Yellow/Orange | Warning, attention | Warnings, highlights |
|
||||
| Purple | Premium, creative | Premium features, creative apps |
|
||||
| Gray | Neutral, professional | Text, backgrounds, borders |
|
||||
|
||||
### Avoid
|
||||
|
||||
- Red for non-destructive primary buttons
|
||||
- Green for errors (colorblind users)
|
||||
- Low-saturation colors for important actions
|
||||
- More than 3 accent colors per interface
|
||||
|
||||
---
|
||||
|
||||
## Border Radius System
|
||||
|
||||
Stay consistent. Pick a system:
|
||||
|
||||
**Sharp/Modern:**
|
||||
```
|
||||
none: 0
|
||||
sm: 2px
|
||||
md: 4px
|
||||
lg: 6px
|
||||
full: 9999px (pills/circles)
|
||||
```
|
||||
|
||||
**Soft/Friendly:**
|
||||
```
|
||||
none: 0
|
||||
sm: 4px
|
||||
md: 8px
|
||||
lg: 12px
|
||||
xl: 16px
|
||||
full: 9999px
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
- Nested elements: inner radius = outer radius - padding
|
||||
- Small elements get smaller radius (badges, tags)
|
||||
- Large elements can have larger radius (cards, modals)
|
||||
- Images inside cards: match card radius or use `overflow-hidden`
|
||||
|
||||
---
|
||||
|
||||
## Text Wrapping & Truncation
|
||||
|
||||
### When to Truncate
|
||||
|
||||
- Navigation items
|
||||
- Table cells with fixed widths
|
||||
- Card titles (with hover to reveal)
|
||||
|
||||
```css
|
||||
.truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
```
|
||||
|
||||
### When NOT to Truncate
|
||||
|
||||
- Body text
|
||||
- Important information
|
||||
- Search results
|
||||
- Error messages
|
||||
|
||||
### Multi-line Truncation
|
||||
|
||||
```css
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Responsive Breakpoints
|
||||
|
||||
Standard breakpoints (Tailwind default):
|
||||
|
||||
```
|
||||
sm: 640px (landscape phones)
|
||||
md: 768px (tablets)
|
||||
lg: 1024px (laptops)
|
||||
xl: 1280px (desktops)
|
||||
2xl: 1536px (large screens)
|
||||
```
|
||||
|
||||
### Mobile-First Principles
|
||||
|
||||
1. Design for mobile first, add complexity for larger screens
|
||||
2. Stack on mobile, side-by-side on desktop
|
||||
3. Full-width inputs on mobile, constrained on desktop
|
||||
4. Larger touch targets on mobile (44px minimum)
|
||||
5. Hide secondary navigation in hamburger on mobile
|
||||
|
||||
---
|
||||
|
||||
## Modal and Overlay Patterns
|
||||
|
||||
### Modal Sizing
|
||||
|
||||
| Content Type | Width | Height |
|
||||
|--------------|-------|--------|
|
||||
| Confirmation dialog | 400-500px | Auto (minimal) |
|
||||
| Form modal | 500-600px | Auto |
|
||||
| Content modal | 600-800px | 70-80vh max |
|
||||
| Full-screen modal | 100vw | 100vh |
|
||||
|
||||
### Modal Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Title ✕ Close │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Modal content here │
|
||||
│ │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Cancel Primary Action│
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Backdrop
|
||||
|
||||
```css
|
||||
.backdrop {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
/* Or with blur */
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
```
|
||||
|
||||
### Modal Transitions
|
||||
|
||||
```css
|
||||
/* Fade + Scale */
|
||||
.modal {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 200ms ease-out, transform 200ms ease-out;
|
||||
}
|
||||
.modal.open {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dropdown and Menu Design
|
||||
|
||||
### Dropdown Positioning
|
||||
|
||||
- Default: Below trigger, left-aligned
|
||||
- Flip: Above if no space below
|
||||
- Constrain to viewport
|
||||
|
||||
```css
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
```
|
||||
|
||||
### Menu Styling
|
||||
|
||||
```css
|
||||
.menu {
|
||||
min-width: 180px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.menu-divider {
|
||||
height: 1px;
|
||||
background: #e5e7eb;
|
||||
margin: 4px 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Menu Item Types
|
||||
|
||||
| Type | Visual |
|
||||
|------|--------|
|
||||
| Standard | Text label |
|
||||
| With icon | Icon + Label |
|
||||
| With shortcut | Label + Shortcut |
|
||||
| With description | Label + Description |
|
||||
| Destructive | Red text |
|
||||
| Disabled | Grayed out |
|
||||
|
||||
---
|
||||
|
||||
## Navigation Patterns
|
||||
|
||||
### Top Navigation
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Logo Nav Item Nav Item Nav Item CTA │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Sticky on scroll (optional)
|
||||
- Collapse to hamburger on mobile
|
||||
- Clear active state
|
||||
|
||||
### Side Navigation
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────────────────────────┐
|
||||
│ Logo │ │
|
||||
│ │ │
|
||||
│ Dashboard │ Main Content │
|
||||
│ Projects │ │
|
||||
│ Settings │ │
|
||||
│ │ │
|
||||
│ ───────── │ │
|
||||
│ Account │ │
|
||||
│ Logout │ │
|
||||
└──────────────┴──────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Collapsible to icons on desktop
|
||||
- Full overlay on mobile
|
||||
- Group related items
|
||||
|
||||
### Breadcrumbs
|
||||
|
||||
```
|
||||
Home > Category > Subcategory > Current Page
|
||||
```
|
||||
|
||||
- Clickable except current page
|
||||
- Truncate middle items if too long
|
||||
- Show on detail/nested pages
|
||||
|
||||
### Tabs
|
||||
|
||||
```css
|
||||
.tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
border-bottom-color: #3b82f6;
|
||||
color: #3b82f6;
|
||||
}
|
||||
```
|
||||
|
||||
- Underline or pill style
|
||||
- Horizontal scroll on mobile if many tabs
|
||||
- Consider vertical tabs for settings
|
||||
@@ -0,0 +1,428 @@
|
||||
# Animation & Microinteractions
|
||||
|
||||
Guidelines for when and how to animate UI elements effectively.
|
||||
|
||||
## The Purpose of Animation
|
||||
|
||||
Animation should serve a function, not just look nice.
|
||||
|
||||
### Valid Reasons to Animate
|
||||
|
||||
| Purpose | Example |
|
||||
|---------|---------|
|
||||
| **Feedback** | Button press confirmation, form submission success |
|
||||
| **Orientation** | Showing where something came from or went to |
|
||||
| **Focus** | Drawing attention to important changes |
|
||||
| **Teaching** | Demonstrating how something works |
|
||||
| **Continuity** | Maintaining context during state changes |
|
||||
| **Delight** | Occasional surprise (use sparingly) |
|
||||
|
||||
### Invalid Reasons to Animate
|
||||
|
||||
- "It looks cool"
|
||||
- "The competition does it"
|
||||
- "To show off our skills"
|
||||
- Every state change
|
||||
- To hide slow performance
|
||||
|
||||
---
|
||||
|
||||
## Timing & Duration
|
||||
|
||||
### Duration Guidelines
|
||||
|
||||
| Animation Type | Duration | Rationale |
|
||||
|----------------|----------|-----------|
|
||||
| Micro-feedback (hover, press) | 100-150ms | Must feel instant |
|
||||
| Simple transitions (fade, slide) | 150-250ms | Noticeable but quick |
|
||||
| Complex transitions (modal, navigation) | 250-350ms | Need time to follow |
|
||||
| Entrances/Reveals | 200-400ms | Can be slightly longer |
|
||||
| Decorative/Emphasis | 300-500ms | Purpose is to be noticed |
|
||||
|
||||
### The 200ms Rule
|
||||
|
||||
Most UI animations should be around 200ms:
|
||||
- Faster than 100ms → Too fast to perceive
|
||||
- Slower than 400ms → Feels sluggish, interrupts flow
|
||||
|
||||
**Exception:** Loading and progress indicators can be slower because they represent real waiting.
|
||||
|
||||
### Duration by Distance
|
||||
|
||||
Longer travel distance = longer duration (but not proportionally).
|
||||
|
||||
```
|
||||
Small movement (8-16px): 100-150ms
|
||||
Medium movement (50-100px): 150-250ms
|
||||
Large movement (full screen): 250-350ms
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Easing Functions
|
||||
|
||||
Easing makes motion feel natural. Linear motion looks robotic.
|
||||
|
||||
### Common Easing Curves
|
||||
|
||||
| Easing | Use For | Feel |
|
||||
|--------|---------|------|
|
||||
| **ease-out** | Elements entering | Fast start, gentle stop |
|
||||
| **ease-in** | Elements leaving | Gentle start, fast exit |
|
||||
| **ease-in-out** | Elements moving within view | Smooth throughout |
|
||||
| **linear** | Progress indicators, opacity changes | Mechanical (intentional) |
|
||||
|
||||
### When to Use Each
|
||||
|
||||
**Ease-out (default for entrances):**
|
||||
```css
|
||||
transition: transform 200ms ease-out;
|
||||
```
|
||||
- Modals appearing
|
||||
- Notifications sliding in
|
||||
- Dropdowns opening
|
||||
- Tooltips appearing
|
||||
|
||||
**Ease-in (for exits):**
|
||||
```css
|
||||
transition: opacity 150ms ease-in;
|
||||
```
|
||||
- Modals dismissing
|
||||
- Elements fading out
|
||||
- Notifications leaving
|
||||
|
||||
**Ease-in-out (for on-screen movement):**
|
||||
```css
|
||||
transition: transform 250ms ease-in-out;
|
||||
```
|
||||
- Tab indicators sliding
|
||||
- Carousel transitions
|
||||
- Drawer/sidebar toggling
|
||||
|
||||
### Custom Cubic Bezier
|
||||
|
||||
For more personality, customize curves:
|
||||
|
||||
```css
|
||||
/* Snappy entrance */
|
||||
transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
|
||||
/* Smooth overshoot */
|
||||
transition: transform 300ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Animation Patterns
|
||||
|
||||
### Button States
|
||||
|
||||
**Hover:**
|
||||
```css
|
||||
.btn {
|
||||
transition: background-color 100ms ease-out, transform 100ms ease-out;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: var(--btn-hover);
|
||||
}
|
||||
```
|
||||
|
||||
**Active/Pressed:**
|
||||
```css
|
||||
.btn:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
```
|
||||
|
||||
**Loading state:**
|
||||
```css
|
||||
.btn.loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.btn.loading .spinner {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
### Modal Entrance/Exit
|
||||
|
||||
**Enter:**
|
||||
```css
|
||||
.modal {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(-10px);
|
||||
transition: opacity 200ms ease-out, transform 200ms ease-out;
|
||||
}
|
||||
.modal.open {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
```
|
||||
|
||||
**Exit:**
|
||||
```css
|
||||
.modal.closing {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 150ms ease-in, transform 150ms ease-in;
|
||||
}
|
||||
```
|
||||
|
||||
### Dropdown/Menu
|
||||
|
||||
```css
|
||||
.dropdown {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
pointer-events: none;
|
||||
transition: opacity 150ms ease-out, transform 150ms ease-out;
|
||||
}
|
||||
.dropdown.open {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
```
|
||||
|
||||
### Toast/Notification
|
||||
|
||||
```css
|
||||
.toast {
|
||||
transform: translateX(100%);
|
||||
transition: transform 300ms ease-out;
|
||||
}
|
||||
.toast.visible {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.toast.exiting {
|
||||
transform: translateX(100%);
|
||||
transition: transform 200ms ease-in;
|
||||
}
|
||||
```
|
||||
|
||||
### Skeleton Loading
|
||||
|
||||
```css
|
||||
.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; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Loading States
|
||||
|
||||
### Types of Loading Indicators
|
||||
|
||||
| Type | Use When | Example |
|
||||
|------|----------|---------|
|
||||
| **Spinner** | Unknown duration, short wait expected | Button submission |
|
||||
| **Progress bar** | Known progress, longer operations | File upload |
|
||||
| **Skeleton** | Loading content layout | Feed items |
|
||||
| **Pulse/Shimmer** | Refreshing existing content | Pull to refresh |
|
||||
|
||||
### Spinner Guidelines
|
||||
|
||||
- Don't show immediately (wait 300-500ms)
|
||||
- If wait < 1 second, spinner may not be needed
|
||||
- Position in context (where content will appear)
|
||||
- Provide cancel option for long operations
|
||||
|
||||
```jsx
|
||||
// Delay spinner to avoid flash for fast operations
|
||||
const [showSpinner, setShowSpinner] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
const timer = setTimeout(() => setShowSpinner(true), 400);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
setShowSpinner(false);
|
||||
}, [isLoading]);
|
||||
```
|
||||
|
||||
### Progress Bar Guidelines
|
||||
|
||||
- Show percentage when meaningful
|
||||
- Don't let it jump backwards
|
||||
- Consider indeterminate state if progress unknown
|
||||
- Complete to 100% before hiding
|
||||
|
||||
### Skeleton Screen Guidelines
|
||||
|
||||
- Match layout of actual content
|
||||
- Use consistent bone shapes
|
||||
- Animate subtly (shimmer, not bounce)
|
||||
- Replace with content immediately when loaded
|
||||
|
||||
---
|
||||
|
||||
## Microinteractions
|
||||
|
||||
Small animations that provide feedback and delight.
|
||||
|
||||
### Effective Microinteractions
|
||||
|
||||
**Toggle switches:**
|
||||
```css
|
||||
.toggle-thumb {
|
||||
transition: transform 150ms ease-out;
|
||||
}
|
||||
.toggle.on .toggle-thumb {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
```
|
||||
|
||||
**Checkbox check:**
|
||||
```css
|
||||
.checkmark {
|
||||
stroke-dasharray: 20;
|
||||
stroke-dashoffset: 20;
|
||||
transition: stroke-dashoffset 200ms ease-out;
|
||||
}
|
||||
.checkbox.checked .checkmark {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
```
|
||||
|
||||
**Like/Heart animation:**
|
||||
```css
|
||||
.heart {
|
||||
transform: scale(1);
|
||||
transition: transform 150ms ease-out;
|
||||
}
|
||||
.heart.liked {
|
||||
animation: pop 300ms ease-out;
|
||||
}
|
||||
@keyframes pop {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.2); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
```
|
||||
|
||||
**Input focus:**
|
||||
```css
|
||||
.input {
|
||||
border-color: #ccc;
|
||||
transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
|
||||
}
|
||||
.input:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
```
|
||||
|
||||
### When Microinteractions Help
|
||||
|
||||
- Confirming user action occurred
|
||||
- Showing state change clearly
|
||||
- Making interface feel responsive
|
||||
- Guiding attention to changes
|
||||
|
||||
### When to Skip
|
||||
|
||||
- Repetitive actions (every keystroke)
|
||||
- Performance-critical paths
|
||||
- Accessibility mode (respect reduce-motion)
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Considerations
|
||||
|
||||
### Respect User Preferences
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Provide Alternatives
|
||||
|
||||
- Don't rely on animation alone to convey information
|
||||
- Ensure state changes are visible without animation
|
||||
- Allow users to disable animations in app settings
|
||||
|
||||
### Avoid Problematic Animations
|
||||
|
||||
| Avoid | Reason |
|
||||
|-------|--------|
|
||||
| Flashing/strobing | Can trigger seizures |
|
||||
| Parallax scrolling | Causes motion sickness |
|
||||
| Auto-playing video | Distracting, accessibility |
|
||||
| Infinite loops | Drains attention, battery |
|
||||
|
||||
---
|
||||
|
||||
## Performance Guidelines
|
||||
|
||||
### GPU-Accelerated Properties
|
||||
|
||||
Animate these for smooth 60fps:
|
||||
- `transform` (translate, scale, rotate)
|
||||
- `opacity`
|
||||
|
||||
Avoid animating (causes reflow/repaint):
|
||||
- `width`, `height`
|
||||
- `top`, `left`, `right`, `bottom`
|
||||
- `margin`, `padding`
|
||||
- `border-width`
|
||||
- `font-size`
|
||||
|
||||
### Use will-change Sparingly
|
||||
|
||||
```css
|
||||
/* Only for elements about to animate */
|
||||
.modal {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
/* Remove after animation */
|
||||
.modal.static {
|
||||
will-change: auto;
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Animations
|
||||
|
||||
Start animations together, not staggered excessively:
|
||||
- 0-50ms stagger: feels cohesive
|
||||
- 100ms+ stagger: feels slow, sequential
|
||||
|
||||
### Test on Low-end Devices
|
||||
|
||||
What's smooth on your MacBook may stutter on a budget Android phone. Test on real devices or throttle CPU in DevTools.
|
||||
|
||||
---
|
||||
|
||||
## Animation Checklist
|
||||
|
||||
Before shipping an animation:
|
||||
|
||||
- [ ] Does it serve a purpose (not just decoration)?
|
||||
- [ ] Is duration appropriate for the action?
|
||||
- [ ] Does easing feel natural?
|
||||
- [ ] Does it work with `prefers-reduced-motion`?
|
||||
- [ ] Is it GPU-accelerated (transform/opacity)?
|
||||
- [ ] Does it perform well on low-end devices?
|
||||
- [ ] Can the interface function without it?
|
||||
@@ -0,0 +1,472 @@
|
||||
# Data Visualization Design
|
||||
|
||||
Practical guide to designing charts, tables, and data displays that communicate clearly.
|
||||
|
||||
## Chart Selection
|
||||
|
||||
### Choosing the Right Chart Type
|
||||
|
||||
| Data Type | Best Chart | Avoid |
|
||||
|-----------|-----------|-------|
|
||||
| Part-to-whole | Pie (≤5 slices), stacked bar | Pie with many slices |
|
||||
| Change over time | Line chart, area chart | Pie chart |
|
||||
| Comparison (few items) | Bar chart (horizontal or vertical) | Line chart |
|
||||
| Comparison (many items) | Horizontal bar chart | Vertical bars (labels hard) |
|
||||
| Distribution | Histogram, box plot | Pie chart |
|
||||
| Correlation | Scatter plot | Line chart |
|
||||
| Composition over time | Stacked area, stacked bar | Multiple pie charts |
|
||||
|
||||
### When NOT to Use a Chart
|
||||
|
||||
Sometimes a simple number is better:
|
||||
- Single data point → Big number display
|
||||
- Two numbers to compare → Side-by-side with % change
|
||||
- Very few data points → Table might be clearer
|
||||
|
||||
```
|
||||
❌ Pie chart with 2 slices (73% vs 27%)
|
||||
✅ "73% of users completed onboarding"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bar Charts
|
||||
|
||||
### Orientation
|
||||
|
||||
**Vertical bars:** Best when comparing few categories with short labels
|
||||
**Horizontal bars:** Best when comparing many categories or labels are long
|
||||
|
||||
### Bar Chart Rules
|
||||
|
||||
1. **Always start at zero** - Truncated axes mislead
|
||||
2. **Order meaningfully** - By value (descending) or logical order (time, alphabet)
|
||||
3. **Space bars correctly** - Gap between bars = 50-100% of bar width
|
||||
4. **Label directly** - Put values on bars, not in legend
|
||||
5. **Limit categories** - 5-7 bars maximum; group others as "Other"
|
||||
|
||||
### Bar Styling
|
||||
|
||||
```css
|
||||
/* Bar appearance */
|
||||
.bar {
|
||||
fill: var(--primary);
|
||||
border-radius: 2px 2px 0 0; /* Slight rounding on top only */
|
||||
}
|
||||
|
||||
/* Hover state */
|
||||
.bar:hover {
|
||||
fill: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.bar-gap: 8px;
|
||||
.bar-width: 32px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Line Charts
|
||||
|
||||
### When to Use
|
||||
|
||||
- Continuous data over time
|
||||
- Trends matter more than individual values
|
||||
- Comparing multiple series over same time period
|
||||
|
||||
### Line Chart Rules
|
||||
|
||||
1. **Start Y-axis at zero** (usually) - Unless all values are in narrow range
|
||||
2. **Limit to 4-5 lines** - More becomes unreadable
|
||||
3. **Use distinct line styles** - Different colors, consider dashes for printing
|
||||
4. **Label lines directly** - Not just in legend
|
||||
5. **Highlight important points** - Mark significant events or thresholds
|
||||
|
||||
### Line Styling
|
||||
|
||||
```css
|
||||
/* Line appearance */
|
||||
.line {
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
/* Data points */
|
||||
.data-point {
|
||||
r: 4px;
|
||||
fill: white;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
/* Only show points on hover or for few data points */
|
||||
.data-point { opacity: 0; }
|
||||
.line-group:hover .data-point { opacity: 1; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pie Charts
|
||||
|
||||
### When to Use (Rarely)
|
||||
|
||||
- Showing parts of a whole
|
||||
- 2-5 slices maximum
|
||||
- Exact values less important than proportions
|
||||
- Users understand percentages must sum to 100%
|
||||
|
||||
### When NOT to Use
|
||||
|
||||
- Comparing values (use bar chart)
|
||||
- More than 5 categories
|
||||
- Values that don't sum to 100%
|
||||
- Showing change over time
|
||||
|
||||
### Pie Chart Rules
|
||||
|
||||
1. **Limit slices** - Maximum 5; combine small values into "Other"
|
||||
2. **Order by size** - Largest slice starting at 12 o'clock
|
||||
3. **Label directly** - Put labels on or near slices, not in legend
|
||||
4. **Show percentages** - Values help interpretation
|
||||
5. **Consider donut** - Center can show total or key metric
|
||||
|
||||
### Alternative: Simple Numbers
|
||||
|
||||
Often clearer than a pie chart:
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 67% Mobile 23% Desktop 10% Tablet │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Color in Data Visualization
|
||||
|
||||
### Categorical Colors
|
||||
|
||||
For different categories, use distinct hues:
|
||||
|
||||
```
|
||||
Blue: #3b82f6
|
||||
Orange: #f97316
|
||||
Green: #22c55e
|
||||
Purple: #8b5cf6
|
||||
Yellow: #eab308
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Maximum 5-7 distinct colors
|
||||
- Avoid red/green together (colorblindness)
|
||||
- Test with colorblind simulator
|
||||
|
||||
### Sequential Colors
|
||||
|
||||
For continuous data (low to high), use single hue with varying lightness:
|
||||
|
||||
```
|
||||
Light: #dbeafe (low values)
|
||||
#93c5fd
|
||||
#60a5fa
|
||||
#3b82f6
|
||||
Dark: #1d4ed8 (high values)
|
||||
```
|
||||
|
||||
### Diverging Colors
|
||||
|
||||
For data with meaningful center (positive/negative, above/below average):
|
||||
|
||||
```
|
||||
Negative: #ef4444 (red)
|
||||
Neutral: #f3f4f6 (gray)
|
||||
Positive: #22c55e (green)
|
||||
```
|
||||
|
||||
### Colorblind-Safe Palettes
|
||||
|
||||
Test your palette with:
|
||||
- Coblis Color Blindness Simulator
|
||||
- Chrome DevTools rendering settings
|
||||
|
||||
**Safe combinations:**
|
||||
- Blue + Orange (deuteranopia safe)
|
||||
- Blue + Yellow
|
||||
- Purple + Green + Orange
|
||||
|
||||
---
|
||||
|
||||
## Tables
|
||||
|
||||
### When Tables Beat Charts
|
||||
|
||||
- Precise values matter
|
||||
- Users need to look up specific data
|
||||
- Multiple attributes per item
|
||||
- Data will be exported/copied
|
||||
|
||||
### Table Design Principles
|
||||
|
||||
**1. Align numbers right**
|
||||
```
|
||||
Revenue
|
||||
────────
|
||||
$1,234
|
||||
$12,456
|
||||
$123,789
|
||||
```
|
||||
|
||||
**2. Align text left**
|
||||
```
|
||||
Product Name
|
||||
────────────
|
||||
Widget A
|
||||
Premium Widget
|
||||
Widget Pro Max
|
||||
```
|
||||
|
||||
**3. Use consistent precision**
|
||||
```
|
||||
❌ $1234, $567.89, $2.5k
|
||||
✅ $1,234.00, $567.89, $2,500.00
|
||||
```
|
||||
|
||||
**4. Minimize borders**
|
||||
```css
|
||||
/* Light horizontal lines only */
|
||||
tr {
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
/* No vertical borders, no heavy lines */
|
||||
```
|
||||
|
||||
**5. Generous padding**
|
||||
```css
|
||||
td {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
```
|
||||
|
||||
### Table Patterns
|
||||
|
||||
**Zebra striping:**
|
||||
```css
|
||||
tr:nth-child(even) {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
```
|
||||
|
||||
**Hover highlighting:**
|
||||
```css
|
||||
tr:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
```
|
||||
|
||||
**Sortable columns:**
|
||||
```html
|
||||
<th>
|
||||
Revenue
|
||||
<button aria-label="Sort by revenue">▼</button>
|
||||
</th>
|
||||
```
|
||||
|
||||
**Sticky headers:**
|
||||
```css
|
||||
thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard Layout
|
||||
|
||||
### Visual Hierarchy
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ KEY METRICS (big numbers) │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ $45.2K │ │ 1,234 │ │ 78.3% │ │ 4.2 │ │
|
||||
│ │ Revenue │ │ Users │ │ Conv. │ │ Rating │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ PRIMARY CHART (most important trend) │
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ [Line chart: Revenue over time] │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
├──────────────────────────┬───────────────────────────┤
|
||||
│ SUPPORTING CHARTS │ DETAIL TABLE │
|
||||
│ ┌──────────────────┐ │ ┌───────────────────────┐ │
|
||||
│ │ [Bar chart] │ │ │ Recent Transactions │ │
|
||||
│ └──────────────────┘ │ │ ... │ │
|
||||
│ ┌──────────────────┐ │ │ ... │ │
|
||||
│ │ [Donut chart] │ │ └───────────────────────┘ │
|
||||
│ └──────────────────┘ │ │
|
||||
└──────────────────────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Metric Cards
|
||||
|
||||
```html
|
||||
<div class="metric-card">
|
||||
<span class="label">Revenue</span>
|
||||
<span class="value">$45,234</span>
|
||||
<span class="change positive">↑ 12.3%</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
.metric-card {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.change.positive { color: #22c55e; }
|
||||
.change.negative { color: #ef4444; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chart Annotations
|
||||
|
||||
### When to Annotate
|
||||
|
||||
- Explain anomalies
|
||||
- Mark significant events
|
||||
- Highlight thresholds
|
||||
- Call out key insights
|
||||
|
||||
### Annotation Patterns
|
||||
|
||||
**Event markers:**
|
||||
```
|
||||
╭─── Product launch
|
||||
Revenue ──────────/│\──────────────
|
||||
│
|
||||
```
|
||||
|
||||
**Threshold lines:**
|
||||
```
|
||||
Revenue ──────────────────────
|
||||
- - - - - Target: $50k - - - -
|
||||
──────────────────────────────
|
||||
```
|
||||
|
||||
**Callout boxes:**
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
│ 📈 Best month ever! │
|
||||
│ +47% vs. last year │
|
||||
└─────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessibility in Data Viz
|
||||
|
||||
### Color Considerations
|
||||
|
||||
- Don't rely on color alone
|
||||
- Add patterns, labels, or icons
|
||||
- Ensure sufficient contrast
|
||||
- Test with colorblind simulators
|
||||
|
||||
### Screen Reader Support
|
||||
|
||||
```html
|
||||
<figure role="img" aria-label="Bar chart showing monthly revenue">
|
||||
<figcaption class="sr-only">
|
||||
Revenue by month: January $10,000, February $12,000...
|
||||
</figcaption>
|
||||
<svg>...</svg>
|
||||
</figure>
|
||||
```
|
||||
|
||||
### Data Tables as Alternative
|
||||
|
||||
For complex charts, provide data table version:
|
||||
```html
|
||||
<details>
|
||||
<summary>View data as table</summary>
|
||||
<table>...</table>
|
||||
</details>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Problem | Fix |
|
||||
|---------|---------|-----|
|
||||
| 3D charts | Distorts perception | Use flat 2D |
|
||||
| Dual Y-axes | Confusing comparisons | Separate charts |
|
||||
| Too many colors | Visual noise | Limit to 5-7 |
|
||||
| Missing zero | Misleading differences | Start at zero |
|
||||
| No labels | Hard to interpret | Label directly |
|
||||
| Decorative elements | Chartjunk distracts | Remove non-data ink |
|
||||
| Inconsistent scales | Misleading comparison | Match scales |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Chart Decision Tree
|
||||
|
||||
```
|
||||
What do you want to show?
|
||||
│
|
||||
├─ Comparison → Bar chart
|
||||
│
|
||||
├─ Change over time → Line chart
|
||||
│
|
||||
├─ Part of whole → Pie (≤5) or stacked bar
|
||||
│
|
||||
├─ Distribution → Histogram
|
||||
│
|
||||
├─ Relationship → Scatter plot
|
||||
│
|
||||
└─ Single value → Big number
|
||||
```
|
||||
|
||||
### Minimum Viable Chart Styling
|
||||
|
||||
```css
|
||||
/* Axes */
|
||||
.axis line, .axis path {
|
||||
stroke: #e5e7eb;
|
||||
}
|
||||
.axis text {
|
||||
fill: #6b7280;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid line {
|
||||
stroke: #f3f4f6;
|
||||
}
|
||||
|
||||
/* Data */
|
||||
.bar, .line, .point {
|
||||
fill: #3b82f6;
|
||||
stroke: #3b82f6;
|
||||
}
|
||||
|
||||
/* Labels */
|
||||
.label {
|
||||
fill: #374151;
|
||||
font-size: 11px;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,446 @@
|
||||
# Theming & Dark Mode Design
|
||||
|
||||
Creating effective color themes and implementing dark mode correctly.
|
||||
|
||||
## Dark Mode Philosophy
|
||||
|
||||
Dark mode isn't just inverting colors—it requires deliberate design decisions to maintain usability, hierarchy, and aesthetics.
|
||||
|
||||
### Why Dark Mode Matters
|
||||
|
||||
- **User preference:** Many users prefer it
|
||||
- **Eye strain:** Reduces strain in low-light environments
|
||||
- **Battery life:** Saves power on OLED screens
|
||||
- **Accessibility:** Some users have photosensitivity
|
||||
- **Professional expectation:** Users expect modern apps to support it
|
||||
|
||||
---
|
||||
|
||||
## Dark Mode Color Principles
|
||||
|
||||
### Don't Just Invert
|
||||
|
||||
| Light Mode | Bad Dark Mode | Good Dark Mode |
|
||||
|------------|---------------|----------------|
|
||||
| White `#ffffff` | Black `#000000` | Dark gray `#18181b` |
|
||||
| Black text `#000000` | White text `#ffffff` | Off-white `#fafafa` |
|
||||
| Gray `#6b7280` | Gray `#6b7280` | Lighter gray `#a1a1aa` |
|
||||
|
||||
### Key Principles
|
||||
|
||||
**1. Use dark grays, not pure black**
|
||||
|
||||
Pure black (`#000000`) creates harsh contrast and "halation" (text appears to glow).
|
||||
|
||||
```css
|
||||
/* Background scale for dark mode */
|
||||
--bg-base: #09090b; /* Deepest background */
|
||||
--bg-subtle: #18181b; /* Cards, elevated surfaces */
|
||||
--bg-muted: #27272a; /* Hover states, inputs */
|
||||
--bg-emphasis: #3f3f46; /* Active states */
|
||||
```
|
||||
|
||||
**2. Reduce contrast slightly**
|
||||
|
||||
Max contrast in dark mode is harsher than in light mode.
|
||||
|
||||
```css
|
||||
/* Text colors for dark mode */
|
||||
--text-primary: #fafafa; /* ~95% white, not 100% */
|
||||
--text-secondary: #a1a1aa; /* Muted text */
|
||||
--text-tertiary: #71717a; /* Subtle text */
|
||||
```
|
||||
|
||||
**3. Desaturate colors**
|
||||
|
||||
Bright saturated colors on dark backgrounds cause eye strain.
|
||||
|
||||
```css
|
||||
/* Light mode brand color */
|
||||
--primary-light: #3b82f6; /* Bright blue */
|
||||
|
||||
/* Dark mode - slightly desaturated */
|
||||
--primary-dark: #60a5fa; /* Lighter, less saturated */
|
||||
```
|
||||
|
||||
**4. Elevate with lightness, not shadow**
|
||||
|
||||
In dark mode, shadows are invisible. Show elevation with lighter surfaces.
|
||||
|
||||
```css
|
||||
/* Light mode: shadow for depth */
|
||||
.card-light {
|
||||
background: white;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Dark mode: lighter surface for depth */
|
||||
.card-dark {
|
||||
background: #27272a; /* Lighter than base */
|
||||
box-shadow: none; /* Or very subtle */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building a Dark Mode Palette
|
||||
|
||||
### Step 1: Define Your Gray Scale
|
||||
|
||||
Create 9-10 shades from near-black to near-white:
|
||||
|
||||
```css
|
||||
/* Dark mode gray scale (Zinc example) */
|
||||
--gray-950: #09090b; /* Deepest background */
|
||||
--gray-900: #18181b; /* Card backgrounds */
|
||||
--gray-800: #27272a; /* Elevated surfaces */
|
||||
--gray-700: #3f3f46; /* Borders, dividers */
|
||||
--gray-600: #52525b; /* Disabled states */
|
||||
--gray-500: #71717a; /* Placeholder text */
|
||||
--gray-400: #a1a1aa; /* Secondary text */
|
||||
--gray-300: #d4d4d8; /* Primary text (alt) */
|
||||
--gray-200: #e4e4e7; /* Headings */
|
||||
--gray-100: #f4f4f5; /* Emphasis text */
|
||||
--gray-50: #fafafa; /* Primary text */
|
||||
```
|
||||
|
||||
### Step 2: Adjust Accent Colors
|
||||
|
||||
```css
|
||||
/* Primary color adjustments */
|
||||
/* Light mode: use 500-600 range */
|
||||
--primary-light: #2563eb;
|
||||
|
||||
/* Dark mode: use 400-500 range (lighter) */
|
||||
--primary-dark: #3b82f6;
|
||||
|
||||
/* Same for semantic colors */
|
||||
--success-light: #16a34a;
|
||||
--success-dark: #22c55e;
|
||||
|
||||
--error-light: #dc2626;
|
||||
--error-dark: #ef4444;
|
||||
```
|
||||
|
||||
### Step 3: Define Semantic Tokens
|
||||
|
||||
```css
|
||||
/* Semantic tokens that switch based on mode */
|
||||
:root {
|
||||
--color-bg: var(--gray-50);
|
||||
--color-bg-subtle: var(--gray-100);
|
||||
--color-text: var(--gray-900);
|
||||
--color-text-muted: var(--gray-600);
|
||||
--color-border: var(--gray-200);
|
||||
--color-primary: var(--blue-600);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-bg: var(--gray-950);
|
||||
--color-bg-subtle: var(--gray-900);
|
||||
--color-text: var(--gray-50);
|
||||
--color-text-muted: var(--gray-400);
|
||||
--color-border: var(--gray-800);
|
||||
--color-primary: var(--blue-400);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategies
|
||||
|
||||
### Strategy 1: CSS Custom Properties
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--text: #18181b;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #18181b;
|
||||
--text: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
```
|
||||
|
||||
### Strategy 2: Data Attribute + Class
|
||||
|
||||
```html
|
||||
<html data-theme="dark">
|
||||
```
|
||||
|
||||
```css
|
||||
[data-theme="light"] {
|
||||
--bg: #ffffff;
|
||||
}
|
||||
[data-theme="dark"] {
|
||||
--bg: #18181b;
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Toggle theme
|
||||
function toggleTheme() {
|
||||
const current = document.documentElement.dataset.theme;
|
||||
document.documentElement.dataset.theme = current === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('theme', document.documentElement.dataset.theme);
|
||||
}
|
||||
|
||||
// Initialize from preference
|
||||
function initTheme() {
|
||||
const saved = localStorage.getItem('theme');
|
||||
const preferred = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
document.documentElement.dataset.theme = saved || preferred;
|
||||
}
|
||||
```
|
||||
|
||||
### Strategy 3: Tailwind Dark Mode
|
||||
|
||||
```html
|
||||
<!-- With class strategy -->
|
||||
<html class="dark">
|
||||
<body class="bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50">
|
||||
```
|
||||
|
||||
```javascript
|
||||
// tailwind.config.js
|
||||
module.exports = {
|
||||
darkMode: 'class', // or 'media' for system preference only
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Component Considerations
|
||||
|
||||
### Cards and Surfaces
|
||||
|
||||
```css
|
||||
/* Light: white with shadow */
|
||||
.card {
|
||||
background: white;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Dark: lighter surface, subtle or no shadow */
|
||||
[data-theme="dark"] .card {
|
||||
background: var(--gray-900);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.3); /* Darker shadow if any */
|
||||
/* Or: border: 1px solid var(--gray-800); */
|
||||
}
|
||||
```
|
||||
|
||||
### Form Inputs
|
||||
|
||||
```css
|
||||
.input {
|
||||
background: white;
|
||||
border: 1px solid var(--gray-300);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input {
|
||||
background: var(--gray-900);
|
||||
border: 1px solid var(--gray-700);
|
||||
}
|
||||
```
|
||||
|
||||
### Buttons
|
||||
|
||||
```css
|
||||
/* Primary button */
|
||||
.btn-primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-primary {
|
||||
/* Often same or slightly adjusted */
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* Secondary button */
|
||||
.btn-secondary {
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-secondary {
|
||||
background: var(--gray-800);
|
||||
color: var(--gray-100);
|
||||
}
|
||||
```
|
||||
|
||||
### Images and Media
|
||||
|
||||
```css
|
||||
/* Reduce brightness/contrast of images in dark mode */
|
||||
[data-theme="dark"] img:not([data-no-dim]) {
|
||||
filter: brightness(0.9) contrast(1.1);
|
||||
}
|
||||
|
||||
/* Invert diagrams/illustrations if needed */
|
||||
[data-theme="dark"] .diagram {
|
||||
filter: invert(1) hue-rotate(180deg);
|
||||
}
|
||||
```
|
||||
|
||||
### Syntax Highlighting
|
||||
|
||||
Don't forget code blocks need dark mode variants:
|
||||
- Use dark theme variants of syntax highlighters
|
||||
- Or invert colors appropriately
|
||||
- Popular: One Dark, Dracula, Night Owl
|
||||
|
||||
---
|
||||
|
||||
## Testing Dark Mode
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] All text is readable (sufficient contrast)
|
||||
- [ ] Hierarchy still clear (headings vs body)
|
||||
- [ ] Focus states visible
|
||||
- [ ] Images don't blow out
|
||||
- [ ] Forms inputs clearly visible
|
||||
- [ ] Error/success states distinct
|
||||
- [ ] Loading states visible
|
||||
- [ ] Shadows/elevation still work
|
||||
- [ ] Icons visible (may need color swap)
|
||||
- [ ] Brand colors still recognizable
|
||||
|
||||
### Contrast Ratios
|
||||
|
||||
Same WCAG requirements apply:
|
||||
- Normal text: 4.5:1 minimum
|
||||
- Large text: 3:1 minimum
|
||||
- UI components: 3:1 minimum
|
||||
|
||||
**Common dark mode fails:**
|
||||
- Gray text on dark background
|
||||
- Colored text on colored backgrounds
|
||||
- Disabled states too subtle
|
||||
|
||||
---
|
||||
|
||||
## Theme Toggle UI
|
||||
|
||||
### Placement
|
||||
|
||||
- Header/navigation (most common)
|
||||
- Settings page
|
||||
- Footer (less common)
|
||||
|
||||
### Icon Patterns
|
||||
|
||||
```html
|
||||
<!-- Sun/Moon toggle -->
|
||||
<button aria-label="Toggle dark mode">
|
||||
<svg class="sun hidden dark:block">...</svg>
|
||||
<svg class="moon block dark:hidden">...</svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
### State Options
|
||||
|
||||
1. **Light / Dark** - Simple toggle
|
||||
2. **Light / Dark / System** - Respect OS preference option
|
||||
3. **Auto only** - Always follow system (no toggle)
|
||||
|
||||
### Persistence
|
||||
|
||||
```javascript
|
||||
// Save preference
|
||||
localStorage.setItem('theme', 'dark');
|
||||
|
||||
// Load preference (with system fallback)
|
||||
const theme = localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced: Multiple Themes
|
||||
|
||||
### Brand Themes
|
||||
|
||||
```css
|
||||
[data-theme="brand-a"] {
|
||||
--primary: #ff6b6b;
|
||||
--primary-hover: #ee5a5a;
|
||||
}
|
||||
|
||||
[data-theme="brand-b"] {
|
||||
--primary: #4ecdc4;
|
||||
--primary-hover: #3dbdb5;
|
||||
}
|
||||
```
|
||||
|
||||
### Theme Structure
|
||||
|
||||
```css
|
||||
/* Base tokens (don't change) */
|
||||
--spacing-4: 16px;
|
||||
--radius-md: 8px;
|
||||
|
||||
/* Color tokens (change per theme) */
|
||||
--color-primary: ...;
|
||||
--color-bg: ...;
|
||||
|
||||
/* Component tokens (reference color tokens) */
|
||||
--button-bg: var(--color-primary);
|
||||
--card-bg: var(--color-bg);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Problem | Fix |
|
||||
|---------|---------|-----|
|
||||
| Pure black background | Harsh, looks flat | Use dark gray (#18181b) |
|
||||
| Pure white text | Too much contrast | Use off-white (#fafafa) |
|
||||
| Same saturated colors | Eye strain | Desaturate for dark mode |
|
||||
| Shadows for elevation | Invisible in dark | Use lighter surfaces |
|
||||
| Forgetting images | Can be too bright | Dim images slightly |
|
||||
| One contrast check | Colors interact differently | Check all combinations |
|
||||
| Forgetting focus states | Invisible borders | Ensure visible focus rings |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Minimum Viable Dark Mode
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--bg-subtle: #f4f4f5;
|
||||
--text: #18181b;
|
||||
--text-muted: #71717a;
|
||||
--border: #e4e4e7;
|
||||
--primary: #2563eb;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #18181b;
|
||||
--bg-subtle: #27272a;
|
||||
--text: #fafafa;
|
||||
--text-muted: #a1a1aa;
|
||||
--border: #3f3f46;
|
||||
--primary: #3b82f6;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,257 @@
|
||||
# UX Heuristic Audit Template
|
||||
|
||||
Structured template for conducting heuristic evaluations. Use this to systematically audit any interface.
|
||||
|
||||
---
|
||||
|
||||
## Audit Information
|
||||
|
||||
**Product/Feature:** ________________
|
||||
**Auditor:** ________________
|
||||
**Date:** ________________
|
||||
**Page/Screen:** ________________
|
||||
**User Context:** ________________ (who is using this, what's their goal?)
|
||||
|
||||
---
|
||||
|
||||
## Severity Scale Reference
|
||||
|
||||
| Rating | Severity | Priority | Description |
|
||||
|--------|----------|----------|-------------|
|
||||
| 0 | Not a problem | — | Cosmetic or disagreement |
|
||||
| 1 | Cosmetic | Low | Minor annoyance |
|
||||
| 2 | Minor | Medium | Delays or frustrates |
|
||||
| 3 | Major | High | Significant task failure |
|
||||
| 4 | Catastrophic | Critical | Prevents task completion |
|
||||
|
||||
---
|
||||
|
||||
## Heuristic Checklist
|
||||
|
||||
### 1. Visibility of System Status
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Loading states are shown | [ ] | | |
|
||||
| Actions provide immediate feedback | [ ] | | |
|
||||
| Errors are communicated clearly | [ ] | | |
|
||||
| Success is confirmed | [ ] | | |
|
||||
| Progress is visible for long tasks | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 2. Match Between System and Real World
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Uses user's language (not jargon) | [ ] | | |
|
||||
| Icons are recognizable | [ ] | | |
|
||||
| Order follows real-world logic | [ ] | | |
|
||||
| Metaphors are appropriate | [ ] | | |
|
||||
| Terms are consistent with industry | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 3. User Control and Freedom
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Undo is available | [ ] | | |
|
||||
| Cancel/Exit is always accessible | [ ] | | |
|
||||
| Back button works as expected | [ ] | | |
|
||||
| Users can skip optional steps | [ ] | | |
|
||||
| Modals have clear close options | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 4. Consistency and Standards
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Same terms for same concepts | [ ] | | |
|
||||
| Buttons/links styled consistently | [ ] | | |
|
||||
| Follows platform conventions | [ ] | | |
|
||||
| Layout is predictable | [ ] | | |
|
||||
| Same actions work the same way | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 5. Error Prevention
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Destructive actions require confirmation | [ ] | | |
|
||||
| Inline validation prevents bad submissions | [ ] | | |
|
||||
| Constrained inputs prevent errors | [ ] | | |
|
||||
| Defaults are sensible | [ ] | | |
|
||||
| "Unsaved changes" warning exists | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 6. Recognition Rather Than Recall
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Options are visible (not hidden) | [ ] | | |
|
||||
| Recent items/history is accessible | [ ] | | |
|
||||
| Icons have labels | [ ] | | |
|
||||
| Instructions are in context | [ ] | | |
|
||||
| Previous input is remembered | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 7. Flexibility and Efficiency
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Keyboard shortcuts for power users | [ ] | | |
|
||||
| Bulk actions available | [ ] | | |
|
||||
| Customization options exist | [ ] | | |
|
||||
| Frequent tasks are quick | [ ] | | |
|
||||
| Advanced features don't clutter basics | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 8. Aesthetic and Minimalist Design
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Only essential info is shown | [ ] | | |
|
||||
| Visual hierarchy is clear | [ ] | | |
|
||||
| Whitespace is used effectively | [ ] | | |
|
||||
| No unnecessary decoration | [ ] | | |
|
||||
| Primary action is obvious | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 9. Error Recovery
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Error messages are in plain language | [ ] | | |
|
||||
| Errors explain what went wrong | [ ] | | |
|
||||
| Errors suggest how to fix | [ ] | | |
|
||||
| User input is preserved on error | [ ] | | |
|
||||
| Error location is highlighted | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 10. Help and Documentation
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Help is searchable | [ ] | | |
|
||||
| Contextual help is available | [ ] | | |
|
||||
| Documentation is task-focused | [ ] | | |
|
||||
| Tooltips explain non-obvious features | [ ] | | |
|
||||
| Support is accessible | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Issue Count by Severity
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| 4 (Catastrophic) | |
|
||||
| 3 (Major) | |
|
||||
| 2 (Minor) | |
|
||||
| 1 (Cosmetic) | |
|
||||
|
||||
### Top Issues (Severity 3-4)
|
||||
|
||||
| # | Issue | Heuristic | Severity | Recommendation |
|
||||
|---|-------|-----------|----------|----------------|
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
| 4 | | | | |
|
||||
| 5 | | | | |
|
||||
|
||||
### Quick Wins (Low effort, high impact)
|
||||
|
||||
| Issue | Fix | Effort |
|
||||
|-------|-----|--------|
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
|
||||
---
|
||||
|
||||
## Task-Based Audit (Optional)
|
||||
|
||||
Test specific user tasks and note where issues occur.
|
||||
|
||||
### Task 1: ________________
|
||||
|
||||
| Step | Expected | Actual | Issue | Severity |
|
||||
|------|----------|--------|-------|----------|
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
|
||||
### Task 2: ________________
|
||||
|
||||
| Step | Expected | Actual | Issue | Severity |
|
||||
|------|----------|--------|-------|----------|
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
|
||||
---
|
||||
|
||||
## Navigation/Trunk Test
|
||||
|
||||
Can users answer these questions immediately?
|
||||
|
||||
| Question | Can Answer? | Notes |
|
||||
|----------|-------------|-------|
|
||||
| What site is this? | [ ] Yes [ ] No | |
|
||||
| What page am I on? | [ ] Yes [ ] No | |
|
||||
| What are the major sections? | [ ] Yes [ ] No | |
|
||||
| What are my options here? | [ ] Yes [ ] No | |
|
||||
| Where am I in the site structure? | [ ] Yes [ ] No | |
|
||||
| How can I search? | [ ] Yes [ ] No | |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations Summary
|
||||
|
||||
### Immediate (Severity 4)
|
||||
1.
|
||||
2.
|
||||
|
||||
### Short-term (Severity 3)
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Medium-term (Severity 2)
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Low Priority (Severity 1)
|
||||
1.
|
||||
2.
|
||||
@@ -0,0 +1,346 @@
|
||||
# Cultural UX Considerations
|
||||
|
||||
Designing for global audiences: RTL languages, color meanings, form conventions, and localization.
|
||||
|
||||
## Right-to-Left (RTL) Languages
|
||||
|
||||
### Languages That Use RTL
|
||||
|
||||
- Arabic
|
||||
- Hebrew
|
||||
- Persian (Farsi)
|
||||
- Urdu
|
||||
|
||||
### What to Mirror
|
||||
|
||||
| Element | LTR | RTL |
|
||||
|---------|-----|-----|
|
||||
| Text alignment | Left | Right |
|
||||
| Navigation | Left side | Right side |
|
||||
| Progress indicators | Left to right | Right to left |
|
||||
| Icons with direction | → | ← |
|
||||
| Checkmarks | ✓ on right | ✓ on left |
|
||||
| Back buttons | ← | → |
|
||||
| Carousels/sliders | Swipe left for next | Swipe right for next |
|
||||
|
||||
### What NOT to Mirror
|
||||
|
||||
| Element | Reason |
|
||||
|---------|--------|
|
||||
| Numbers | Always LTR (123, not ٣٢١) |
|
||||
| Phone numbers | Universal format |
|
||||
| Clocks | Universal format |
|
||||
| Video player controls | Universal convention |
|
||||
| Brand logos | Design integrity |
|
||||
| Math equations | Universal notation |
|
||||
|
||||
### CSS for RTL
|
||||
|
||||
```css
|
||||
/* Modern approach using logical properties */
|
||||
.element {
|
||||
/* Instead of margin-left: 16px */
|
||||
margin-inline-start: 16px;
|
||||
|
||||
/* Instead of padding-right: 8px */
|
||||
padding-inline-end: 8px;
|
||||
|
||||
/* Instead of text-align: left */
|
||||
text-align: start;
|
||||
|
||||
/* Instead of float: left */
|
||||
float: inline-start;
|
||||
}
|
||||
|
||||
/* Or use dir attribute */
|
||||
[dir="rtl"] .element {
|
||||
/* RTL-specific overrides */
|
||||
}
|
||||
```
|
||||
|
||||
### RTL Icons
|
||||
|
||||
Icons with inherent direction need mirroring:
|
||||
|
||||
| Icon | Mirror? | Reason |
|
||||
|------|---------|--------|
|
||||
| Back arrow | Yes | Directional navigation |
|
||||
| Forward arrow | Yes | Directional navigation |
|
||||
| Reply icon | Yes | Shows direction of response |
|
||||
| Search icon | No | Magnifying glass is universal |
|
||||
| Home icon | No | House has no direction |
|
||||
| Settings gear | No | Symmetric |
|
||||
| Play button | No | Universal media convention |
|
||||
|
||||
---
|
||||
|
||||
## Color Meanings Across Cultures
|
||||
|
||||
### Red
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Danger, stop, error, love |
|
||||
| China | Good luck, prosperity, happiness |
|
||||
| India | Purity, fertility |
|
||||
| South Africa | Mourning |
|
||||
| Japan | Life, anger |
|
||||
|
||||
**Design implication:** Don't use red for error states in apps targeting Chinese markets without context.
|
||||
|
||||
### White
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Purity, cleanliness, peace |
|
||||
| China/Japan | Death, mourning |
|
||||
| India | Unhappiness, mourning |
|
||||
|
||||
**Design implication:** White space may have different connotations; "clean" design may feel empty or cold.
|
||||
|
||||
### Green
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Go, success, nature, money |
|
||||
| Islamic | Sacred, paradise |
|
||||
| China | Infidelity (green hat = cuckold) |
|
||||
| Ireland | National identity |
|
||||
|
||||
**Design implication:** Green success states work globally, but be careful with green accessories or hats in Chinese contexts.
|
||||
|
||||
### Yellow
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Caution, happiness |
|
||||
| Japan | Courage, royalty |
|
||||
| Egypt | Mourning |
|
||||
| Latin America | Death, mourning |
|
||||
|
||||
### Blue
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Most cultures | Trust, calm, professionalism |
|
||||
| Iran | Mourning, spirituality |
|
||||
|
||||
**Design implication:** Blue is relatively safe globally; commonly used for links and primary actions.
|
||||
|
||||
### General Guidelines
|
||||
|
||||
1. **Test with local users** - Color perception varies
|
||||
2. **Don't rely on color alone** - Add icons and text
|
||||
3. **Provide customization** - Let users choose colors where possible
|
||||
4. **Research target markets** - Specific meanings in your target regions
|
||||
|
||||
---
|
||||
|
||||
## Form Conventions
|
||||
|
||||
### Name Fields
|
||||
|
||||
| Culture | Name Structure |
|
||||
|---------|----------------|
|
||||
| Western | First + Last (given + family) |
|
||||
| China/Japan/Korea | Family + Given |
|
||||
| Iceland | Given + Patronymic |
|
||||
| Spanish | Given + Father's surname + Mother's surname |
|
||||
| Arabic | Given + Father's + Grandfather's + Family |
|
||||
|
||||
**Best practices:**
|
||||
- Use single "Full Name" field when possible
|
||||
- If splitting, use "Given Name" and "Family Name" (not First/Last)
|
||||
- Don't assume first/last order
|
||||
- Allow long names (>50 characters)
|
||||
|
||||
### Address Fields
|
||||
|
||||
| Region | Considerations |
|
||||
|--------|----------------|
|
||||
| US | State, ZIP code (5 or 9 digits) |
|
||||
| UK | County optional, postcode format varies |
|
||||
| Japan | Prefecture, address reads large→small |
|
||||
| Brazil | CEP codes |
|
||||
| Countries without postal codes | 50+ countries don't use them |
|
||||
|
||||
**Best practices:**
|
||||
- Don't require postal code universally
|
||||
- Use country-appropriate field labels
|
||||
- Allow flexible formats
|
||||
- Consider address autocomplete services
|
||||
|
||||
### Phone Numbers
|
||||
|
||||
| Consideration | Approach |
|
||||
|---------------|----------|
|
||||
| Country codes | Allow input or select separately |
|
||||
| Length | Varies widely (7-15 digits) |
|
||||
| Format | Don't enforce specific format |
|
||||
| Mobile vs landline | Labels may not translate |
|
||||
|
||||
**Best practices:**
|
||||
- Accept multiple formats
|
||||
- Store in E.164 format internally (+1234567890)
|
||||
- Display in local format
|
||||
- Don't validate too strictly
|
||||
|
||||
### Dates
|
||||
|
||||
| Region | Format |
|
||||
|--------|--------|
|
||||
| US | MM/DD/YYYY |
|
||||
| Most of world | DD/MM/YYYY |
|
||||
| Japan, China, Korea | YYYY/MM/DD |
|
||||
| ISO standard | YYYY-MM-DD |
|
||||
|
||||
**Best practices:**
|
||||
- Use date pickers instead of text input
|
||||
- Show month names (not numbers) to avoid confusion
|
||||
- Store in ISO format (YYYY-MM-DD)
|
||||
- Display in user's locale
|
||||
|
||||
### Currency
|
||||
|
||||
| Consideration | Examples |
|
||||
|---------------|----------|
|
||||
| Symbol position | $100 vs 100€ vs 100 kr |
|
||||
| Decimal separator | $1,234.56 vs €1.234,56 |
|
||||
| Thousands separator | 1,000 vs 1.000 vs 1 000 |
|
||||
| Currency names | Dollar, Yuan, Rupee |
|
||||
|
||||
**Best practices:**
|
||||
- Format according to user locale
|
||||
- Always show currency symbol/code
|
||||
- Be clear about which currency
|
||||
- Handle conversion if multi-currency
|
||||
|
||||
---
|
||||
|
||||
## Text Considerations
|
||||
|
||||
### Text Expansion
|
||||
|
||||
Translated text is often longer than English:
|
||||
|
||||
| Language | Expansion Factor |
|
||||
|----------|------------------|
|
||||
| German | 1.3x |
|
||||
| French | 1.2x |
|
||||
| Russian | 1.2x |
|
||||
| Spanish | 1.2x |
|
||||
| Chinese | 0.8x |
|
||||
| Japanese | 0.9x |
|
||||
|
||||
**Design implications:**
|
||||
- Don't design to exact English text length
|
||||
- Allow buttons and labels to expand
|
||||
- Test layouts with longest expected translations
|
||||
- Use flexible grid/flexbox layouts
|
||||
|
||||
### Text in Images
|
||||
|
||||
Avoid text in images because:
|
||||
- Can't be translated easily
|
||||
- Not accessible to screen readers
|
||||
- Doesn't scale with user preferences
|
||||
- Complicates localization workflow
|
||||
|
||||
**Alternative:** Use CSS text over images.
|
||||
|
||||
### Numbers and Units
|
||||
|
||||
| Element | Consideration |
|
||||
|---------|---------------|
|
||||
| Measurement | Metric (most world) vs Imperial (US) |
|
||||
| Paper size | A4 (most world) vs Letter (US, Canada) |
|
||||
| Temperature | Celsius vs Fahrenheit |
|
||||
| Time | 24-hour vs 12-hour (AM/PM) |
|
||||
|
||||
---
|
||||
|
||||
## Icons and Imagery
|
||||
|
||||
### Potentially Problematic Icons
|
||||
|
||||
| Icon | Issue | Alternative |
|
||||
|------|-------|-------------|
|
||||
| Mailbox | US-specific design | Envelope |
|
||||
| Check mark | Means "wrong" in some cultures | Consider context |
|
||||
| Thumbs up | Offensive in some cultures | Hearts, stars |
|
||||
| Hand gestures | Vary widely in meaning | Avoid gestures |
|
||||
| Animals | Religious/cultural sensitivities | Research target market |
|
||||
| Religious symbols | May exclude or offend | Use neutral symbols |
|
||||
|
||||
### Photography
|
||||
|
||||
| Consideration | Approach |
|
||||
|---------------|----------|
|
||||
| Diversity | Represent target audience |
|
||||
| Gestures | Avoid culture-specific gestures |
|
||||
| Clothing | Consider cultural norms |
|
||||
| Settings | Use locally relevant contexts |
|
||||
| Food | Be aware of dietary restrictions |
|
||||
|
||||
---
|
||||
|
||||
## Localization Best Practices
|
||||
|
||||
### Technical
|
||||
|
||||
```javascript
|
||||
// Use internationalization libraries
|
||||
const formatter = new Intl.DateTimeFormat('de-DE');
|
||||
const date = formatter.format(new Date());
|
||||
|
||||
const currencyFormatter = new Intl.NumberFormat('ja-JP', {
|
||||
style: 'currency',
|
||||
currency: 'JPY'
|
||||
});
|
||||
const price = currencyFormatter.format(1000);
|
||||
```
|
||||
|
||||
### Content
|
||||
|
||||
- Use simple, clear language (easier to translate)
|
||||
- Avoid idioms and colloquialisms
|
||||
- Don't hardcode strings (use translation keys)
|
||||
- Provide context for translators
|
||||
- Test with pseudo-localization during development
|
||||
|
||||
### Design
|
||||
|
||||
- Design for flexibility (expanding text)
|
||||
- Use icons with text (not alone)
|
||||
- Test layouts in RTL and longest languages
|
||||
- Consider reading order in complex layouts
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Checklist
|
||||
|
||||
Before launching internationally:
|
||||
|
||||
**Layout:**
|
||||
- [ ] Supports RTL if targeting Arabic, Hebrew, Persian
|
||||
- [ ] Text can expand 30% without breaking
|
||||
- [ ] No text in images
|
||||
- [ ] Icons are culturally neutral
|
||||
|
||||
**Forms:**
|
||||
- [ ] Name fields are flexible
|
||||
- [ ] Addresses work without postal codes
|
||||
- [ ] Phone numbers accept various formats
|
||||
- [ ] Dates use pickers or clear formats
|
||||
|
||||
**Content:**
|
||||
- [ ] Colors don't carry unintended meaning
|
||||
- [ ] Images represent target audience
|
||||
- [ ] No culture-specific idioms
|
||||
- [ ] Units match target region
|
||||
|
||||
**Technical:**
|
||||
- [ ] Dates, numbers, currency use locale formatting
|
||||
- [ ] All strings externalized for translation
|
||||
- [ ] Character encoding supports target languages
|
||||
- [ ] Font supports required character sets
|
||||
@@ -0,0 +1,362 @@
|
||||
# Dark Patterns in UX Design
|
||||
|
||||
Understanding manipulative design practices to recognize and avoid them. Ethical alternatives that achieve business goals without deceiving users.
|
||||
|
||||
## What Are Dark Patterns?
|
||||
|
||||
Dark patterns are user interface designs that trick users into doing things they didn't intend. They exploit cognitive biases and psychological vulnerabilities for business benefit at user expense.
|
||||
|
||||
**Key distinction:**
|
||||
- Persuasion = helping users make decisions aligned with their goals
|
||||
- Dark patterns = tricking users into decisions against their interests
|
||||
|
||||
---
|
||||
|
||||
## Categories of Dark Patterns
|
||||
|
||||
### 1. Forced Continuity
|
||||
|
||||
**Definition:** Making it easy to sign up for a free trial but difficult to cancel.
|
||||
|
||||
**Examples:**
|
||||
- Require phone call to cancel (but signup was online)
|
||||
- Bury cancellation in settings maze
|
||||
- Show "Are you sure?" modals repeatedly
|
||||
- Require cancellation 30 days before renewal
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users pay for services they don't want
|
||||
- Exploits inertia and forgetfulness
|
||||
- Damages trust when discovered
|
||||
|
||||
**Ethical alternative:**
|
||||
- Cancel button as easy to find as signup
|
||||
- Clear cancellation confirmation (not guilt trips)
|
||||
- Email reminder before renewal charge
|
||||
- Allow pause instead of forcing cancel
|
||||
|
||||
### 2. Roach Motel
|
||||
|
||||
**Definition:** Easy to get into a situation, hard to get out.
|
||||
|
||||
**Examples:**
|
||||
- Account creation takes 1 minute, deletion takes 30 days and support tickets
|
||||
- Subscribing to emails requires one click, unsubscribing requires login + multiple confirmations
|
||||
- Joining is free, but exported data costs money
|
||||
|
||||
**Why it's harmful:**
|
||||
- Traps users against their will
|
||||
- Violates user autonomy
|
||||
- Often illegal under GDPR and similar regulations
|
||||
|
||||
**Ethical alternative:**
|
||||
- Symmetric design: if X is easy, reversing X should be easy
|
||||
- Account deletion should be self-service
|
||||
- Data export should be free and complete
|
||||
- Unsubscribe = one click
|
||||
|
||||
### 3. Privacy Zuckering
|
||||
|
||||
**Definition:** Tricking users into sharing more information than intended.
|
||||
|
||||
**Examples:**
|
||||
- Default settings share everything publicly
|
||||
- "Connect with friends" imports entire contact list
|
||||
- Profile completion gamification encourages oversharing
|
||||
- Confusing privacy controls that require expertise
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users lose control of personal information
|
||||
- Can lead to real-world harm (stalking, discrimination)
|
||||
- Exploits the complexity of privacy settings
|
||||
|
||||
**Ethical alternative:**
|
||||
- Privacy-respecting defaults (share nothing by default)
|
||||
- Clear, plain-language privacy explanations
|
||||
- Granular, understandable controls
|
||||
- Regular privacy checkups that surface settings
|
||||
|
||||
### 4. Bait and Switch
|
||||
|
||||
**Definition:** User sets out to do one thing, but something different happens.
|
||||
|
||||
**Examples:**
|
||||
- "X" button that triggers action instead of closing
|
||||
- "Download" button that's actually an ad
|
||||
- "Free trial" that immediately charges
|
||||
- Changing terms after user commits to purchase
|
||||
|
||||
**Why it's harmful:**
|
||||
- Directly deceives users about consequences
|
||||
- Violates fundamental expectations
|
||||
- Often results in unwanted charges or actions
|
||||
|
||||
**Ethical alternative:**
|
||||
- Buttons do exactly what they say
|
||||
- Clear labeling distinguishes ads from content
|
||||
- Free trials are genuinely free until stated conversion point
|
||||
- Terms are locked at time of agreement
|
||||
|
||||
### 5. Confirmshaming
|
||||
|
||||
**Definition:** Using guilt or shame to manipulate users into opting in.
|
||||
|
||||
**Examples:**
|
||||
- "No thanks, I don't want to save money"
|
||||
- "I'll stay ignorant" (for newsletter)
|
||||
- "I don't care about my health"
|
||||
- Imagery showing sad faces for decline option
|
||||
|
||||
**Why it's harmful:**
|
||||
- Manipulates emotions to override rational decision
|
||||
- Disrespects user autonomy
|
||||
- Creates negative brand association
|
||||
|
||||
**Ethical alternative:**
|
||||
- Neutral decline options: "No thanks" or "Maybe later"
|
||||
- Equal visual weight for both choices
|
||||
- Respect the "no" without comment
|
||||
- Focus on value proposition, not guilt
|
||||
|
||||
### 6. Hidden Costs
|
||||
|
||||
**Definition:** Prices or fees revealed only at final checkout.
|
||||
|
||||
**Examples:**
|
||||
- Service fees added at last step
|
||||
- Required "convenience fees"
|
||||
- Shipping costs revealed after entering payment info
|
||||
- "Processing fees" on top of advertised price
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users commit time/effort before learning true cost
|
||||
- Exploits sunk cost fallacy
|
||||
- Illegal in many jurisdictions (price must be clear)
|
||||
|
||||
**Ethical alternative:**
|
||||
- Show total cost including all fees upfront
|
||||
- If fees depend on choices, show estimates early
|
||||
- Price transparency builds trust
|
||||
- All-in pricing where possible
|
||||
|
||||
### 7. Misdirection
|
||||
|
||||
**Definition:** Design draws attention away from important information.
|
||||
|
||||
**Examples:**
|
||||
- Terms and conditions in tiny gray text
|
||||
- "Yes" button prominent, "No" button hidden
|
||||
- Pre-selected add-ons that require unchecking
|
||||
- Important disclaimers below the fold
|
||||
|
||||
**Why it's harmful:**
|
||||
- Prevents informed decision-making
|
||||
- Hides information users would want to know
|
||||
- Exploits visual hierarchy against users
|
||||
|
||||
**Ethical alternative:**
|
||||
- Important information is visually prominent
|
||||
- Both options equally accessible
|
||||
- Nothing pre-selected that costs money
|
||||
- Disclaimers at point of relevance, not hidden
|
||||
|
||||
### 8. Trick Questions
|
||||
|
||||
**Definition:** Confusing wording that leads to unintended choices.
|
||||
|
||||
**Examples:**
|
||||
- "Uncheck to not receive no emails" (double negative)
|
||||
- Checkboxes that mean opposite things mixed together
|
||||
- "Continue" meaning "I agree" without stating so
|
||||
- Questions worded to confuse opt-in vs opt-out
|
||||
|
||||
**Why it's harmful:**
|
||||
- Deliberately confuses users
|
||||
- Results in choices user didn't mean to make
|
||||
- Exploits cognitive load
|
||||
|
||||
**Ethical alternative:**
|
||||
- Clear, simple language
|
||||
- Consistent meaning (check = yes, uncheck = no)
|
||||
- Explicit confirmation language
|
||||
- User testing to catch confusing wording
|
||||
|
||||
### 9. Sneak into Basket
|
||||
|
||||
**Definition:** Items added to cart without user action.
|
||||
|
||||
**Examples:**
|
||||
- Insurance pre-selected during checkout
|
||||
- "Protection plan" added by default
|
||||
- Donation to charity checked by default
|
||||
- Accessories added when buying main product
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users pay for things they didn't choose
|
||||
- Exploits inattention during checkout
|
||||
- Often hidden in long checkout flows
|
||||
|
||||
**Ethical alternative:**
|
||||
- Nothing added without explicit user action
|
||||
- Optional items clearly offered (not pre-selected)
|
||||
- Cart contents always visible and editable
|
||||
- Confirmation of what's being purchased
|
||||
|
||||
### 10. Urgency & Scarcity (False)
|
||||
|
||||
**Definition:** Creating fake urgency or scarcity to pressure decisions.
|
||||
|
||||
**Examples:**
|
||||
- "Only 2 left!" (restocked hourly)
|
||||
- "This deal expires in 10:00" (resets on refresh)
|
||||
- "15 people viewing this" (fabricated)
|
||||
- "Prices increase tomorrow" (they don't)
|
||||
|
||||
**Why it's harmful:**
|
||||
- Pressures users into hasty decisions
|
||||
- Based on lies (not real scarcity)
|
||||
- Prevents price comparison and consideration
|
||||
- Particularly harmful for high-stakes purchases
|
||||
|
||||
**Ethical alternative:**
|
||||
- Only show real inventory counts
|
||||
- Honest sale end dates
|
||||
- If scarcity is real, explain why
|
||||
- Give users time to decide
|
||||
|
||||
---
|
||||
|
||||
## Regulatory Context
|
||||
|
||||
### GDPR (Europe)
|
||||
|
||||
Dark patterns affecting consent are illegal:
|
||||
- Consent must be freely given
|
||||
- Rejecting must be as easy as accepting
|
||||
- Pre-ticked boxes invalid for consent
|
||||
- Bundled consent (all-or-nothing) invalid
|
||||
|
||||
### FTC (United States)
|
||||
|
||||
The FTC has taken action against:
|
||||
- Hidden subscription fees
|
||||
- Difficult cancellation processes
|
||||
- Misleading "free trial" offers
|
||||
- Fake urgency and scarcity
|
||||
|
||||
### California Privacy Rights Act (CPRA)
|
||||
|
||||
Specifically prohibits:
|
||||
- Dark patterns in opt-out processes
|
||||
- Requires symmetry in design
|
||||
- Consent obtained through dark patterns is invalid
|
||||
|
||||
---
|
||||
|
||||
## How to Audit for Dark Patterns
|
||||
|
||||
### Checklist
|
||||
|
||||
**Signup/Subscription:**
|
||||
- [ ] Can users cancel as easily as they signed up?
|
||||
- [ ] Are renewal terms clear at signup?
|
||||
- [ ] Is the "free" trial genuinely free?
|
||||
|
||||
**Checkout:**
|
||||
- [ ] Is the total price clear before final step?
|
||||
- [ ] Are all added items explicitly chosen by user?
|
||||
- [ ] Are opt-outs as prominent as opt-ins?
|
||||
|
||||
**Data/Privacy:**
|
||||
- [ ] Are privacy settings understandable?
|
||||
- [ ] Are defaults privacy-respecting?
|
||||
- [ ] Can users export/delete their data easily?
|
||||
|
||||
**General:**
|
||||
- [ ] Does every button do what it says?
|
||||
- [ ] Is important information visually prominent?
|
||||
- [ ] Are decline options neutral (no shaming)?
|
||||
- [ ] Is urgency/scarcity real?
|
||||
|
||||
### The Mirror Test
|
||||
|
||||
Ask: "Would I feel comfortable if a journalist wrote about how this works?"
|
||||
|
||||
If the answer is no, it's probably a dark pattern.
|
||||
|
||||
---
|
||||
|
||||
## Ethical Alternatives That Work
|
||||
|
||||
### Instead of Forced Continuity
|
||||
|
||||
**Business goal:** Retain subscribers
|
||||
|
||||
**Ethical approach:**
|
||||
- Make the product so good they don't want to cancel
|
||||
- Offer pause option instead of cancel
|
||||
- Win-back campaigns for churned users
|
||||
- Ask why they're leaving and address it
|
||||
|
||||
### Instead of Hidden Costs
|
||||
|
||||
**Business goal:** Competitive-looking prices
|
||||
|
||||
**Ethical approach:**
|
||||
- All-in pricing (include fees in advertised price)
|
||||
- Compete on value, not deceptive pricing
|
||||
- Explain what fees cover (transparency builds trust)
|
||||
- Offer fee-free options (digital delivery, etc.)
|
||||
|
||||
### Instead of Confirmshaming
|
||||
|
||||
**Business goal:** Higher opt-in rates
|
||||
|
||||
**Ethical approach:**
|
||||
- Stronger value proposition
|
||||
- Social proof (join 100k subscribers)
|
||||
- Clear benefit statement
|
||||
- Respect "no" and try again later
|
||||
|
||||
### Instead of False Urgency
|
||||
|
||||
**Business goal:** Faster purchase decisions
|
||||
|
||||
**Ethical approach:**
|
||||
- Genuine limited-time offers (and honor them)
|
||||
- Waitlists for genuinely scarce items
|
||||
- Early access for committed customers
|
||||
- Value-based urgency (limited capacity, real deadlines)
|
||||
|
||||
---
|
||||
|
||||
## The Business Case Against Dark Patterns
|
||||
|
||||
### Short-term vs Long-term
|
||||
|
||||
| Metric | Dark Pattern Impact | Ethical Design Impact |
|
||||
|--------|---------------------|----------------------|
|
||||
| Initial conversion | ↑ Higher | Slightly lower |
|
||||
| Customer trust | ↓ Lower | ↑ Higher |
|
||||
| Churn rate | ↑ Higher | ↓ Lower |
|
||||
| Customer lifetime value | ↓ Lower | ↑ Higher |
|
||||
| Word of mouth | Negative | Positive |
|
||||
| Regulatory risk | High | Low |
|
||||
|
||||
### Real Costs of Dark Patterns
|
||||
|
||||
1. **Support costs** - Dealing with angry customers
|
||||
2. **Chargeback rates** - Users disputing unwanted charges
|
||||
3. **Reputation damage** - Social media exposure
|
||||
4. **Legal fees** - Defending against lawsuits
|
||||
5. **Regulatory fines** - Increasing enforcement
|
||||
6. **Employee morale** - Good people don't want to deceive users
|
||||
|
||||
### Companies That Changed
|
||||
|
||||
Several major companies have eliminated dark patterns after backlash:
|
||||
- LinkedIn simplified privacy controls after criticism
|
||||
- Amazon made "Subscribe & Save" more transparent
|
||||
- Apple added App Store subscription management
|
||||
|
||||
The pattern: Short-term thinking creates dark patterns; long-term thinking removes them.
|
||||
@@ -0,0 +1,296 @@
|
||||
# Resolving Heuristic Conflicts
|
||||
|
||||
When usability principles contradict each other—frameworks for making trade-off decisions.
|
||||
|
||||
## The Nature of Conflicts
|
||||
|
||||
Usability heuristics are guidelines, not laws. In real design, they often pull in opposite directions. Good UX design requires recognizing these tensions and making thoughtful trade-offs.
|
||||
|
||||
**Common tension patterns:**
|
||||
- Simplicity vs. Power
|
||||
- Consistency vs. Optimal for context
|
||||
- Efficiency vs. Error prevention
|
||||
- User control vs. Guidance
|
||||
- Discoverability vs. Clean interface
|
||||
|
||||
---
|
||||
|
||||
## Major Heuristic Conflicts
|
||||
|
||||
### 1. Simplicity vs. Flexibility
|
||||
|
||||
**The tension:**
|
||||
- "Keep it simple" (Krug, Nielsen)
|
||||
- "Support user control and freedom" (Nielsen #3)
|
||||
- "Flexibility and efficiency of use" (Nielsen #7)
|
||||
|
||||
**Example:** Photo editing app
|
||||
- Simple: Few options, quick editing
|
||||
- Flexible: Many controls, professional results
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Simple | Lean Flexible |
|
||||
|--------|-------------|---------------|
|
||||
| User expertise | Novice | Expert |
|
||||
| Task frequency | Occasional | Daily |
|
||||
| Consequences of error | Low | High |
|
||||
| Time pressure | High | Low |
|
||||
|
||||
**Design patterns:**
|
||||
- Progressive disclosure (simple default, power underneath)
|
||||
- Personas-based modes (Basic/Advanced)
|
||||
- Contextual features (show when relevant)
|
||||
|
||||
### 2. Consistency vs. Context Optimization
|
||||
|
||||
**The tension:**
|
||||
- "Consistency and standards" (Nielsen #4)
|
||||
- "Match between system and real world" (Nielsen #2)
|
||||
|
||||
**Example:** Destructive actions
|
||||
- Consistent: Red "Delete" button everywhere
|
||||
- Context-optimized: Prominent delete for items that should be deleted, hidden for critical data
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Consistent | Lean Contextual |
|
||||
|--------|-----------------|-----------------|
|
||||
| User base diversity | High | Low |
|
||||
| Learning curve concern | Yes | No |
|
||||
| Action frequency | Common | Rare |
|
||||
| Mental model strength | Established | Forming |
|
||||
|
||||
**Design patterns:**
|
||||
- Consistent patterns, contextual prominence
|
||||
- Same actions, different emphasis
|
||||
- Gradual introduction of contextual variations
|
||||
|
||||
### 3. Efficiency vs. Error Prevention
|
||||
|
||||
**The tension:**
|
||||
- "Error prevention" (Nielsen #5)
|
||||
- "Flexibility and efficiency of use" (Nielsen #7)
|
||||
|
||||
**Example:** Checkout flow
|
||||
- Error prevention: Confirmation at every step
|
||||
- Efficiency: One-click purchase
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Error Prevention | Lean Efficiency |
|
||||
|--------|----------------------|-----------------|
|
||||
| Reversibility | Irreversible | Easily undone |
|
||||
| Cost of error | High (money, data) | Low |
|
||||
| User familiarity | New users | Power users |
|
||||
| Frequency | Rare | Very frequent |
|
||||
|
||||
**Design patterns:**
|
||||
- Undo instead of confirm (efficient + safe)
|
||||
- Confidence-based friction (add steps for risky actions)
|
||||
- User-controlled safety level
|
||||
|
||||
### 4. Discoverability vs. Clean Interface
|
||||
|
||||
**The tension:**
|
||||
- "Recognition rather than recall" (Nielsen #6)
|
||||
- "Aesthetic and minimalist design" (Nielsen #8)
|
||||
|
||||
**Example:** Feature-rich application
|
||||
- Discoverable: Show all options visibly
|
||||
- Clean: Hide features until needed
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Discoverable | Lean Clean |
|
||||
|--------|-------------------|------------|
|
||||
| Feature frequency | Core features | Edge cases |
|
||||
| User expertise | Beginner | Expert |
|
||||
| Task complexity | Simple | Complex |
|
||||
| Screen real estate | Generous | Limited |
|
||||
|
||||
**Design patterns:**
|
||||
- Primary actions visible, secondary in menus
|
||||
- Progressive disclosure
|
||||
- Contextual menus
|
||||
- Search/command palettes for power users
|
||||
|
||||
### 5. Guidance vs. User Control
|
||||
|
||||
**The tension:**
|
||||
- Help users avoid errors
|
||||
- Respect user autonomy
|
||||
|
||||
**Example:** Form validation
|
||||
- Guidance: Prevent submission until valid
|
||||
- Control: Let users submit and see what happens
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Guidance | Lean Control |
|
||||
|--------|---------------|--------------|
|
||||
| User expertise | Novice | Expert |
|
||||
| Error recovery | Difficult | Easy |
|
||||
| System tolerance | Low (strict rules) | High (flexible) |
|
||||
| User frustration with restrictions | Low | High |
|
||||
|
||||
**Design patterns:**
|
||||
- Inline guidance (not blocking)
|
||||
- Warnings vs. blockers
|
||||
- "Are you sure?" rather than "You can't"
|
||||
|
||||
---
|
||||
|
||||
## Resolution Framework
|
||||
|
||||
### Step 1: Identify the Conflict
|
||||
|
||||
Name the specific heuristics in tension:
|
||||
- Which principle says do A?
|
||||
- Which principle says do B?
|
||||
- Why can't we fully satisfy both?
|
||||
|
||||
### Step 2: Assess the Context
|
||||
|
||||
Consider:
|
||||
- **User type:** Novice vs. expert
|
||||
- **Task criticality:** Browsing vs. financial transaction
|
||||
- **Frequency:** One-time vs. daily
|
||||
- **Reversibility:** Can they undo?
|
||||
- **Consequence:** What happens if wrong?
|
||||
|
||||
### Step 3: Prioritize for This Context
|
||||
|
||||
| Context | Usually Prioritize |
|
||||
|---------|-------------------|
|
||||
| Critical data, irreversible | Error prevention > Efficiency |
|
||||
| Frequent actions, low stakes | Efficiency > Error prevention |
|
||||
| New users, unfamiliar domain | Guidance > Control |
|
||||
| Expert users, familiar domain | Flexibility > Simplicity |
|
||||
| Limited screen, focused task | Minimalism > Discoverability |
|
||||
| Learning interface | Discoverability > Minimalism |
|
||||
|
||||
### Step 4: Design for Both When Possible
|
||||
|
||||
Often, clever design satisfies both:
|
||||
|
||||
| Conflict | Both-And Solution |
|
||||
|----------|-------------------|
|
||||
| Simple vs. Powerful | Progressive disclosure |
|
||||
| Efficient vs. Safe | Undo instead of confirm |
|
||||
| Clean vs. Discoverable | Contextual reveal |
|
||||
| Consistent vs. Optimal | Consistent patterns, variable emphasis |
|
||||
| Guided vs. Controlled | Warnings, not blockers |
|
||||
|
||||
### Step 5: Test the Trade-off
|
||||
|
||||
Validate with real users:
|
||||
- Does the chosen priority work for target users?
|
||||
- Are edge cases handled acceptably?
|
||||
- Do users understand why limitations exist?
|
||||
|
||||
---
|
||||
|
||||
## Common Conflict Scenarios
|
||||
|
||||
### Scenario: Onboarding Flow
|
||||
|
||||
**Conflict:** Efficiency (skip it) vs. Guidance (require it)
|
||||
|
||||
**Resolution:**
|
||||
- Allow skip but show value
|
||||
- Defer to contextual moments
|
||||
- Progressive onboarding during natural use
|
||||
|
||||
### Scenario: Mobile Navigation
|
||||
|
||||
**Conflict:** Discoverability (show all options) vs. Minimalism (hamburger menu)
|
||||
|
||||
**Resolution:**
|
||||
- Bottom navigation for 3-5 key items
|
||||
- Hamburger for secondary items
|
||||
- Tab bar > hamburger for primary navigation
|
||||
|
||||
### Scenario: Form Validation
|
||||
|
||||
**Conflict:** Error prevention (validate immediately) vs. Efficiency (let them type)
|
||||
|
||||
**Resolution:**
|
||||
- Validate on blur (not on keystroke)
|
||||
- Show errors inline, not modal
|
||||
- Allow submission, show all errors
|
||||
|
||||
### Scenario: Confirmation Dialogs
|
||||
|
||||
**Conflict:** Error prevention (confirm everything) vs. Efficiency (just do it)
|
||||
|
||||
**Resolution:**
|
||||
- Confirm only irreversible/high-cost actions
|
||||
- Provide undo instead of confirm
|
||||
- Use clear language about consequences
|
||||
|
||||
### Scenario: Default Settings
|
||||
|
||||
**Conflict:** User control (let them configure) vs. Simplicity (sensible defaults)
|
||||
|
||||
**Resolution:**
|
||||
- Smart defaults that work for 80%
|
||||
- Easy access to change settings
|
||||
- Don't require configuration to start
|
||||
|
||||
---
|
||||
|
||||
## Decision Documentation
|
||||
|
||||
When making trade-off decisions, document:
|
||||
|
||||
```markdown
|
||||
## Design Decision: [Feature]
|
||||
|
||||
### Conflict
|
||||
[Heuristic A] suggests we should...
|
||||
[Heuristic B] suggests we should...
|
||||
|
||||
### Context
|
||||
- User type: [Novice/Expert]
|
||||
- Task: [Critical/Casual]
|
||||
- Frequency: [Daily/Occasional]
|
||||
- Reversibility: [Yes/No]
|
||||
|
||||
### Decision
|
||||
We chose to prioritize [Heuristic] because...
|
||||
|
||||
### Mitigation
|
||||
We addressed the other concern by...
|
||||
|
||||
### Validation
|
||||
We'll know this is right when...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Principles
|
||||
|
||||
### 1. Context Is King
|
||||
|
||||
The same conflict should be resolved differently in different contexts. A banking app and a social media app may reach opposite conclusions.
|
||||
|
||||
### 2. Know Your Users
|
||||
|
||||
The trade-off depends heavily on user expertise and expectations. Test with real users, not assumptions.
|
||||
|
||||
### 3. Design for Both When Possible
|
||||
|
||||
Often, the conflict is artificial and clever design can satisfy both principles.
|
||||
|
||||
### 4. Make Conscious Trade-offs
|
||||
|
||||
Don't accidentally violate a heuristic. Violate it consciously with reasoning.
|
||||
|
||||
### 5. Document Decisions
|
||||
|
||||
Future you (and your team) will want to know why choices were made.
|
||||
|
||||
### 6. Revisit Trade-offs
|
||||
|
||||
As products and users evolve, optimal trade-offs may shift. Periodically review past decisions.
|
||||
@@ -0,0 +1,289 @@
|
||||
# Krug's Usability Principles
|
||||
|
||||
Full methodology from "Don't Make Me Think" for creating intuitive, usable interfaces.
|
||||
|
||||
## The Reality of How Users Use the Web
|
||||
|
||||
### Fact 1: We Don't Read, We Scan
|
||||
|
||||
**Why users scan:**
|
||||
- They're on a mission (have a goal)
|
||||
- They know they don't need to read everything
|
||||
- They're good at scanning from years of practice
|
||||
|
||||
**Design implications:**
|
||||
- Create visual hierarchy (important things bigger, bolder)
|
||||
- Use headings and subheadings liberally
|
||||
- Keep paragraphs short
|
||||
- Use bulleted lists
|
||||
- Highlight key terms
|
||||
|
||||
### Fact 2: We Don't Make Optimal Choices, We Satisfice
|
||||
|
||||
**Satisficing** = "satisfying" + "sufficing"
|
||||
|
||||
Users click the first reasonable option, not the best option.
|
||||
|
||||
**Why satisficing:**
|
||||
- Optimizing is hard and takes time
|
||||
- The penalty for guessing wrong is low (back button)
|
||||
- Weighing options doesn't improve results much
|
||||
- Guessing is more fun
|
||||
|
||||
**Design implications:**
|
||||
- Make the right choice obvious
|
||||
- Don't rely on users reading all options
|
||||
- Make consequences clear before clicking
|
||||
|
||||
### Fact 3: We Don't Figure Out How Things Work, We Muddle Through
|
||||
|
||||
Users don't read instructions. They try things until something works.
|
||||
|
||||
**Why muddling through:**
|
||||
- It's not important to them to know how it works
|
||||
- If they find something that works, they stick with it
|
||||
- It rarely matters if they don't use something optimally
|
||||
|
||||
**Design implications:**
|
||||
- Make things obvious so "figuring out" isn't needed
|
||||
- Expect users to use things "wrong"
|
||||
- Design for recovery from mistakes
|
||||
|
||||
---
|
||||
|
||||
## Things You Should Never Do
|
||||
|
||||
### Don't Make Users Think
|
||||
|
||||
Signs that your page makes users think:
|
||||
|
||||
| Symptom | Example |
|
||||
|---------|---------|
|
||||
| Puzzling labels | "Solutions" (solutions to what?) |
|
||||
| Links that could go anywhere | "Click here" |
|
||||
| Unexplained options | Checkboxes without context |
|
||||
| Unfamiliar terminology | Industry jargon |
|
||||
| Gratuitous cleverness | Puns, wordplay in navigation |
|
||||
|
||||
### Don't Waste Users' Goodwill
|
||||
|
||||
Users have a finite reservoir of goodwill:
|
||||
|
||||
**Things that diminish goodwill:**
|
||||
- Hiding info they need (phone numbers, prices)
|
||||
- Punishing them for not doing things your way
|
||||
- Asking for unnecessary information
|
||||
- Making them feel stupid
|
||||
- Making them repeat themselves
|
||||
- Sites that look like an afterthought
|
||||
- Amateur errors (broken links, typos)
|
||||
|
||||
**Things that increase goodwill:**
|
||||
- Know what questions they have and answer them
|
||||
- Minimize steps
|
||||
- Put effort into UI quality
|
||||
- Know what they're likely to struggle with
|
||||
- Make it easy to recover from errors
|
||||
- Apologize when things go wrong
|
||||
|
||||
### Don't Make Words Seem Important If They're Not
|
||||
|
||||
**Happy talk example:**
|
||||
> "Welcome to our website! We're excited to help you find exactly what you're looking for. Our team of dedicated professionals is committed to providing you with the best possible experience."
|
||||
|
||||
**Reality:** Users skip this. It says nothing.
|
||||
|
||||
**Rule:** If users will skip it, remove it.
|
||||
|
||||
---
|
||||
|
||||
## Navigation Must-Haves
|
||||
|
||||
### The Permanent Navigation
|
||||
|
||||
Every page needs:
|
||||
|
||||
1. **Site ID** (logo/name) - Top left corner
|
||||
2. **Page name** - Prominent, matches link that brought them
|
||||
3. **Sections** - Major site areas
|
||||
4. **Local navigation** - What's in this section
|
||||
5. **Utilities** - Sign in, Search, Help, Cart
|
||||
6. **"You are here" indicator** - Highlighted nav item
|
||||
|
||||
### The Trunk Test
|
||||
|
||||
Can users answer these on any random page?
|
||||
|
||||
| Question | Element That Answers It |
|
||||
|----------|------------------------|
|
||||
| What site is this? | Logo/Site ID |
|
||||
| What page am I on? | Page title |
|
||||
| What major sections exist? | Main navigation |
|
||||
| What are my options here? | Local navigation |
|
||||
| Where am I in the structure? | Breadcrumbs, highlighted nav |
|
||||
| How can I search? | Search box |
|
||||
|
||||
### Breadcrumbs
|
||||
|
||||
**Good breadcrumbs:**
|
||||
```
|
||||
Home > Products > Shoes > Running > Men's Trail Runners
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Put them at the top
|
||||
- Use ">" between levels
|
||||
- Make the current page name visible but not a link
|
||||
- Use small text (secondary importance)
|
||||
|
||||
---
|
||||
|
||||
## Homepage Guidelines
|
||||
|
||||
The homepage has to do too many things:
|
||||
|
||||
1. Site identity and mission
|
||||
2. Site hierarchy (navigation)
|
||||
3. Search
|
||||
4. Teases (content, features)
|
||||
5. Timely content
|
||||
6. Deals
|
||||
7. Shortcuts (popular items)
|
||||
8. Registration/Sign in
|
||||
|
||||
### Homepage Priorities
|
||||
|
||||
**Must do:**
|
||||
- Tell me what this site is
|
||||
- Tell me what I can do here
|
||||
- Tell me why I should be here (and not somewhere else)
|
||||
- Start me on my way
|
||||
|
||||
**Should do:**
|
||||
- Show me what I'm looking for
|
||||
- Show me where to start
|
||||
- Establish credibility and trust
|
||||
|
||||
### The Big Bang Theory of Web Design
|
||||
|
||||
**You have 3-4 seconds to answer:**
|
||||
1. What is this?
|
||||
2. What can I do here?
|
||||
3. Why should I be here?
|
||||
|
||||
### Tagline Guidelines
|
||||
|
||||
| Good Tagline | Bad Tagline |
|
||||
|--------------|-------------|
|
||||
| Conveys unique value | Generic platitude |
|
||||
| Specific and informative | Vague and fluffy |
|
||||
| 6-8 words | Too long or too short |
|
||||
| Instantly understandable | Requires thought |
|
||||
|
||||
Examples:
|
||||
- Good: "Find anything from thousands of stores"
|
||||
- Bad: "Welcome to the future of shopping"
|
||||
|
||||
---
|
||||
|
||||
## Mobile Usability
|
||||
|
||||
### Mobile Considerations
|
||||
|
||||
**Constraints:**
|
||||
- Smaller viewport
|
||||
- Fat fingers (need bigger targets)
|
||||
- Single-column layout
|
||||
- No hover states
|
||||
- Variable attention and context
|
||||
|
||||
### Mobile Specifics
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Tiny tap targets | Minimum 44×44 px |
|
||||
| Crowded nav | Hamburger or bottom nav |
|
||||
| Long forms | Break into steps |
|
||||
| Hover-dependent UI | Alternative for touch |
|
||||
| Text too small | 16px minimum body text |
|
||||
|
||||
### Mobile Trade-offs
|
||||
|
||||
**What to prioritize:**
|
||||
- Primary tasks front and center
|
||||
- Essential content visible
|
||||
- Fast load times
|
||||
- Easy to tap, hard to mis-tap
|
||||
|
||||
**What to hide/remove:**
|
||||
- Secondary navigation
|
||||
- Non-essential images
|
||||
- Decorative elements
|
||||
- Long-form content
|
||||
|
||||
---
|
||||
|
||||
## Usability Testing on $0
|
||||
|
||||
### How Many Users?
|
||||
|
||||
**3-4 users catches most issues.**
|
||||
|
||||
Testing with more users has diminishing returns. Better to test with 3, fix issues, then test again with 3.
|
||||
|
||||
### What to Test
|
||||
|
||||
1. Can they complete core tasks?
|
||||
2. Where do they get stuck?
|
||||
3. What do they say out loud?
|
||||
4. What did they expect vs. what happened?
|
||||
|
||||
### Test Protocol
|
||||
|
||||
**Before:**
|
||||
- "I'm testing the site, not you"
|
||||
- "Think out loud as you go"
|
||||
- "There are no wrong answers"
|
||||
|
||||
**During:**
|
||||
- Don't help. Don't explain. Just watch.
|
||||
- Note hesitations and confusions
|
||||
- Write down what they say
|
||||
|
||||
**After:**
|
||||
- Ask what was confusing
|
||||
- Ask what they expected
|
||||
- Ask how they'd describe the site
|
||||
|
||||
### Common Findings
|
||||
|
||||
| What Users Do | What It Means |
|
||||
|---------------|---------------|
|
||||
| Click wrong thing | Label is confusing |
|
||||
| Hesitate | Decision isn't obvious |
|
||||
| Look around lost | "You are here" is unclear |
|
||||
| Read everything | Design isn't self-evident |
|
||||
| Use search immediately | Navigation is failing |
|
||||
| Express confusion | Copy is unclear |
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Basics
|
||||
|
||||
### Why It Matters
|
||||
|
||||
- 15-20% of population has some disability
|
||||
- Accessible sites are better for everyone
|
||||
- It's often required by law
|
||||
- It's the right thing to do
|
||||
|
||||
### Quick Wins
|
||||
|
||||
| Fix | Benefit |
|
||||
|-----|---------|
|
||||
| Add alt text to images | Screen readers can describe |
|
||||
| Use sufficient contrast | Low-vision users can read |
|
||||
| Allow keyboard navigation | Motor-impaired users can navigate |
|
||||
| Use semantic HTML | Assistive tech understands structure |
|
||||
| Add focus indicators | Keyboard users know where they are |
|
||||
| Make touch targets large | Everyone benefits |
|
||||
@@ -0,0 +1,360 @@
|
||||
# Nielsen's 10 Usability Heuristics
|
||||
|
||||
Jakob Nielsen's 10 general principles for interaction design, with practical examples and common violations.
|
||||
|
||||
---
|
||||
|
||||
## 1. Visibility of System Status
|
||||
|
||||
**Principle:** The system should always keep users informed about what is going on, through appropriate feedback within reasonable time.
|
||||
|
||||
### Examples of Good Implementation
|
||||
|
||||
| Situation | Good Feedback |
|
||||
|-----------|---------------|
|
||||
| File uploading | Progress bar with percentage |
|
||||
| Form submitted | "Your message has been sent" |
|
||||
| Action processing | Loading spinner |
|
||||
| Background task | "Syncing 3 files..." notification |
|
||||
| Successful action | Green checkmark confirmation |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No loading indicator | User thinks it's broken | Add spinner/progress |
|
||||
| Silent failures | User thinks action worked | Show error message |
|
||||
| Delayed feedback | User clicks again | Immediate visual response |
|
||||
| No confirmation | "Did that work?" | Confirm successful actions |
|
||||
| Hidden status | User can't find progress | Surface status prominently |
|
||||
|
||||
### Severity Examples
|
||||
|
||||
- **Minor (1):** Save button has no "saved" confirmation
|
||||
- **Major (3):** Payment processing with no indicator
|
||||
- **Catastrophic (4):** Form submit shows nothing, user submits multiple times
|
||||
|
||||
---
|
||||
|
||||
## 2. Match Between System and Real World
|
||||
|
||||
**Principle:** The system should speak the users' language, with words, phrases, and concepts familiar to the user, rather than system-oriented terms.
|
||||
|
||||
### Examples of Good Implementation
|
||||
|
||||
| System Term | User-Friendly Term |
|
||||
|-------------|-------------------|
|
||||
| Authenticate | Sign in |
|
||||
| Terminate | Cancel / End |
|
||||
| Query | Search |
|
||||
| Repository | Folder |
|
||||
| Navigate to | Go to |
|
||||
| Initiate | Start |
|
||||
|
||||
### Real-World Metaphors
|
||||
|
||||
| Digital Element | Real-World Match |
|
||||
|-----------------|------------------|
|
||||
| Trash/Recycle bin | Waste basket |
|
||||
| Folder | File folder |
|
||||
| Desktop | Physical desk |
|
||||
| Shopping cart | Store cart |
|
||||
| Bookmark | Physical bookmark |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Technical jargon | Confusion | Use plain language |
|
||||
| Internal names | Meaningless to users | User-tested labels |
|
||||
| Inconsistent terms | Same thing, different names | One term per concept |
|
||||
| Unfamiliar icons | Users guess wrong | Add labels or tooltips |
|
||||
| Illogical order | Not matching expectations | Follow real-world sequences |
|
||||
|
||||
---
|
||||
|
||||
## 3. User Control and Freedom
|
||||
|
||||
**Principle:** Users often choose system functions by mistake and need a clearly marked "emergency exit" to leave the unwanted state without having to go through an extended dialogue.
|
||||
|
||||
### Examples of Good Implementation
|
||||
|
||||
| Action | Escape Route |
|
||||
|--------|--------------|
|
||||
| Accidentally deleted email | Undo button (Gmail) |
|
||||
| Wrong menu opened | Click outside to close |
|
||||
| Filled form incorrectly | Clear form / Reset |
|
||||
| Navigated wrong | Back button works |
|
||||
| Started wrong workflow | Cancel / Exit anytime |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No undo | Users afraid to act | Add undo for all actions |
|
||||
| Forced wizards | Can't skip or go back | Allow non-linear navigation |
|
||||
| Modal traps | Can't escape | Clear close/cancel buttons |
|
||||
| Broken back button | Frustration | Never hijack browser history |
|
||||
| Immediate deletion | No recovery | Soft delete + undo option |
|
||||
|
||||
### Key Principle
|
||||
|
||||
**Undo > Confirmation dialogs**
|
||||
|
||||
Users click through "Are you sure?" without reading. Undo lets them act confidently.
|
||||
|
||||
---
|
||||
|
||||
## 4. Consistency and Standards
|
||||
|
||||
**Principle:** Users should not have to wonder whether different words, situations, or actions mean the same thing. Follow platform conventions.
|
||||
|
||||
### Types of Consistency
|
||||
|
||||
| Type | Example |
|
||||
|------|---------|
|
||||
| **Internal** | Same button style throughout your app |
|
||||
| **External** | Same patterns as other apps |
|
||||
| **Visual** | Same colors mean same things |
|
||||
| **Functional** | Same action = same result |
|
||||
| **Linguistic** | Same terms for same concepts |
|
||||
|
||||
### Platform Conventions
|
||||
|
||||
| Element | Convention |
|
||||
|---------|------------|
|
||||
| Logo | Top left, links to home |
|
||||
| Search | Top right, magnifying glass |
|
||||
| Cart | Top right, shopping cart icon |
|
||||
| Menu (mobile) | Hamburger icon |
|
||||
| Primary action | Right side or bottom of form |
|
||||
| Cancel | Left of primary action (or text link) |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Different button styles | Confusion about importance | Consistent button hierarchy |
|
||||
| Same word, different meanings | Misunderstanding | One term per concept |
|
||||
| Unexpected link behavior | New tab when expecting same tab | Follow conventions |
|
||||
| Non-standard icons | Guessing game | Use recognized icons |
|
||||
| Inconsistent layouts | Relearning each page | Template-based layouts |
|
||||
|
||||
---
|
||||
|
||||
## 5. Error Prevention
|
||||
|
||||
**Principle:** Even better than good error messages is a careful design which prevents a problem from occurring in the first place.
|
||||
|
||||
### Prevention Strategies
|
||||
|
||||
| Strategy | Example |
|
||||
|----------|---------|
|
||||
| **Constraints** | Date picker instead of text field |
|
||||
| **Suggestions** | Autocomplete |
|
||||
| **Defaults** | Pre-fill common values |
|
||||
| **Confirmation** | "Delete permanently?" for destructive actions |
|
||||
| **Warnings** | "Unsaved changes" before leaving |
|
||||
|
||||
### Types of Errors to Prevent
|
||||
|
||||
| Error Type | Prevention |
|
||||
|------------|------------|
|
||||
| **Slips** (accidental) | Confirmation, undo, large targets |
|
||||
| **Mistakes** (wrong intention) | Clear instructions, better defaults |
|
||||
| **Data errors** | Validation, formatting help |
|
||||
| **Navigation errors** | Clear labels, undo |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Free text for constrained data | Invalid entries | Dropdowns, pickers |
|
||||
| No save warning | Lost work | "Unsaved changes" prompt |
|
||||
| Easy destructive actions | Accidental deletion | Require confirmation |
|
||||
| Accepting bad input | Garbage data | Inline validation |
|
||||
| Ambiguous choices | Wrong selection | Clear differentiation |
|
||||
|
||||
---
|
||||
|
||||
## 6. Recognition Rather Than Recall
|
||||
|
||||
**Principle:** Minimize the user's memory load by making objects, actions, and options visible. Don't require users to remember information.
|
||||
|
||||
### Recognition Techniques
|
||||
|
||||
| Instead of | Do This |
|
||||
|------------|---------|
|
||||
| User remembers command | Show menu of options |
|
||||
| User types from memory | Dropdown/autocomplete |
|
||||
| User remembers where they were | Breadcrumbs, recent history |
|
||||
| User remembers codes | Show decoded values |
|
||||
| User recalls previous info | Show previous entries |
|
||||
|
||||
### Examples
|
||||
|
||||
| Bad (Recall) | Good (Recognition) |
|
||||
|--------------|-------------------|
|
||||
| "Enter country code" | Dropdown with country names |
|
||||
| Command-line interface | Graphical menus |
|
||||
| "See page 47 for options" | Options shown in context |
|
||||
| "Re-enter your email" | Pre-filled from previous step |
|
||||
| Complex keyboard shortcuts | Visible toolbar buttons |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Empty form fields | User must remember format | Placeholder examples |
|
||||
| Hidden actions | User forgets they exist | Keep visible or in menus |
|
||||
| No recent items | User re-searches | Show search history |
|
||||
| Unlabeled icons | User guesses meaning | Add text labels |
|
||||
| Disconnected workflows | User loses context | Show progress, breadcrumbs |
|
||||
|
||||
---
|
||||
|
||||
## 7. Flexibility and Efficiency of Use
|
||||
|
||||
**Principle:** Accelerators—unseen by the novice user—may often speed up the interaction for the expert user. Allow users to tailor frequent actions.
|
||||
|
||||
### Accelerators for Experts
|
||||
|
||||
| Feature | Example |
|
||||
|---------|---------|
|
||||
| **Keyboard shortcuts** | Ctrl+S to save |
|
||||
| **Touch gestures** | Swipe to archive |
|
||||
| **Recent/Favorites** | Quick access to common items |
|
||||
| **Saved searches** | One-click complex queries |
|
||||
| **Customization** | Personalized dashboard |
|
||||
| **Bulk actions** | Select all + action |
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
| User Level | What They See |
|
||||
|------------|---------------|
|
||||
| Novice | Essential features only |
|
||||
| Intermediate | Common advanced options |
|
||||
| Expert | Full power (shortcuts, customization) |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No shortcuts | Experts slowed down | Add keyboard shortcuts |
|
||||
| No bulk operations | Tedious repetition | Add multi-select |
|
||||
| Required tutorials | Experts frustrated | Allow skipping |
|
||||
| Hidden power features | Experts don't find them | Discoverable advanced mode |
|
||||
| No customization | Forced workflows | Allow personalization |
|
||||
|
||||
---
|
||||
|
||||
## 8. Aesthetic and Minimalist Design
|
||||
|
||||
**Principle:** Dialogues should not contain information which is irrelevant or rarely needed. Every extra unit of information competes with the relevant units.
|
||||
|
||||
### Principles
|
||||
|
||||
| Principle | Application |
|
||||
|-----------|-------------|
|
||||
| **Signal/Noise** | Increase signal, reduce noise |
|
||||
| **Visual hierarchy** | Important things stand out |
|
||||
| **Whitespace** | Give elements room to breathe |
|
||||
| **Content priority** | Show what matters, hide what doesn't |
|
||||
| **Progressive disclosure** | Complexity on demand |
|
||||
|
||||
### What to Remove
|
||||
|
||||
| Remove | Why |
|
||||
|--------|-----|
|
||||
| Rarely-used features | Clutter |
|
||||
| Decorative elements | Distraction |
|
||||
| Redundant text | Noise |
|
||||
| Unnecessary options | Decision fatigue |
|
||||
| Instructions users skip | Wasted space |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Cluttered screens | Overwhelming | Remove/hide secondary |
|
||||
| Everything is "important" | Nothing stands out | Create hierarchy |
|
||||
| Long blocks of text | Nobody reads | Break up, summarize |
|
||||
| Too many colors | Visual noise | Limit palette |
|
||||
| Dense layouts | Hard to scan | Add whitespace |
|
||||
|
||||
---
|
||||
|
||||
## 9. Help Users Recognize, Diagnose, and Recover from Errors
|
||||
|
||||
**Principle:** Error messages should be expressed in plain language (no codes), precisely indicate the problem, and constructively suggest a solution.
|
||||
|
||||
### Good Error Message Components
|
||||
|
||||
1. **What happened** (plain language)
|
||||
2. **Why it happened** (if helpful)
|
||||
3. **How to fix it** (specific action)
|
||||
|
||||
### Examples
|
||||
|
||||
| Bad Error | Good Error |
|
||||
|-----------|------------|
|
||||
| "Error 500" | "Something went wrong. Please try again." |
|
||||
| "Invalid input" | "Email must include @" |
|
||||
| "Failed" | "Payment declined. Check card number or try different card." |
|
||||
| "Null reference exception" | "We couldn't load your data. Refresh the page." |
|
||||
|
||||
### Error Message Guidelines
|
||||
|
||||
| Guideline | Example |
|
||||
|-----------|---------|
|
||||
| Use plain language | "Connection failed" not "ECONNREFUSED" |
|
||||
| Be specific | "Password too short" not "Invalid password" |
|
||||
| Provide action | "Try again" button visible |
|
||||
| Don't blame user | "Card declined" not "You entered wrong info" |
|
||||
| Maintain context | Keep filled data, highlight error field |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Technical jargon | Confusion | Translate to plain English |
|
||||
| No solution | User stuck | Include next steps |
|
||||
| Generic messages | Not helpful | Be specific |
|
||||
| Blaming language | Defensive users | Neutral, helpful tone |
|
||||
| Clearing form on error | Punishment | Preserve user input |
|
||||
|
||||
---
|
||||
|
||||
## 10. Help and Documentation
|
||||
|
||||
**Principle:** Even though it's better if the system can be used without documentation, it may be necessary to provide help and documentation. Such information should be easy to search, focused on the user's task, and not be too large.
|
||||
|
||||
### Characteristics of Good Help
|
||||
|
||||
| Characteristic | Implementation |
|
||||
|----------------|----------------|
|
||||
| **Searchable** | Full-text search |
|
||||
| **Task-focused** | "How to..." format |
|
||||
| **Contextual** | In-page tooltips |
|
||||
| **Scannable** | Short paragraphs, lists |
|
||||
| **Actionable** | Step-by-step instructions |
|
||||
|
||||
### Types of Help
|
||||
|
||||
| Type | When to Use |
|
||||
|------|-------------|
|
||||
| **Inline help** | Tooltips, hints | Next to complex fields |
|
||||
| **Contextual help** | "?" icons | For non-obvious features |
|
||||
| **Searchable docs** | Knowledge base | For detailed questions |
|
||||
| **Guided tours** | Onboarding | First-time users |
|
||||
| **Chat/Support** | Complex issues | When self-service fails |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No search in docs | Can't find answers | Add search |
|
||||
| Long documentation | Nobody reads | Concise, task-focused |
|
||||
| Generic help | Doesn't answer question | Specific to feature/page |
|
||||
| Hidden help | Users can't find it | Visible help links |
|
||||
| No contextual help | Users leave page | Inline tooltips |
|
||||
@@ -0,0 +1,306 @@
|
||||
# WCAG 2.1 AA Checklist
|
||||
|
||||
Complete checklist for WCAG 2.1 Level AA compliance with testing guidance.
|
||||
|
||||
## Perceivable
|
||||
|
||||
Content must be presentable in ways users can perceive.
|
||||
|
||||
### 1.1 Text Alternatives
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.1.1 Non-text Content | All images, icons, and visual content have text alternatives | Inspect alt attributes; use screen reader |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Informative images have descriptive alt text
|
||||
- [ ] Decorative images have empty alt (`alt=""`)
|
||||
- [ ] Complex images (charts, diagrams) have extended descriptions
|
||||
- [ ] Icons have accessible names
|
||||
- [ ] CAPTCHA provides audio alternative
|
||||
|
||||
### 1.2 Time-based Media
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.2.1 Audio/Video (prerecorded) | Captions and/or transcripts | Check video player for captions |
|
||||
| 1.2.2 Captions | Synchronized captions for video | Watch with captions on |
|
||||
| 1.2.3 Audio Description | Description of visual content | Check for AD track |
|
||||
| 1.2.5 Audio Description (AA) | Audio description for all video | Verify AD available |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Videos have synchronized captions
|
||||
- [ ] Captions are accurate and complete
|
||||
- [ ] Audio descriptions available for important visual content
|
||||
- [ ] Transcripts available for audio-only content
|
||||
|
||||
### 1.3 Adaptable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.3.1 Info and Relationships | Semantic structure preserved | Inspect HTML; use screen reader |
|
||||
| 1.3.2 Meaningful Sequence | Content order makes sense | Disable CSS; read in DOM order |
|
||||
| 1.3.3 Sensory Characteristics | Don't rely on shape/color/position alone | Check instructions |
|
||||
| 1.3.4 Orientation (AA) | Works in portrait and landscape | Rotate device |
|
||||
| 1.3.5 Identify Input Purpose (AA) | Input fields have autocomplete | Check `autocomplete` attributes |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Headings use proper h1-h6 hierarchy
|
||||
- [ ] Lists use `<ul>`, `<ol>`, `<dl>` elements
|
||||
- [ ] Forms have proper labels associated with inputs
|
||||
- [ ] Tables have headers marked with `<th>`
|
||||
- [ ] Reading order is logical without CSS
|
||||
- [ ] Works in both orientations
|
||||
- [ ] Input fields have appropriate autocomplete values
|
||||
|
||||
### 1.4 Distinguishable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.4.1 Use of Color | Color not sole means of info | View in grayscale |
|
||||
| 1.4.2 Audio Control | Auto-playing audio can be stopped | Check for controls |
|
||||
| 1.4.3 Contrast (Minimum) | 4.5:1 text, 3:1 large text | Use contrast checker |
|
||||
| 1.4.4 Resize Text | Readable at 200% zoom | Zoom browser |
|
||||
| 1.4.5 Images of Text | Use real text, not images | Inspect for text images |
|
||||
| 1.4.10 Reflow (AA) | No horizontal scroll at 320px | Test at narrow width |
|
||||
| 1.4.11 Non-text Contrast (AA) | 3:1 for UI components | Check buttons, inputs |
|
||||
| 1.4.12 Text Spacing (AA) | Survives increased text spacing | Apply spacing override |
|
||||
| 1.4.13 Content on Hover/Focus (AA) | Hoverable, dismissible, persistent | Test tooltips, menus |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Normal text has 4.5:1 contrast ratio minimum
|
||||
- [ ] Large text (18pt+) has 3:1 contrast ratio minimum
|
||||
- [ ] UI components have 3:1 contrast
|
||||
- [ ] Text resizes to 200% without loss
|
||||
- [ ] No horizontal scrolling at 320px width
|
||||
- [ ] Tooltips are dismissible, hoverable, persistent
|
||||
- [ ] Color alone doesn't convey meaning
|
||||
|
||||
---
|
||||
|
||||
## Operable
|
||||
|
||||
Users must be able to operate the interface.
|
||||
|
||||
### 2.1 Keyboard Accessible
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.1.1 Keyboard | All functionality works with keyboard | Navigate with Tab, Enter, Space |
|
||||
| 2.1.2 No Keyboard Trap | Focus can always move away | Tab through everything |
|
||||
| 2.1.4 Character Key Shortcuts (AA) | Single-key shortcuts can be disabled | Check for shortcut conflicts |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] All interactive elements are focusable
|
||||
- [ ] All actions can be performed via keyboard
|
||||
- [ ] Focus is never trapped
|
||||
- [ ] Single-key shortcuts can be disabled or remapped
|
||||
- [ ] Tab order follows logical sequence
|
||||
|
||||
### 2.2 Enough Time
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.2.1 Timing Adjustable | Users can extend/disable time limits | Check for timeouts |
|
||||
| 2.2.2 Pause, Stop, Hide | Auto-updating content can be controlled | Check carousels, animations |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Session timeouts have 20-second warning
|
||||
- [ ] Users can extend time limits
|
||||
- [ ] Auto-playing content has pause control
|
||||
- [ ] Animations can be stopped
|
||||
|
||||
### 2.3 Seizures
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.3.1 Three Flashes | No content flashes >3 times/second | Measure flash rate |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] No flashing content above threshold
|
||||
- [ ] Animations don't cause seizure risk
|
||||
|
||||
### 2.4 Navigable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.4.1 Bypass Blocks | Skip links to bypass repeated content | Check for skip link |
|
||||
| 2.4.2 Page Titled | Pages have descriptive titles | Check `<title>` element |
|
||||
| 2.4.3 Focus Order | Focus sequence is logical | Tab through page |
|
||||
| 2.4.4 Link Purpose | Link text describes destination | Read links out of context |
|
||||
| 2.4.5 Multiple Ways (AA) | Multiple ways to find pages | Check nav, search, sitemap |
|
||||
| 2.4.6 Headings and Labels (AA) | Headings and labels are descriptive | Review all headings |
|
||||
| 2.4.7 Focus Visible (AA) | Focus indicator is visible | Tab through interface |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Skip link present at top of page
|
||||
- [ ] Page titles are unique and descriptive
|
||||
- [ ] Tab order follows visual order
|
||||
- [ ] Link text is meaningful ("Read more about X" not "Click here")
|
||||
- [ ] Multiple navigation methods available
|
||||
- [ ] All headings are descriptive
|
||||
- [ ] Focus indicator is clearly visible
|
||||
|
||||
### 2.5 Input Modalities
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.5.1 Pointer Gestures (AA) | Complex gestures have simple alternatives | Check for alternatives |
|
||||
| 2.5.2 Pointer Cancellation (AA) | Actions on up-event, cancelable | Test click/drag behavior |
|
||||
| 2.5.3 Label in Name (AA) | Accessible name contains visible label | Compare visible/accessible names |
|
||||
| 2.5.4 Motion Actuation (AA) | Motion-based actions have alternatives | Check for non-motion options |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Pinch, swipe have tap alternatives
|
||||
- [ ] Click actions happen on release (up-event)
|
||||
- [ ] Dragging outside target cancels action
|
||||
- [ ] Accessible names include visible text
|
||||
- [ ] Shake/tilt actions have button alternatives
|
||||
|
||||
---
|
||||
|
||||
## Understandable
|
||||
|
||||
Content must be understandable to users.
|
||||
|
||||
### 3.1 Readable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 3.1.1 Language of Page | Page language specified | Check `<html lang="">` |
|
||||
| 3.1.2 Language of Parts (AA) | Foreign text marked with lang | Check multilingual content |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] HTML has lang attribute
|
||||
- [ ] Foreign language passages have lang attribute
|
||||
|
||||
### 3.2 Predictable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 3.2.1 On Focus | Focus doesn't cause unexpected changes | Tab through all elements |
|
||||
| 3.2.2 On Input | Input doesn't cause unexpected changes | Fill forms without submitting |
|
||||
| 3.2.3 Consistent Navigation (AA) | Navigation is consistent across pages | Compare pages |
|
||||
| 3.2.4 Consistent Identification (AA) | Same functions have same labels | Compare repeated elements |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Focus doesn't trigger context changes
|
||||
- [ ] Selecting options doesn't submit forms
|
||||
- [ ] Navigation is in same location on all pages
|
||||
- [ ] Same icons/labels used for same functions
|
||||
|
||||
### 3.3 Input Assistance
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 3.3.1 Error Identification | Errors clearly identified | Submit invalid forms |
|
||||
| 3.3.2 Labels or Instructions | Labels and instructions provided | Review all forms |
|
||||
| 3.3.3 Error Suggestion (AA) | Suggestions for fixing errors | Trigger errors |
|
||||
| 3.3.4 Error Prevention (AA) | Confirmation for legal/financial actions | Test critical submissions |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Errors are clearly described in text
|
||||
- [ ] Error messages explain how to fix
|
||||
- [ ] All inputs have visible labels
|
||||
- [ ] Required fields are indicated
|
||||
- [ ] Legal/financial submissions are reversible or confirmed
|
||||
|
||||
---
|
||||
|
||||
## Robust
|
||||
|
||||
Content must work with assistive technologies.
|
||||
|
||||
### 4.1 Compatible
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 4.1.1 Parsing | Valid HTML | Run HTML validator |
|
||||
| 4.1.2 Name, Role, Value | ARIA used correctly | Inspect with accessibility tools |
|
||||
| 4.1.3 Status Messages (AA) | Status updates announced | Test with screen reader |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] HTML is valid (no duplicate IDs, proper nesting)
|
||||
- [ ] Custom controls have appropriate roles
|
||||
- [ ] States (expanded, selected) are programmatically set
|
||||
- [ ] Status messages use aria-live regions
|
||||
|
||||
---
|
||||
|
||||
## Testing Tools
|
||||
|
||||
### Automated Testing
|
||||
|
||||
| Tool | What It Catches | Use For |
|
||||
|------|-----------------|---------|
|
||||
| axe DevTools | ~30% of issues | Quick scan |
|
||||
| WAVE | Similar to axe | Visual overlay |
|
||||
| Lighthouse | Basic accessibility | CI/CD integration |
|
||||
| Pa11y | CLI-based | Automated testing |
|
||||
|
||||
### Manual Testing Required
|
||||
|
||||
| Area | How to Test |
|
||||
|------|-------------|
|
||||
| Keyboard navigation | Unplug mouse, use only keyboard |
|
||||
| Screen reader | VoiceOver (Mac), NVDA (Windows) |
|
||||
| Zoom | Browser zoom to 200%, 400% |
|
||||
| Color contrast | WebAIM Contrast Checker |
|
||||
| Color blindness | Sim Daltonism, Chrome DevTools |
|
||||
|
||||
### Screen Reader Testing
|
||||
|
||||
**VoiceOver (Mac):**
|
||||
1. Enable: Cmd + F5
|
||||
2. Navigate: VO (Ctrl + Opt) + arrows
|
||||
3. Rotor: VO + U (headings, links, forms)
|
||||
|
||||
**NVDA (Windows):**
|
||||
1. Download free from nvaccess.org
|
||||
2. Navigate: Arrow keys, Tab
|
||||
3. Elements list: NVDA + F7
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Common Failures
|
||||
|
||||
| Issue | Failure | Fix |
|
||||
|-------|---------|-----|
|
||||
| Missing alt text | 1.1.1 | Add descriptive alt or `alt=""` |
|
||||
| Low contrast | 1.4.3 | Increase to 4.5:1 (text) or 3:1 (large/UI) |
|
||||
| No focus indicator | 2.4.7 | Add visible :focus-visible styles |
|
||||
| Keyboard inaccessible | 2.1.1 | Make interactive, add tabindex=0 |
|
||||
| Missing form labels | 1.3.1, 3.3.2 | Add `<label>` with `for` attribute |
|
||||
| No skip link | 2.4.1 | Add "Skip to content" link |
|
||||
| Missing page title | 2.4.2 | Add descriptive `<title>` |
|
||||
| Color-only meaning | 1.4.1 | Add icon or text alongside |
|
||||
| No lang attribute | 3.1.1 | Add `<html lang="en">` |
|
||||
| Ambiguous links | 2.4.4 | Use descriptive link text |
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist Summary
|
||||
|
||||
Before launch, verify:
|
||||
|
||||
**Automated scan:**
|
||||
- [ ] axe DevTools shows 0 issues
|
||||
- [ ] Lighthouse accessibility score >90
|
||||
|
||||
**Keyboard:**
|
||||
- [ ] All functionality works with keyboard
|
||||
- [ ] Focus visible on all elements
|
||||
- [ ] No keyboard traps
|
||||
- [ ] Skip link works
|
||||
|
||||
**Screen reader:**
|
||||
- [ ] All content announced correctly
|
||||
- [ ] Form fields have labels
|
||||
- [ ] Images have alt text
|
||||
- [ ] Headings create logical outline
|
||||
|
||||
**Visual:**
|
||||
- [ ] All text meets contrast requirements
|
||||
- [ ] Works at 200% zoom
|
||||
- [ ] Works at 320px width (mobile)
|
||||
- [ ] Color not sole indicator
|
||||
Reference in New Issue
Block a user