Web Interface Guidelines
Third-party skill (upstream-maintained). See ATTRIBUTION.md.
Upstream: vercel-labs/web-interface-guidelines · vercel.com/design/guidelines · Local name: thirdparty-web-interface-guidelines
Review target files for compliance. Read files, check against rules below. Output concise but comprehensive—sacrifice grammar for brevity. High signal-to-noise.
When the user provides specific files or patterns, review those. Otherwise ask which files to review.
Rules
Accessibility
- Icon-only buttons need
aria-label
- Form controls need
<label> or aria-label
- Interactive elements need keyboard handlers (
onKeyDown/onKeyUp)
<button> for actions, <a>/<Link> for navigation (not <div onClick>)
- Images need
alt (or alt="" if decorative)
- Decorative icons need
aria-hidden="true"
- Async updates (toasts, validation) need
aria-live="polite"
- Use semantic HTML (
<button>, <a>, <label>, <table>) before ARIA
- Headings hierarchical
<h1>–<h6>; include skip link for main content
scroll-margin-top on heading anchors
Focus States
- Interactive elements need visible focus:
focus-visible:ring-* or equivalent
- Never
outline-none / outline: none without focus replacement
- Use
:focus-visible over :focus (avoid focus ring on click)
- Group focus with
:focus-within for compound controls
Forms
- Inputs need
autocomplete and meaningful name
- Use correct
type (email, tel, url, number) and inputmode
- Never block paste (
onPaste + preventDefault)
- Labels clickable (
htmlFor or wrapping control)
- Disable spellcheck on emails, codes, usernames (
spellCheck={false})
- Checkboxes/radios: label + control share single hit target (no dead zones)
- Submit button stays enabled until request starts; spinner during request
- Errors inline next to fields; focus first error on submit
- Placeholders end with
… and show example pattern
autocomplete="off" on non-auth fields to avoid password manager triggers
- Warn before navigation with unsaved changes (
beforeunload or router guard)
Animation
- Honor
prefers-reduced-motion (provide reduced variant or disable)
- Animate
transform/opacity only (compositor-friendly)
- Never
transition: all—list properties explicitly
- Set correct
transform-origin
- SVG: transforms on
<g> wrapper with transform-box: fill-box; transform-origin: center
- Animations interruptible—respond to user input mid-animation
Typography
… not ...
- Curly quotes
" " not straight "
- Non-breaking spaces:
10 MB, ⌘ K, brand names
- Loading states end with
…: "Loading…", "Saving…"
font-variant-numeric: tabular-nums for number columns/comparisons
- Use
text-wrap: balance or text-pretty on headings (prevents widows)
Content Handling
- Text containers handle long content:
truncate, line-clamp-*, or break-words
- Flex children need
min-w-0 to allow text truncation
- Handle empty states—don't render broken UI for empty strings/arrays
- User-generated content: anticipate short, average, and very long inputs
Images
<img> needs explicit width and height (prevents CLS)
- Below-fold images:
loading="lazy"
- Above-fold critical images:
priority or fetchpriority="high"
Performance
- Large lists (>50 items): virtualize (
virtua, content-visibility: auto)
- No layout reads in render (
getBoundingClientRect, offsetHeight, offsetWidth, scrollTop)
- 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> (Cmd/Ctrl+click, middle-click support)
- Deep-link all stateful UI (if uses
useState, consider URL sync via nuqs or similar)
- Destructive actions need confirmation modal or undo window—never immediate
Touch & Interaction
touch-action: manipulation (prevents double-tap zoom delay)
-webkit-tap-highlight-color set intentionally
overscroll-behavior: contain in modals/drawers/sheets
- During drag: disable text selection,
inert on dragged elements
autoFocus sparingly—desktop only, single primary input; avoid on mobile
Safe Areas & Layout
- Full-bleed layouts need
env(safe-area-inset-*) for notches
- Avoid unwanted scrollbars:
overflow-x-hidden on containers, fix content overflow
- Flex/grid over JS measurement for layout
Dark Mode & Theming
color-scheme: dark on <html> for dark themes (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 not hardcoded formats
- Numbers/currency: use
Intl.NumberFormat not hardcoded formats
- Detect language via
Accept-Language / navigator.languages, not IP
- Brand names, code tokens, identifiers: wrap with
translate="no" to prevent garbled auto-translation
Hydration Safety
- Inputs with
value need onChange (or use defaultValue for uncontrolled)
- Date/time rendering: guard against hydration mismatch (server vs client)
suppressHydrationWarning only where truly needed
Hover & Interactive States
- Buttons/links need
hover: state (visual feedback)
- Interactive states increase contrast: hover/active/focus more prominent 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
- Second person; avoid first person
& over "and" where space-constrained
Anti-patterns (flag these)
user-scalable=no or maximum-scale=1 disabling zoom
onPaste with preventDefault
transition: all
outline-none without focus-visible replacement
- Inline
onClick navigation without <a>
<div> or <span> with click handlers (should be <button>)
- Images without dimensions
- Large arrays
.map() without virtualization
- Form inputs without labels
- Icon buttons without
aria-label
- Hardcoded date/number formats (use
Intl.*)
autoFocus without clear justification
Output Format
Group by file. Use file:line format (VS Code clickable). Terse 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 unless fix non-obvious. No preamble.
Source: vercel-labs/web-interface-guidelines · Vercel Web Interface Guidelines. Installed in cursor_kenji as thirdparty-web-interface-guidelines. See ATTRIBUTION.md.
1---2name: thirdparty-web-interface-guidelines3description: Third-party skill — Vercel Web Interface Guidelines compliance (focus, forms, animation, copy). Use when the user names Vercel guidelines, web interface guidelines, or /thirdparty-web-interface-guidelines. Generic a11y → audit-accessibility.4---56# Web Interface Guidelines78> **Third-party skill** (upstream-maintained). See [ATTRIBUTION.md](ATTRIBUTION.md). 9> Upstream: [vercel-labs/web-interface-guidelines](https://github.com/vercel-labs/web-interface-guidelines) · [vercel.com/design/guidelines](https://vercel.com/design/guidelines) · Local name: `thirdparty-web-interface-guidelines`1011Review target files for compliance. Read files, check against rules below. Output concise but comprehensive—sacrifice grammar for brevity. High signal-to-noise.1213When the user provides specific files or patterns, review those. Otherwise ask which files to review.1415## Rules1617### Accessibility1819- Icon-only buttons need `aria-label`20- Form controls need `<label>` or `aria-label`21- Interactive elements need keyboard handlers (`onKeyDown`/`onKeyUp`)22- `<button>` for actions, `<a>`/`<Link>` for navigation (not `<div onClick>`)23- Images need `alt` (or `alt=""` if decorative)24- Decorative icons need `aria-hidden="true"`25- Async updates (toasts, validation) need `aria-live="polite"`26- Use semantic HTML (`<button>`, `<a>`, `<label>`, `<table>`) before ARIA27- Headings hierarchical `<h1>`–`<h6>`; include skip link for main content28- `scroll-margin-top` on heading anchors2930### Focus States3132- Interactive elements need visible focus: `focus-visible:ring-*` or equivalent33- Never `outline-none` / `outline: none` without focus replacement34- Use `:focus-visible` over `:focus` (avoid focus ring on click)35- Group focus with `:focus-within` for compound controls3637### Forms3839- Inputs need `autocomplete` and meaningful `name`40- Use correct `type` (`email`, `tel`, `url`, `number`) and `inputmode`41- Never block paste (`onPaste` + `preventDefault`)42- Labels clickable (`htmlFor` or wrapping control)43- Disable spellcheck on emails, codes, usernames (`spellCheck={false}`)44- Checkboxes/radios: label + control share single hit target (no dead zones)45- Submit button stays enabled until request starts; spinner during request46- Errors inline next to fields; focus first error on submit47- Placeholders end with `…` and show example pattern48- `autocomplete="off"` on non-auth fields to avoid password manager triggers49- Warn before navigation with unsaved changes (`beforeunload` or router guard)5051### Animation5253- Honor `prefers-reduced-motion` (provide reduced variant or disable)54- Animate `transform`/`opacity` only (compositor-friendly)55- Never `transition: all`—list properties explicitly56- Set correct `transform-origin`57- SVG: transforms on `<g>` wrapper with `transform-box: fill-box; transform-origin: center`58- Animations interruptible—respond to user input mid-animation5960### Typography6162- `…` not `...`63- Curly quotes `"` `"` not straight `"`64- Non-breaking spaces: `10 MB`, `⌘ K`, brand names65- Loading states end with `…`: `"Loading…"`, `"Saving…"`66- `font-variant-numeric: tabular-nums` for number columns/comparisons67- Use `text-wrap: balance` or `text-pretty` on headings (prevents widows)6869### Content Handling7071- Text containers handle long content: `truncate`, `line-clamp-*`, or `break-words`72- Flex children need `min-w-0` to allow text truncation73- Handle empty states—don't render broken UI for empty strings/arrays74- User-generated content: anticipate short, average, and very long inputs7576### Images7778- `<img>` needs explicit `width` and `height` (prevents CLS)79- Below-fold images: `loading="lazy"`80- Above-fold critical images: `priority` or `fetchpriority="high"`8182### Performance8384- Large lists (>50 items): virtualize (`virtua`, `content-visibility: auto`)85- No layout reads in render (`getBoundingClientRect`, `offsetHeight`, `offsetWidth`, `scrollTop`)86- Batch DOM reads/writes; avoid interleaving87- Prefer uncontrolled inputs; controlled inputs must be cheap per keystroke88- Add `<link rel="preconnect">` for CDN/asset domains89- Critical fonts: `<link rel="preload" as="font">` with `font-display: swap`9091### Navigation & State9293- URL reflects state—filters, tabs, pagination, expanded panels in query params94- Links use `<a>`/`<Link>` (Cmd/Ctrl+click, middle-click support)95- Deep-link all stateful UI (if uses `useState`, consider URL sync via nuqs or similar)96- Destructive actions need confirmation modal or undo window—never immediate9798### Touch & Interaction99100- `touch-action: manipulation` (prevents double-tap zoom delay)101- `-webkit-tap-highlight-color` set intentionally102- `overscroll-behavior: contain` in modals/drawers/sheets103- During drag: disable text selection, `inert` on dragged elements104- `autoFocus` sparingly—desktop only, single primary input; avoid on mobile105106### Safe Areas & Layout107108- Full-bleed layouts need `env(safe-area-inset-*)` for notches109- Avoid unwanted scrollbars: `overflow-x-hidden` on containers, fix content overflow110- Flex/grid over JS measurement for layout111112### Dark Mode & Theming113114- `color-scheme: dark` on `<html>` for dark themes (fixes scrollbar, inputs)115- `<meta name="theme-color">` matches page background116- Native `<select>`: explicit `background-color` and `color` (Windows dark mode)117118### Locale & i18n119120- Dates/times: use `Intl.DateTimeFormat` not hardcoded formats121- Numbers/currency: use `Intl.NumberFormat` not hardcoded formats122- Detect language via `Accept-Language` / `navigator.languages`, not IP123- Brand names, code tokens, identifiers: wrap with `translate="no"` to prevent garbled auto-translation124125### Hydration Safety126127- Inputs with `value` need `onChange` (or use `defaultValue` for uncontrolled)128- Date/time rendering: guard against hydration mismatch (server vs client)129- `suppressHydrationWarning` only where truly needed130131### Hover & Interactive States132133- Buttons/links need `hover:` state (visual feedback)134- Interactive states increase contrast: hover/active/focus more prominent than rest135136### Content & Copy137138- Active voice: "Install the CLI" not "The CLI will be installed"139- Title Case for headings/buttons (Chicago style)140- Numerals for counts: "8 deployments" not "eight"141- Specific button labels: "Save API Key" not "Continue"142- Error messages include fix/next step, not just problem143- Second person; avoid first person144- `&` over "and" where space-constrained145146### Anti-patterns (flag these)147148- `user-scalable=no` or `maximum-scale=1` disabling zoom149- `onPaste` with `preventDefault`150- `transition: all`151- `outline-none` without focus-visible replacement152- Inline `onClick` navigation without `<a>`153- `<div>` or `<span>` with click handlers (should be `<button>`)154- Images without dimensions155- Large arrays `.map()` without virtualization156- Form inputs without labels157- Icon buttons without `aria-label`158- Hardcoded date/number formats (use `Intl.*`)159- `autoFocus` without clear justification160161## Output Format162163Group by file. Use `file:line` format (VS Code clickable). Terse findings.164165```text166## src/Button.tsx167168src/Button.tsx:42 - icon button missing aria-label169src/Button.tsx:18 - input lacks label170src/Button.tsx:55 - animation missing prefers-reduced-motion171src/Button.tsx:67 - transition: all → list properties172173## src/Modal.tsx174175src/Modal.tsx:12 - missing overscroll-behavior: contain176src/Modal.tsx:34 - "..." → "…"177178## src/Card.tsx179180✓ pass181```182183State issue + location. Skip explanation unless fix non-obvious. No preamble.184185---186187**Source:** [vercel-labs/web-interface-guidelines](https://github.com/vercel-labs/web-interface-guidelines) · [Vercel Web Interface Guidelines](https://vercel.com/design/guidelines). Installed in cursor_kenji as `thirdparty-web-interface-guidelines`. See [ATTRIBUTION.md](ATTRIBUTION.md).