Web Interface Guidelines
Interfaces succeed because of hundreds of choices. This is a living, non-exhaustive list of those decisions. Most guidelines are framework-agnostic, some specific to React/Next.js. Feedback is welcome.
Interactions
- Keyboard works everywhere. All flows are keyboard-operable & follow the WAI-ARIA Authoring Patterns.
- Clear focus. Every focusable element shows a visible focus ring. Prefer
:focus-visible
over:focus
to avoid distracting pointer users. Set:focus-within
for grouped controls. - Manage focus. Use focus traps, move & return focus according to the WAI-ARIA Patterns.
- Match visual & hit targets. Exception: if the visual target is < 24px, expand its hit target to ≥ 24px. On mobile, the minimum size is 44px.
- Mobile input size.
<input>
font size is ≥ 16px on mobile to prevent iOS Safari auto-zoom/pan on focus. Or set<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" />
. - Respect zoom. Never disable browser zoom.
- Hydration-safe inputs. Inputs must not lose focus or value after hydration.
- Don’t block paste. Never disable paste in
<input>
or<textarea>
. - Loading buttons. Show a loading indicator & keep the original label.
- URL as state. Persist state in the URL so share, refresh, and Back/Forward navigation work e.g., nuqs.
- Optimistic updates. Update the UI immediately when success is likely; reconcile on server response. On failure, show an error and roll back or provide Undo.
- Ellipsis for further input. Menu options that open a follow-up e.g., “Rename…” end with an ellipsis.
- Confirm destructive actions. Require confirmation or provide Undo with a safe window.
- Prevent double-tap zoom on controls. Set
touch-action: manipulation
. - Tap highlight follows design. Set
webkit-tap-highlight-color
. - Design forgiving interactions. Controls minimize finickiness with generous hit targets, clear affordances, and predictable interactions, i.e., prediction cones.
- Tooltip timing. Delay the first tooltip in a group; subsequent peers have no delay.
- Overscroll behavior. Set
overscroll-behavior: contain
intentionally e.g., in modals/drawers. - Scroll positions persist. Back/Forward restores prior scroll.
- Autofocus for speed. On desktop screens with a single primary input, autofocus. Rarely autofocus on mobile because the keyboard opening can cause layout shift.
- No dead zones. If part of a control looks interactive, it should be interactive. Don’t leave users guessing where to interact.
- Deep-link everything. Filters, tabs, pagination, expanded panels, anytime
useState
is used. - Clean drag interactions. Disable text selection & apply
inert
(which prevents interaction) while an element is dragged so selection/hover don't occur simultaneously. - Links are links. Use
<a>
or<Link>
for navigation so standard browser behaviors work (Cmd/Ctrl+Click, middle-click, right-click to open in a new tab). Never substitute with<button>
or<div>
for navigational links. - Announce async updates. Use polite aria-live for toasts and inline validation.
Animations
- Honor
prefers-reduced-motion
. Provide a reduced-motion variant. - Implementation preference. Prefer CSS, avoid main-thread JS-driven animations when possible.
- Preference: CSS > Web Animations API > Javascript libraries e.g., motion.
- Compositor-friendly. Prioritize GPU-accelerated properties (
transform
,opacity
) & avoid properties that trigger reflows/repaints (width
,height
,top
,left
). - Necessity check. Only animate when it clarifies cause & effect or when it adds deliberate delight e.g., the northern lights.
- Easing fits the subject. Choose easing based on what changes (size, distance, trigger).
- Interruptible. Animations are cancelable by user input.
- Input-driven. Avoid autoplay; animate in response to actions.
- Correct transform origin. Anchor motion to where it “physically” starts.
Layout
- Optical alignment. Adjust ±1px when perception beats geometry.
- Deliberate alignment. Every element aligns with something intentionally whether to a grid, baseline, edge, or optical center. No accidental positioning.
- Balance contrast in lockups. When text & icons sit side by side, adjust weight, size, spacing, or color so they don’t clash. For example, a thin-stroke icon may need a bolder stroke next to medium-weight text.
- Responsive coverage. Verify on mobile, laptop, & ultra-wide. For ultra-wide, zoom out to 50% to simulate.
- Respect safe areas. Account for notches & insets with safe-area variables.
- No excessive scrollbars. Only render useful scrollbars; fix overflow issues to prevent unwanted scrollbars.
Content
- Inline help first. Prefer inline explanations; use tooltips as a last resort.
- Stable skeletons. Skeletons mirror final content exactly to avoid layout shift.
- Accurate page titles.
<title>
reflects the current context. - No dead ends. Every screen offers a next step or recovery path.
- All states designed. Empty, sparse, dense, & error states.
- Typographic quotes. Prefer curly quotes (“ ”) over straight quotes (" ").
- Avoid widows/orphans. Tidy rag & line breaks.
- Tabular numbers for comparisons. Use
font-variant-numeric: tabular-nums
or a monospace like Geist Mono. - Redundant status cues. Don’t rely on color alone; include text labels.
- Icons have labels. Convey the same meaning with text for non-sighted users.
- Don’t ship the schema. Visual layouts may omit visible labels, but accessible names/labels still exist for assistive tech.
- Use the ellipsis character.
…
over three periods...
. - Anchored headings. Set
scroll-margin-top
for headers when linking to sections. - Resilient to user-generated content. Layouts handle short, average, & very long content.
- Locale-aware formats. Format dates, times, numbers, delimiters, & currencies for the user’s locale.
- Accessible content. Set accurate names (
aria-label
), hide decoration (aria-hidden
), and verify in the accessibility tree. - Icon-only buttons are named. Provide a descriptive
aria-label
. - Semantics before ARIA. Prefer native elements (
button
,a
,label
,table
), beforearia-*
. - Headings & skip link. Hierarchical
<h1–h6>
& a “Skip to content” link. - Brand resources from the logo. Right-click the nav logo to surface brand assets for quick access.
- Non-breaking spaces for glued terms. Use a non-breaking space
to keep units, shortcuts, and names together:10 MB
→10 MB
,⌘ + K
→⌘ + K
,Vercel SDK
→Vercel SDK
.
Forms
- Enter submits. When a text input is focused, Enter submits.
- Textarea behavior. In
<textarea>
, ⌘/⌃+Enter submits; Enter inserts a new line. - Labels everywhere. Every control has a
<label>
or is associated with a label for assistive tech. - Label activation. Clicking a
<label>
focuses the associated control. - Submission rule. Keep submit enabled until submission starts; then disable during the in-flight request, show a spinner, & include an idempotency key.
- Don’t block typing. Even if a field only accepts numbers, allow any input & show validation feedback. Blocking keystrokes entirely is confusing because the user gets no explanation.
- Don’t pre-disable submit. Allow submitting incomplete forms to surface validation feedback.
- No dead zones on controls. Checkboxes & radios avoid dead zones; the label & control share a single generous hit target.
- Error placement. Show errors next to their fields; on submit, focus the first error.
- Autocomplete & names. Set
autocomplete
and meaningfulname
values to enable autofill. - Spellcheck selectively. Disable for emails, codes, usernames, etc.
- Correct types & input modes. Use the right
type
&inputmode
for better keyboards & validation. - Placeholders signal emptiness. End with an ellipsis.
- Placeholder value. Set placeholder to an example value or pattern e.g.,
+1 (123) 456-7890
&sk-012345679…
- Unsaved changes. Warn before navigation when data could be lost.
- Password managers and 2FA. Ensure compatibility and allow pasting one-time codes.
- Text replacements and expansions. Some add a trailing whitespace. The input should trim the value to avoid showing a confusing error message.
Performance
- Device/browser matrix. Test iOS Low Power Mode and macOS Safari.
- Measure reliably. Disable extensions that add overhead or change runtime behavior.
- Track re-renders. Minimize & make re-renders fast. Use React DevTools or React Scan.
- Throttle when profiling. Test with CPU and network throttling.
- Minimize layout work. Batch reads/writes; avoid unnecessary reflows/repaints.
- Network latency budgets.
POST/PATCH/DELETE
complete in <500ms. - Keystroke cost. Prefer uncontrolled inputs; make controlled loops cheap.
- Large lists. Virtualize large lists e.g., virtua.
- Preload wisely. Preload only above-the-fold images; lazy-load the rest.
- No image-caused CLS. Set explicit image dimensions and reserve space.
Design
- Layered shadows. Mimic ambient + direct light with at least two layers.
- Crisp borders. Combine borders and shadows; semi-transparent borders improve edge clarity.
- Nested radii. Child radius ≤ parent radius and concentric so curves align.
- Hue consistency. On non-neutral backgrounds, tint borders/shadows/text toward the same hue.
- Accessible charts. Use color-blind-friendly palettes.
- Minimum contrast. Prefer APCA over WCAG 2 for more accurate perceptual contrast.
- Interactions increase contrast.
:hover
,:active
,:focus
have more contrast than rest state. - Browser UI matches your background. Set
<meta name="theme-color" content="#000000">
to align the browser’s theme color with the page background. - Avoid gradient banding. Some colors & display types will have color banding. Masks can be used instead.
Vercel-specific
These preferences reflect Vercel’s brand and product choices. They aren’t universal guidelines.
Copywriting
- Active voice.
- Instead of “The CLI will be installed,” say “Install the CLI.”
- Headings & buttons use Title Case (Chicago). On marketing pages, use sentence case.
- Be clear & concise. Use as few words as possible.
- Prefer
&
overand
. - Action-oriented language.
- Instead of “You will need the CLI…” say “Install the CLI…”.
- Keep nouns consistent. Introduce as few unique terms as possible.
- Write in second person. Avoid first person.
- Use consistent placeholders.
- Strings:
YOUR_API_TOKEN_HERE
. Numbers:0123456789
.
- Strings:
- Use numerals for counts.
- Instead of “eight deployments” say “8 deployments”.
- Consistent currency formatting. In any given context, display currency with either 0 or 2 decimal places, never mix both.
- Separate numbers & units with a space.
- Instead of
10MB
say10 MB
. - Use a non-breaking space e.g.,
10 MB
.
- Instead of
- Default to positive language. Frame messages in an encouraging, problem-solving way, even for errors.
- Instead of “Your deployment failed,” say “Something went wrong—try again or contact support.”
- Error messages guide the exit. Don’t just state what went wrong—tell the user how to fix it.
- Instead of “Invalid API key,” say “Your API key is incorrect or expired. Generate a new key in your account settings.” The copy & buttons/links should educate & give a clear action.
- Avoid ambiguity. Labels are clear & specific.
- Instead of the button label “Continue” say “Save API Key”.
Integrate with Agents
An AGENTS.md file provides guidance for agents. Use this AGENTS.md with your agents to ensure your interfaces follow these guidelines. We recommend auditing all generated interfaces.
Join Vercel
We’re hiring people who live for these details. Check out the job postings.