Interface: Forms

Rule

Input Types

  • MUSTCorrect type for keyboard/validation: email, tel, url, number, search, password

Input Attributes

  • MUSTautocomplete + meaningful name for login/address forms
  • SHOULDspellcheck="false" for emails, codes, usernames
  • SHOULDdata-1p-ignore to suppress password manager icons where unwanted
<input type="text" spellcheck="false" autocomplete="off" data-1p-ignore />

Input Decorations

  • SHOULDPosition icons absolutely inside input with padding offset
  • MUSTClickable icons trigger input focus
<div className="relative">
  <SearchIcon className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-gray-400" />
  <input className="pl-10 ..." />
</div>

iOS

  • MUSTtext-base minimum on mobile inputs (16px prevents zoom on focus)

Autofocus

  • SHOULDAutofocus primary input on desktop
  • NEVERAutofocus on touch devices (opens keyboard unexpectedly)
<input autoFocus={!('ontouchstart' in window)} />

Form Behavior

  • MUSTWrap inputs in <form> for Enter submission
  • MUST⌘/Ctrl+Enter submits <textarea>; Enter adds newline
  • MUSTKeep submit enabled until request starts, then disable + show spinner + keep original label
  • MUSTUse idempotency keys on submit to prevent duplicate requests
  • MUSTAccept free text input; validate after, don't block typing
  • MUSTAllow submitting incomplete forms to surface validation errors
  • MUSTWarn on unsaved changes before navigation
  • MUSTAllow pasting (never block paste)
  • MUSTTrim whitespace from values
  • MUSTHydration-safe inputs — no lost focus or value after hydration
  • MUSTCompatible with password managers and 2FA; allow pasting one-time codes
  • SHOULDPrefill with user data when available
  • SHOULDPlaceholder ends with ellipsis: Search…, sk-012345…

Buttons

  • MUSTDisable after submission to prevent duplicates
  • SHOULDShow keyboard shortcut in tooltip: Save (⌘S)
  • SHOULDactive:scale-[0.97] for press feedback
<button className="active:scale-[0.97] transition-transform">Submit</button>

Checkboxes/Radios

  • MUSTNo dead zones — entire row clickable
<label className="flex items-center gap-2 cursor-pointer">
  <input type="checkbox" />
  <span>Remember me</span>
</label>

Validation

  • MUSTUse aria-invalid on invalid inputs
<input aria-invalid={!!error} className={error ? "border-red-500" : ""} />
{error && <span className="text-sm text-red-500">{error}</span>}

Destructive Actions

  • MUSTRequire confirmation (use AlertDialog, not confirm())