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
- Disable the button to prevent double-clicks.
- Keep the original label. Add a spinner alongside it.
- On success, restore the button and confirm.
- 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
autocompletevalues. Browser saves email as wrong-site username. onPaste={(e) => e.preventDefault()}on password fields. Breaks managers.text-smon 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"andautocapitalize="off".