web-interface-guidelines
Web Interface Guidelines
Concise rules for building accessible, fast, delightful UIs. Use MUST/SHOULD/NEVER to guide decisions and reviews.
How to apply
- Prioritize MUST items first; NEVER items are hard constraints.
- When reviewing, scan each section and confirm every MUST is satisfied or consciously justified.
- If a guideline conflicts with product requirements, surface the conflict explicitly.
Quick reference
| Area | Top MUST checks |
|---|---|
| Keyboard | Full keyboard support per WAI-ARIA APG; visible focus rings; manage focus (trap/move/return) |
| Forms | No paste blocking; loading buttons keep label + spinner; inline errors + focus first error |
| Navigation | URL reflects state; links are links; back/forward restores scroll |
| Motion | Respect prefers-reduced-motion; animate transform/opacity only; animations interruptible |
| Layout | Respect safe areas; avoid unwanted scrollbars; verify mobile/laptop/ultra-wide |
| Content | Accurate labels; skip link + proper headings; resilient to long user content |
| Performance | Measure reliably; batch layout reads/writes; prevent CLS from images |
Interactions
- Keyboard
- MUST: Full keyboard support per WAI-ARIA APG patterns
- MUST: Visible focus rings (
:focus-visible; group with:focus-within) - MUST: Manage focus (trap, move, and return) per APG patterns
- Targets & input
- MUST: Hit target >= 24px (mobile >= 44px). If visual < 24px, expand hit area.
- MUST: Mobile
<input>font-size >= 16px or set:<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"> - NEVER: Disable browser zoom
- MUST:
touch-action: manipulationto prevent double-tap zoom; set-webkit-tap-highlight-colorto match design
- Inputs & forms (behavior)
- MUST: Hydration-safe inputs (no lost focus/value)
- NEVER: Block paste in
<input>/<textarea> - MUST: Loading buttons show spinner and keep original label
- MUST: Enter submits focused text input. In
<textarea>, Cmd/Ctrl+Enter submits; Enter adds newline. - MUST: Keep submit enabled until request starts; then disable, show spinner, use idempotency key
- MUST: Don’t block typing; accept free text and validate after
- MUST: Allow submitting incomplete forms to surface validation
- MUST: Errors inline next to fields; on submit, focus first error
- MUST:
autocomplete+ meaningfulname; correcttypeandinputmode - SHOULD: Disable spellcheck for emails/codes/usernames
- SHOULD: Placeholders end with ellipsis and show example pattern (e.g.,
+1 (123) 456-7890,sk-012345…) - MUST: Warn on unsaved changes before navigation
- MUST: Compatible with password managers and 2FA; allow pasting one-time codes
- MUST: Trim values to handle text expansion trailing spaces
- MUST: No dead zones on checkboxes/radios; label+control share one generous hit target
- State & navigation
- MUST: URL reflects state (deep-link filters/tabs/pagination/expanded panels). Prefer libs like nuqs.
- MUST: Back/Forward restores scroll
- MUST: Links are links: use
<a>/<Link>for navigation (support Cmd/Ctrl/middle-click)
- Feedback
- SHOULD: Optimistic UI; reconcile on response; on failure show error and rollback or offer Undo
- MUST: Confirm destructive actions or provide Undo window
- MUST: Use polite
aria-livefor toasts/inline validation - SHOULD: Ellipsis (
…) for options that open follow-ups (e.g., "Rename…") and loading states (e.g., "Loading…")
- Touch/drag/scroll
- MUST: Design forgiving interactions (generous targets, clear affordances; avoid finickiness)
- MUST: Delay first tooltip in a group; subsequent peers no delay
- MUST: Intentional
overscroll-behavior: containin modals/drawers - MUST: During drag, disable text selection and set
inerton dragged element/containers - MUST: No “dead-looking” interactive zones—if it looks clickable, it is
- Autofocus
- SHOULD: Autofocus on desktop when there’s a single primary input; rarely on mobile (avoid layout shift)
Animation
- MUST: Honor
prefers-reduced-motion(provide reduced variant) - SHOULD: Prefer CSS > Web Animations API > JS libraries
- MUST: Animate compositor-friendly props (
transform,opacity); avoid layout/repaint props (top/left/width/height) - SHOULD: Animate only to clarify cause/effect or add deliberate delight
- SHOULD: Choose easing to match the change (size/distance/trigger)
- MUST: Animations are interruptible and input-driven (avoid autoplay)
- MUST: Correct
transform-origin(motion starts where it "physically" should)
Layout
- SHOULD: Optical alignment; adjust by +/- 1px when perception beats geometry
- MUST: Deliberate alignment to grid/baseline/edges/optical centers (no accidental placement)
- SHOULD: Balance icon/text lockups (stroke/weight/size/spacing/color)
- MUST: Verify mobile, laptop, ultra-wide (simulate ultra-wide at 50% zoom)
- MUST: Respect safe areas (use
env(safe-area-inset-*)) - MUST: Avoid unwanted scrollbars; fix overflows
Content & accessibility
- SHOULD: Inline help first; tooltips last resort
- MUST: Skeletons mirror final content to avoid layout shift
- MUST:
<title>matches current context - MUST: No dead ends; always offer next step/recovery
- MUST: Design empty/sparse/dense/error states
- SHOULD: Curly quotes (“ ”); avoid widows/orphans
- MUST: Tabular numbers for comparisons (
font-variant-numeric: tabular-numsor a mono like Geist Mono) - MUST: Redundant status cues (not color-only); icons have text labels
- MUST: Don’t ship the schema—visuals may omit labels but accessible names still exist
- MUST: Use the ellipsis character
… - MUST:
scroll-margin-topon headings for anchored links; include a “Skip to content” link; hierarchical<h1–h6> - MUST: Resilient to user-generated content (short/avg/very long)
- MUST: Locale-aware dates/times/numbers/currency
- MUST: Accurate names (
aria-label), decorative elementsaria-hidden, verify in the Accessibility Tree - MUST: Icon-only buttons have descriptive
aria-label - MUST: Prefer native semantics (
button,a,label,table) before ARIA - SHOULD: Right-clicking the nav logo surfaces brand assets
- MUST: Use non-breaking spaces to glue terms:
10 MB,⌘ + K,Vercel SDK
Performance
- SHOULD: Test iOS Low Power Mode and macOS Safari
- MUST: Measure reliably (disable extensions that skew runtime)
- MUST: Track and minimize re-renders (React DevTools/React Scan)
- MUST: Profile with CPU/network throttling
- MUST: Batch layout reads/writes; avoid unnecessary reflows/repaints
- MUST: Mutations (
POST/PATCH/DELETE) target < 500 ms - SHOULD: Prefer uncontrolled inputs; make controlled loops cheap (keystroke cost)
- MUST: Virtualize large lists (e.g., virtua)
- MUST: Preload only above-the-fold images; lazy-load the rest
- MUST: Prevent CLS from images (explicit dimensions or reserved space)
Design
- SHOULD: Layered shadows (ambient + direct)
- SHOULD: Crisp edges via semi-transparent borders + shadows
- SHOULD: Nested radii: child <= parent; concentric
- SHOULD: Hue consistency: tint borders/shadows/text toward bg hue
- MUST: Accessible charts (color-blind-friendly palettes)
- MUST: Meet contrast (prefer APCA over WCAG 2)
- MUST: Increase contrast on
:hover/:active/:focus - SHOULD: Match browser UI to bg
- SHOULD: Avoid gradient banding (use masks when needed)
Example: loading submit button with inline error focus
type Props = {
pending: boolean;
errorId?: string;
};
export function SubmitButton({ pending, errorId }: Props) {
return (
<button
type="submit"
aria-describedby={errorId}
aria-busy={pending}
disabled={pending}
>
{pending ? (
<span aria-live="polite">
<span className="spinner" aria-hidden="true" />
Saving…
</span>
) : (
"Save"
)}
</button>
);
}
Common mistakes
- Missing focus management in dialogs/drawers (no trap/return)
- Blocking paste in inputs or disabling zoom on mobile
- Loading states that replace labels instead of adding a spinner
- Links implemented as buttons (breaks Cmd/Ctrl/middle-click)
- Animating layout properties instead of
transform/opacity - Shipping icons without accessible labels or text alternatives
Red flags
- Keyboard users cannot reach or activate every interactive element
- Forms fail when autofill or password managers are used
- Scroll position is lost on back/forward
- CLS occurs when images load
- Motion plays for users who prefer reduced motion
More from dereknex/skills
web-guidelines
Use when designing, reviewing, or implementing web UIs and you need concrete MUST/SHOULD/NEVER rules for accessibility, interaction patterns, forms, layout, animation, performance, content, or visual design decisions. Also use when asked to "review my UI", "check accessibility", or "audit design".
11animation-designer
Use when the user explicitly requests animations, transitions, or motion design for a web UI.
6frontend-design
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications. Generates creative, polished code that avoids generic AI aesthetics.
6seo
SEO auditing, optimization, and implementation. Use when auditing sites for SEO issues, optimizing content for search, conducting keyword research, implementing technical SEO, or improving organic search performance. For automated crawl-based audits with 230+ rules, use audit-website instead.
5ux-design-tips
Use when asked for UX strategy or behavior guidance on landing pages, onboarding, pricing, CTAs, social proof, personalization, login, permissions, or brand memorability.
5ui-designer
Use when the user provides UI screenshots/mockups and wants a design system extracted or an implementation prompt that matches those references.
5