name: designing-forms description: Designs and builds web forms with correct labels, input types, autocomplete attributes, inline validation timing, error messaging, and layout — including multi-step forms and submit states. Use when the user asks to create or improve a form, sign-up or checkout flow, add form validation or error messages, fix form UX, or build a multi-step wizard. Do not use for backend validation logic or for general accessibility audits (ensuring-accessibility).
Designing Forms
When to use / when NOT to use
- Use for: creating or improving forms — field markup, labels, validation timing, error messages, layout, submit states, multi-step flows.
- Do NOT use for: server-side validation rules or business logic, and full WCAG audits (ensuring-accessibility) — though forms built here must already follow its basics.
Core rules
-
Every field has a visible
<label>; placeholder never replaces it. Placeholders are optional format hints only.- ✅
<label for="phone">Phone</label><input id="phone" placeholder="514-555-0100"> - ❌
<input placeholder="Phone">
- ✅
-
Use the right
typeandautocompleteso mobile keyboards and autofill work:type="email" autocomplete="email",type="tel" autocomplete="tel",autocomplete="given-name","postal-code","cc-number", etc. -
Validation timing: validate on blur; after a field first errors, re-validate on every input. Never validate on every keystroke of an untouched field, never only on submit.
-
Error messages are specific and adjacent: placed next to the field, saying what's wrong AND how to fix it, wired with
aria-describedby+aria-invalid="true".- ✅ "Enter an email address with an @, like name@example.com."
- ❌ "Invalid input."
-
Single-column layout. Related short fields (city/postal code) may share a row; everything else stacks. Group related fields with
<fieldset><legend>. -
Ask for the minimum. Every field must justify itself; mark the exception ("optional") rather than decorating everything with asterisks when most fields are required.
-
Submit button states: descriptive label ("Create account", not "Submit"); disable only during submission with a pending indicator; on failure re-enable and show a summarized error (
role="alert") that links to the first invalid field. -
Multi-step forms: one topic per step, visible progress ("Step 2 of 4"), Back never loses data, validate per-step, final review step before irreversible submission.
Workflow
- List the data actually needed; cut or mark-optional everything else (rule 6).
- Write the markup: label + correct type/autocomplete per field, fieldsets for groups, single-column layout.
- Implement validation with rule-3 timing and rule-4 messages (constraint attributes first —
required,minlength,pattern— JS only on top). - Wire submit states (rule 7); for multi-step flows apply rule 8.
- Error-state walkthrough: submit empty, then fix fields one by one — each error appears next to its field, is announced (aria wiring), disappears on fix, and focus lands on the first invalid field after a failed submit.
- Autofill test: trigger browser autofill; every field must fill correctly (wrong fills = wrong
autocompletevalues).
Edge cases & failure modes
- Password fields: allow paste, provide show/hide toggle, state the rules up front (not only as errors),
autocomplete="new-password"vs"current-password". - Server-side failure after client-side pass → show a
role="alert"summary at the top with per-field errors re-injected; never lose the user's input. - Select with >10 options → use a searchable combobox or grouped options; >2–4 radio options → use a select.
- Date inputs:
type="date"unless the design demands a custom picker; always allow keyboard typing. - Names, addresses, phone numbers vary globally → no restrictive patterns (e.g., don't reject accents or 5+ digit postal codes) unless the business rule is explicit.
References
Copy-paste patterns (full field markup, validation JS, error summary, multi-step skeleton): see references/patterns.md