# Form Patterns — Copy-Paste Reference
## Contents
- Complete field markup
- Common autocomplete values
- Validation timing (blur, then input)
- Error summary after failed submit
- Submit button pending state
- Multi-step form skeleton
- Gotchas
## Complete field markup
```html
```
The error node exists from the start (hidden) so `aria-describedby` stays stable.
## Common autocomplete values
| Field | type | autocomplete |
|---|---|---|
| Email | email | email |
| Phone | tel | tel |
| First / last name | text | given-name / family-name |
| Street / city / postal | text | address-line1 / address-level2 / postal-code |
| Country | (select) | country-name |
| Card number / expiry / CVC | text | cc-number / cc-exp / cc-csc |
| New / current password | password | new-password / current-password |
| One-time code | text | one-time-code |
## Validation timing (blur, then input)
```js
const messages = {
valueMissing: (label) => `Enter your ${label.toLowerCase()}.`,
typeMismatch: () => 'Enter an email address with an @, like name@example.com.',
tooShort: (label, input) => `${label} must be at least ${input.minLength} characters.`,
};
function validate(input) {
const label = input.labels[0].textContent;
const errEl = document.getElementById(input.getAttribute('aria-describedby')
.split(' ').find(id => id.endsWith('-error')));
let msg = '';
for (const key of Object.keys(messages)) {
if (input.validity[key]) { msg = messages[key](label, input); break; }
}
input.setAttribute('aria-invalid', msg ? 'true' : 'false');
errEl.textContent = msg;
errEl.hidden = !msg;
return !msg;
}
document.querySelectorAll('input, select, textarea').forEach((input) => {
input.addEventListener('blur', () => validate(input), { once: false });
input.addEventListener('input', () => {
// re-validate live only after the field has already errored
if (input.getAttribute('aria-invalid') === 'true') validate(input);
});
});
```
Pair with `
```
Rules encoded above: steps are fieldsets shown one at a time (data survives because
nothing unmounts), per-step validation runs on "Continue", the last step is a
read-only review, and the progress line is announced on change.
## Gotchas
- `