Forms

Missing labels, broken autocomplete, punitive validation. A respect problem.

Most form abandonment isn't about the number of fields. It's about the wrong keyboard appearing, autocomplete not firing, or an error message that says "invalid input" instead of what to fix. ## Labels are not optional Every input needs a visible label. Placeholders vanish on typing. - **`<label for="...">`** binds label to input. - **`type="email"`** changes mobile keyboard and enables browser validation. - **`name="email"`** for form submission and password manager matching. - **`autocomplete="email"`** enables autofill. Spec values: `email`, `name`, `current-password`, `new-password`, `street-address`, `tel`, `cc-number`, `one-time-code`. - **`inputmode="email"`** controls keyboard without changing validation. No room for a visible label? Use a visually-hidden one. ## Placeholders are examples, not labels Show the _shape_ of the answer, not a paraphrase of the label: WCAG requires 4.5:1 on placeholder text. Most designs fail this: ## Autocomplete Use exact spec values. `autocomplete="new-password"` triggers password generation. `autocomplete="current-password"` triggers saved credentials. Wrong values mean wrong autofills. Never use `autocomplete="off"` on auth fields. ## `inputmode` for the right keyboard - `inputmode="numeric"`: number pad. Amounts, verification codes. - `inputmode="decimal"`: number pad with decimal. Currency. - `inputmode="tel"`: phone keypad. - `inputmode="email"`: @ and . prominent. - `inputmode="url"`: / and .com. - `inputmode="search"`: search/return key. `type="number"` adds spinners. `inputmode="numeric"` gives the keyboard without spinners. ## Enter-to-submit Single-line forms submit on Enter by default when `<button type="submit">` exists inside `<form>`. React `onClick`-only handlers break this. Multi-line: Enter inserts newline, Cmd/Ctrl+Enter submits. ## Inline errors - **On keystroke:** No. "Email is invalid" while typing is hostile. - **On blur:** Validate when the user leaves the field. - **On submit:** Required-field and cross-field checks. On failure, focus the first error. Be specific: "Email is required", not "Invalid input." Keep submit enabled. Show all errors on click. Disable only during the request. ## Submission state 1. **Disable** the button to prevent double-clicks. 2. **Keep the original label.** Add a spinner alongside it. 3. On success, **restore** the button and confirm. 4. On failure, **restore** the button and surface an actionable error. Don't swap the label to _Saving…_. The button jumps width and translations break. Collect form data first, then disable, then send. Warn on navigation with unsaved changes: ## Mobile font size: the 16px rule iOS Safari zooms the page when input font-size is below 16px. Override on desktop only: Tailwind `text-sm` is 14px. The most common offender. ## Never block paste Validate _after_ paste: ## Common mistakes - **Placeholder as only label.** Vanishes on typing. - **Wrong `autocomplete` values.** Browser saves email as wrong-site username. - **`onPaste={(e) => e.preventDefault()}`** on password fields. Breaks managers. - **`text-sm` on inputs without mobile override.** iOS zooms. - **Disabled submit until form is "complete."** User can't discover missing fields. - **Spellcheck on email/code fields.** Add `spellcheck="false"` and `autocapitalize="off"`.