Forms
Layout, validation and submission rules — extracted from our most conversion-tested checkout.
Structure
- Single page over a multi-step wizard when conversion matters — payment methods visible immediately; accordions hide data and create doubt about what is saved.
- When most fields are required, mark the optional minority with “(optional)” — don’t asterisk everything.
- Placeholders are format hints only (“+420 …”), and only where the format isn’t obvious. Never use a placeholder as the field label.
Submit
- The submit is the surface’s primary (
variant="default"). Placement: full-width on narrow stacked forms; directly right of the input in inline apply rows; in a multi-step flow it lives in the flow’s action bar, not inside the form. - High-stakes submits (payment, binding offers): disabled until valid, with the reasons visible — tooltip listing blockers or a readiness checklist. Simple forms (contact): keep the submit enabled and validate on submit.
- Busy: disable + progressive label (“Sending…”) + spinner when the wait is noticeable.
Validation
- Validate on blur and on submit; clear a field’s error on the first keystroke; synchronous, no debounce.
- On submit with errors: scroll to and focus the first invalid field.
- Error copy is action-oriented and unique per field (“Enter a company name”, not “This field is required”).
- Wire it up:
aria-invalid+aria-describedbyon the field,role="alert"on the message. [a11y]
Related
- docs/patterns/action-hierarchy.md — where the submit sits among other actions.
- docs/components/button.md — busy and disabled states.