Distilled from Vercel's https://github.com/vercel-labs/web-interface-guidelines
Interactions
- [ ] Keyboard works everywhere (WAI-ARIA patterns)
- [ ] Clear focus rings (
:focus-visible) - [ ] Manage focus (traps, move, return)
- [ ] Match visual & hit targets (≥24px desktop, ≥44px mobile)
- [ ] Mobile input font ≥16px
- [ ] Never disable zoom
- [ ] Hydration-safe inputs
- [ ] Never block paste
- [ ] Loading buttons show indicator + original label
- [ ] Minimum loading duration (150-300ms delay, 300-500ms min)
- [ ] URL as state (shareable, refreshable)
- [ ] Optimistic updates with rollback
- [ ] Ellipsis for further input & loading states
- [ ] Confirm destructive actions
- [ ]
touch-action: manipulationon controls - [ ] Set
webkit-tap-highlight-color - [ ] Generous hit targets, predictable interactions
- [ ] Delay first tooltip, no delay for peers
- [ ]
overscroll-behavior: containin modals/drawers - [ ] Scroll positions persist on Back/Forward
- [ ] Autofocus single input on desktop (rarely mobile)
- [ ] No dead zones - interactive looks interactive
- [ ] Deep-link everything (filters, tabs, panels)
- [ ] Clean drag interactions (disable selection, use
inert) - [ ] Links are
<a>or<Link>(not buttons/divs) - [ ] Announce async updates (aria-live)
- [ ] Locale-aware keyboard shortcuts
Animations
- [ ] Honor
prefers-reduced-motion - [ ] Prefer CSS > Web Animations API > JS libraries
- [ ] GPU-accelerated properties (
transform,opacity) - [ ] Animate only for clarity or delight
- [ ] Easing fits the subject
- [ ] Interruptible by user input
- [ ] Input-driven, not autoplay
- [ ] Correct transform origin
- [ ] Never
transition: all(explicit properties only) - [ ] SVG transforms on
<g>wrappers withtransform-box: fill-box
Layout
- [ ] Optical alignment (±1px adjustments)
- [ ] Deliberate alignment (grid, baseline, edge, center)
- [ ] Balance contrast in text/icon lockups
- [ ] Test mobile, laptop, ultra-wide
- [ ] Respect safe areas (notches, insets)
- [ ] No excessive scrollbars
- [ ] Let browser size things (flex/grid over JS)
Content
- [ ] Inline help first, tooltips last
- [ ] Stable skeletons mirror final content
- [ ] Accurate
<title>reflects context - [ ] No dead ends (next step or recovery)
- [ ] Design all states (empty, sparse, dense, error)
- [ ] Curly quotes (" ")
- [ ] Avoid widows/orphans
- [ ] Tabular numbers for comparisons
- [ ] Redundant status cues (not color alone)
- [ ] Icons have text labels
- [ ] Accessible names exist even if labels hidden
- [ ] Use ellipsis character
…not... - [ ]
scroll-margin-topon headings - [ ] Handle short, average, long content
- [ ] Locale-aware formats (dates, numbers, currency)
- [ ] Language via
Accept-Language, not IP/GPS - [ ] Accurate
aria-label,aria-hidden - [ ] Icon-only buttons have
aria-label - [ ] Semantics before ARIA (native elements first)
- [ ] Hierarchical
<h1–h6>+ skip link - [ ] Brand resources from logo right-click
- [ ] Non-breaking spaces for glued terms (
,⁠)
Forms
- [ ] Enter submits (single input or last control)
- [ ] Textarea: ⌘/⌃+Enter submits, Enter = new line
- [ ] Every control has
<label>or association - [ ] Label clicks focus control
- [ ] Submit enabled until in-flight (spinner + idempotency key)
- [ ] Don't block typing (validate, don't restrict)
- [ ] Don't pre-disable submit (surface validation)
- [ ] No dead zones on checkboxes/radios
- [ ] Errors next to fields, focus first error on submit
- [ ] Set
autocomplete& meaningfulname - [ ] Disable spellcheck for emails, codes, usernames
- [ ] Correct
type&inputmode - [ ] Placeholders end with ellipsis, show example
- [ ] Warn before navigation with unsaved changes
- [ ] Password manager & 2FA compatible
- [ ] Don't trigger password managers for non-auth
- [ ] Trim trailing whitespace from input
- [ ] Set
background-color&coloron<select>
Performance
- [ ] Test iOS Low Power Mode & macOS Safari
- [ ] Disable extensions when profiling
- [ ] Track re-renders (React DevTools, React Scan)
- [ ] Throttle CPU/network when profiling
- [ ] Minimize layout work (batch reads/writes)
- [ ] POST/PATCH/DELETE <500ms
- [ ] Prefer uncontrolled inputs, cheap controlled loops
- [ ] Virtualize large lists
- [ ] Preload above-fold images, lazy-load rest
- [ ] Set explicit image dimensions (no CLS)
- [ ]
<link rel="preconnect">for asset/CDN domains - [ ] Preload critical fonts
- [ ] Subset fonts (unicode-range)
- [ ] Expensive work in Web Workers
Design
- [ ] Layered shadows (ambient + direct)
- [ ] Crisp borders + shadows
- [ ] Nested radii (child ≤ parent, concentric)
- [ ] Hue consistency on non-neutral backgrounds
- [ ] Color-blind-friendly chart palettes
- [ ] Prefer APCA over WCAG 2 for contrast
- [ ] Interactions increase contrast
- [ ] Set
<meta name="theme-color"> - [ ] Set
color-scheme: darkon<html>for dark themes - [ ] Animate wrapper, not text node (or
translateZ(0)) - [ ] Avoid gradient banding (use background images)
Copywriting (Vercel-specific)
- [ ] Active voice
- [ ] Headings/buttons: Title Case (Chicago)
- [ ] Marketing pages: sentence case
- [ ] Clear & concise
- [ ] Prefer
&overand - [ ] Action-oriented language
- [ ] Keep nouns consistent
- [ ] Second person, no first person
- [ ] Consistent placeholders (
YOUR_API_TOKEN_HERE,0123456789) - [ ] Numerals for counts
- [ ] Consistent currency (0 or 2 decimals, never mix)
- [ ] Space between numbers & units (
10 MB, use ) - [ ] Default to positive language
- [ ] Error messages guide the exit
- [ ] Avoid ambiguity (specific labels)
Top comments (0)