Web Interface Guidelines
Review UI code according to Vercel Web Interface Guidelines.
When to Use
Use proactively when:
- UI code review requested
- Accessibility (a11y) check requested
- Design audit requested
- UX review requested
- Best practices check requested
Triggers: review UI, check accessibility, audit design, review UX,
check best practices, UI review, accessibility check, design review, UX review,
web guidelines, a11y check
Do NOT use for: backend code, API design, database
Usage
/web-design-guidelines src/components/Button.tsx
/web-design-guidelines "src/**/*.tsx"
/web-design-guidelines # prompt for file
Review Rules
Accessibility
- Icon-only buttons require
aria-label
- Form controls require
<label> or aria-label
- Interactive elements require keyboard handlers (
onKeyDown/onKeyUp)
- Use
<button> for actions, <a>/<Link> for navigation (no <div onClick>)
- Images require
alt (use alt="" for decorative)
- Decorative icons require
aria-hidden="true"
- Async updates (toasts, validation) require
aria-live="polite"
- Prefer semantic HTML over ARIA (
<button>, <a>, <label>, <table>)
- Maintain heading hierarchy
<h1>–<h6>; include skip link to main content
- Anchor headings require
scroll-margin-top
Focus States
- Interactive elements require visible focus:
focus-visible:ring-* or equivalent
- Using
outline-none / outline: none requires alternative focus indicator
- Use
:focus-visible over :focus (prevents focus ring on click)
- Use
:focus-within on compound controls for group focus
Forms
- Inputs require
autocomplete and meaningful name
- Use correct
type (email, tel, url, number) and inputmode
- Never block paste (
onPaste + preventDefault)
- Labels must be clickable (
htmlFor or wrap control)
- Disable spellcheck for email, code, username (
spellCheck={false})
- Checkbox/Radio: label + control = single hit target (no dead zone)
- Submit button active until request starts; show spinner during request
- Errors shown inline next to field; focus first error on submit
- Placeholders end with
… and show example pattern
- Use
autocomplete="off" on non-auth fields (prevent password manager trigger)
- Warn on navigation with unsaved changes (
beforeunload or router guard)
Animation
- Respect
prefers-reduced-motion (provide reduced version or disable)
- Animate only
transform/opacity (compositor-friendly)
- Forbid
transition: all—list properties explicitly
- Set correct
transform-origin
- SVG: transforms on
<g> wrapper, transform-box: fill-box; transform-origin: center
- Animations must be interruptible—respond to user input during animation
Typography
- Use
… instead of ...
- Use curly quotes
" " instead of straight "
- Non-breaking spaces:
10 MB, ⌘ K, brand names
- Loading states end with
…: "Loading…", "Saving…"
- Numeric columns/comparisons:
font-variant-numeric: tabular-nums
- Headings:
text-wrap: balance or text-pretty (prevent widows)
Content Handling
- Text containers handle long content:
truncate, line-clamp-*, or break-words
- Flex children need
min-w-0 (allow text truncation)
- Handle empty states—no broken UI for empty strings/arrays
- User-generated content: expect short, average, very long inputs
Images
<img> requires explicit width and height (prevent CLS)
- Below-fold images:
loading="lazy"
- Above-fold critical images:
priority or fetchpriority="high"
Performance
- Large lists (>50 items): virtualize (
virtua, content-visibility: auto)
- Never read layout in render (
getBoundingClientRect, offsetHeight, etc.)
- Batch DOM reads/writes; avoid interleaving
- Prefer uncontrolled inputs; controlled inputs must be cheap per keystroke
- Add
<link rel="preconnect"> for CDN/asset domains
- Critical fonts:
<link rel="preload" as="font"> with font-display: swap
Navigation & State
- URL reflects state—filters, tabs, pagination, expanded panels in query params
- Links use
<a>/<Link> (support Cmd/Ctrl+click, middle-click)
- Deep-link all state UI (consider nuqs etc. to sync URL when using
useState)
- Destructive actions require confirmation modal or undo window—no instant execution
Touch & Interaction
touch-action: manipulation (prevent double-tap zoom delay)
- Set
-webkit-tap-highlight-color intentionally
- Modals/drawers/sheets:
overscroll-behavior: contain
- During drag: disable text selection,
inert on dragged element
- Use
autoFocus carefully—desktop only, single primary input; avoid on mobile
Safe Areas & Layout
- Full-bleed layouts require
env(safe-area-inset-*) for notch
- Prevent unwanted scrollbars:
overflow-x-hidden on container, fix content overflow
- Prefer flex/grid over JS measurement
Dark Mode & Theming
- Dark theme requires
color-scheme: dark on <html> (fixes scrollbar, inputs)
<meta name="theme-color"> matches page background
- Native
<select>: explicit background-color and color (Windows dark mode)
Locale & i18n
- Dates/times: use
Intl.DateTimeFormat instead of hardcoded format
- Numbers/currency: use
Intl.NumberFormat instead of hardcoded format
- Language detection:
Accept-Language / navigator.languages instead of IP
Hydration Safety
- Input with
value requires onChange (or use defaultValue for uncontrolled)
- Date/time rendering: prevent hydration mismatch (server vs client)
- Use
suppressHydrationWarning only where truly needed
Hover & Interactive States
- Buttons/links require
hover: state (visual feedback)
- Interactive states increase contrast: hover/active/focus more visible than rest
Content & Copy
- Active voice: "Install the CLI" not "The CLI will be installed"
- Title Case for headings/buttons (Chicago style)
- Numerals for counts: "8 deployments" not "eight"
- Specific button labels: "Save API Key" not "Continue"
- Error messages include fix/next step, not just problem
- Use second person; avoid first person
- Use
& over "and" when space-constrained
Anti-patterns - Must Flag
user-scalable=no or maximum-scale=1 (disables zoom)
onPaste with preventDefault
transition: all
outline-none without focus-visible alternative
- Inline
onClick navigation without <a>
<div> or <span> with click handler (must use <button>)
- Images without dimensions
- Large array
.map() without virtualization
- Form inputs without labels
- Icon buttons without
aria-label
- Hardcoded date/number format (must use
Intl.*)
autoFocus without clear justification
AI Slop Detection - Hard Reject
AI-generated UI that looks generic must be flagged and reworked. These patterns indicate low-effort AI output:
Visual Anti-patterns (flag any occurrence)
- Purple-to-blue gradient as primary accent with no brand justification
- 3-column icon grid layout (icon + heading + paragraph x3)
- Icons inside colored circles as section markers
- "Hero image + 3 feature cards" cookie-cutter layout
- Generic stock photo placeholders or AI-generated hero images
- Overly uniform border-radius on everything (all-8px syndrome)
- Gradient CTA buttons floating in white space
- Cookie-cutter testimonial carousel (avatar + quote + name)
- Decorative blobs/mesh gradients as background filler
- "Dashboard" UI with identical stat cards in a row
Hard Rejection Criteria (immediate rework required)
- Layout is indistinguishable from a SaaS landing page template
- Color palette matches default Tailwind purple/indigo/violet without customization
- Every section follows identical padding/spacing rhythm with no visual hierarchy
- Illustrations or icons are all from the same generic set without style adaptation
- Typography uses only one weight with no clear heading/body distinction
- No brand-specific design tokens — all values are framework defaults
- Page could belong to any product — zero domain-specific visual identity
Output Format
Group by file. Use file:line format (VS Code clickable). Concise findings.
## src/Button.tsx
src/Button.tsx:42 - icon button missing aria-label
src/Button.tsx:18 - input lacks label
src/Button.tsx:55 - animation missing prefers-reduced-motion
src/Button.tsx:67 - transition: all → list properties
## src/Modal.tsx
src/Modal.tsx:12 - missing overscroll-behavior: contain
src/Modal.tsx:34 - "..." → "…"
## src/Card.tsx
✓ pass
State issue + location. Skip explanation if fix is obvious. No preamble.
Related Skills
/react-best-practices - React/Next.js performance optimization
/frontend-design - Frontend UI generation
1---2name: web-design-guidelines3description: UI code review against Vercel's Web Interface Guidelines (100+ accessibility/performance/UX rules). Use for UI, a11y, or UX reviews of frontend code — not for backend or API design.4---56# Web Interface Guidelines78Review UI code according to Vercel Web Interface Guidelines.910## When to Use1112Use proactively when:13- UI code review requested14- Accessibility (a11y) check requested15- Design audit requested16- UX review requested17- Best practices check requested1819Triggers: review UI, check accessibility, audit design, review UX,20check best practices, UI review, accessibility check, design review, UX review,21web guidelines, a11y check2223Do NOT use for: backend code, API design, database2425## Usage2627```bash28/web-design-guidelines src/components/Button.tsx29/web-design-guidelines "src/**/*.tsx"30/web-design-guidelines # prompt for file31```3233## Review Rules3435### Accessibility3637- Icon-only buttons require `aria-label`38- Form controls require `<label>` or `aria-label`39- Interactive elements require keyboard handlers (`onKeyDown`/`onKeyUp`)40- Use `<button>` for actions, `<a>`/`<Link>` for navigation (no `<div onClick>`)41- Images require `alt` (use `alt=""` for decorative)42- Decorative icons require `aria-hidden="true"`43- Async updates (toasts, validation) require `aria-live="polite"`44- Prefer semantic HTML over ARIA (`<button>`, `<a>`, `<label>`, `<table>`)45- Maintain heading hierarchy `<h1>`–`<h6>`; include skip link to main content46- Anchor headings require `scroll-margin-top`4748### Focus States4950- Interactive elements require visible focus: `focus-visible:ring-*` or equivalent51- Using `outline-none` / `outline: none` requires alternative focus indicator52- Use `:focus-visible` over `:focus` (prevents focus ring on click)53- Use `:focus-within` on compound controls for group focus5455### Forms5657- Inputs require `autocomplete` and meaningful `name`58- Use correct `type` (`email`, `tel`, `url`, `number`) and `inputmode`59- Never block paste (`onPaste` + `preventDefault`)60- Labels must be clickable (`htmlFor` or wrap control)61- Disable spellcheck for email, code, username (`spellCheck={false}`)62- Checkbox/Radio: label + control = single hit target (no dead zone)63- Submit button active until request starts; show spinner during request64- Errors shown inline next to field; focus first error on submit65- Placeholders end with `…` and show example pattern66- Use `autocomplete="off"` on non-auth fields (prevent password manager trigger)67- Warn on navigation with unsaved changes (`beforeunload` or router guard)6869### Animation7071- Respect `prefers-reduced-motion` (provide reduced version or disable)72- Animate only `transform`/`opacity` (compositor-friendly)73- Forbid `transition: all`—list properties explicitly74- Set correct `transform-origin`75- SVG: transforms on `<g>` wrapper, `transform-box: fill-box; transform-origin: center`76- Animations must be interruptible—respond to user input during animation7778### Typography7980- Use `…` instead of `...`81- Use curly quotes `"` `"` instead of straight `"`82- Non-breaking spaces: `10 MB`, `⌘ K`, brand names83- Loading states end with `…`: `"Loading…"`, `"Saving…"`84- Numeric columns/comparisons: `font-variant-numeric: tabular-nums`85- Headings: `text-wrap: balance` or `text-pretty` (prevent widows)8687### Content Handling8889- Text containers handle long content: `truncate`, `line-clamp-*`, or `break-words`90- Flex children need `min-w-0` (allow text truncation)91- Handle empty states—no broken UI for empty strings/arrays92- User-generated content: expect short, average, very long inputs9394### Images9596- `<img>` requires explicit `width` and `height` (prevent CLS)97- Below-fold images: `loading="lazy"`98- Above-fold critical images: `priority` or `fetchpriority="high"`99100### Performance101102- Large lists (>50 items): virtualize (`virtua`, `content-visibility: auto`)103- Never read layout in render (`getBoundingClientRect`, `offsetHeight`, etc.)104- Batch DOM reads/writes; avoid interleaving105- Prefer uncontrolled inputs; controlled inputs must be cheap per keystroke106- Add `<link rel="preconnect">` for CDN/asset domains107- Critical fonts: `<link rel="preload" as="font">` with `font-display: swap`108109### Navigation & State110111- URL reflects state—filters, tabs, pagination, expanded panels in query params112- Links use `<a>`/`<Link>` (support Cmd/Ctrl+click, middle-click)113- Deep-link all state UI (consider nuqs etc. to sync URL when using `useState`)114- Destructive actions require confirmation modal or undo window—no instant execution115116### Touch & Interaction117118- `touch-action: manipulation` (prevent double-tap zoom delay)119- Set `-webkit-tap-highlight-color` intentionally120- Modals/drawers/sheets: `overscroll-behavior: contain`121- During drag: disable text selection, `inert` on dragged element122- Use `autoFocus` carefully—desktop only, single primary input; avoid on mobile123124### Safe Areas & Layout125126- Full-bleed layouts require `env(safe-area-inset-*)` for notch127- Prevent unwanted scrollbars: `overflow-x-hidden` on container, fix content overflow128- Prefer flex/grid over JS measurement129130### Dark Mode & Theming131132- Dark theme requires `color-scheme: dark` on `<html>` (fixes scrollbar, inputs)133- `<meta name="theme-color">` matches page background134- Native `<select>`: explicit `background-color` and `color` (Windows dark mode)135136### Locale & i18n137138- Dates/times: use `Intl.DateTimeFormat` instead of hardcoded format139- Numbers/currency: use `Intl.NumberFormat` instead of hardcoded format140- Language detection: `Accept-Language` / `navigator.languages` instead of IP141142### Hydration Safety143144- Input with `value` requires `onChange` (or use `defaultValue` for uncontrolled)145- Date/time rendering: prevent hydration mismatch (server vs client)146- Use `suppressHydrationWarning` only where truly needed147148### Hover & Interactive States149150- Buttons/links require `hover:` state (visual feedback)151- Interactive states increase contrast: hover/active/focus more visible than rest152153### Content & Copy154155- Active voice: "Install the CLI" not "The CLI will be installed"156- Title Case for headings/buttons (Chicago style)157- Numerals for counts: "8 deployments" not "eight"158- Specific button labels: "Save API Key" not "Continue"159- Error messages include fix/next step, not just problem160- Use second person; avoid first person161- Use `&` over "and" when space-constrained162163## Anti-patterns - Must Flag164165- `user-scalable=no` or `maximum-scale=1` (disables zoom)166- `onPaste` with `preventDefault`167- `transition: all`168- `outline-none` without focus-visible alternative169- Inline `onClick` navigation without `<a>`170- `<div>` or `<span>` with click handler (must use `<button>`)171- Images without dimensions172- Large array `.map()` without virtualization173- Form inputs without labels174- Icon buttons without `aria-label`175- Hardcoded date/number format (must use `Intl.*`)176- `autoFocus` without clear justification177178## AI Slop Detection - Hard Reject179180AI-generated UI that looks generic must be flagged and reworked. These patterns indicate low-effort AI output:181182### Visual Anti-patterns (flag any occurrence)183- Purple-to-blue gradient as primary accent with no brand justification184- 3-column icon grid layout (icon + heading + paragraph x3)185- Icons inside colored circles as section markers186- "Hero image + 3 feature cards" cookie-cutter layout187- Generic stock photo placeholders or AI-generated hero images188- Overly uniform border-radius on everything (all-8px syndrome)189- Gradient CTA buttons floating in white space190- Cookie-cutter testimonial carousel (avatar + quote + name)191- Decorative blobs/mesh gradients as background filler192- "Dashboard" UI with identical stat cards in a row193194### Hard Rejection Criteria (immediate rework required)195- Layout is indistinguishable from a SaaS landing page template196- Color palette matches default Tailwind purple/indigo/violet without customization197- Every section follows identical padding/spacing rhythm with no visual hierarchy198- Illustrations or icons are all from the same generic set without style adaptation199- Typography uses only one weight with no clear heading/body distinction200- No brand-specific design tokens — all values are framework defaults201- Page could belong to any product — zero domain-specific visual identity202203## Output Format204205Group by file. Use `file:line` format (VS Code clickable). Concise findings.206207```text208## src/Button.tsx209210src/Button.tsx:42 - icon button missing aria-label211src/Button.tsx:18 - input lacks label212src/Button.tsx:55 - animation missing prefers-reduced-motion213src/Button.tsx:67 - transition: all → list properties214215## src/Modal.tsx216217src/Modal.tsx:12 - missing overscroll-behavior: contain218src/Modal.tsx:34 - "..." → "…"219220## src/Card.tsx221222✓ pass223```224225State issue + location. Skip explanation if fix is obvious. No preamble.226227## Related Skills228229- `/react-best-practices` - React/Next.js performance optimization230- `/frontend-design` - Frontend UI generation