Forms and validation
This page shows one complete form: labels, hints, required fields, error messages and native submission. Each control has its own reference page. Start here when you need them to work together.
Which controls take part in a form
Section titled “Which controls take part in a form”These controls are form-associated. Their name and value are included in FormData, they respond to form.reset(), and they report validity to the form:
holo-text-fieldholo-textareaholo-selectholo-checkboxholo-switchholo-radio-groupholo-slider
holo-button is not form-associated. It wraps a <button type="button">, so clicking it never submits a form on its own. Call form.requestSubmit() from its click handler, as the example below does. For a plain, no-JavaScript submit button, use a native <button type="submit">.
Labels, hints and errors
Section titled “Labels, hints and errors”holo-text-field, holo-select, holo-checkbox and holo-switch render their own label. holo-text-field and holo-select also take invalid and error-message. Set them together. The message appears below the control and is announced as an alert.
holo-form-field adds a label, hint and error message to a control that has none of its own, such as a native <input>. It forwards aria-describedby, aria-invalid and aria-required to the first slotted element. Do not wrap a control that already has a label in holo-form-field, or the label appears twice.
| Need | Use |
|---|---|
| Label | The control’s label, or label on holo-form-field |
| Required marker | required on the control, or on holo-form-field |
| Hint text | hint on holo-form-field wrapping a native control |
| Error message and styling | invalid plus error-message |
Example
Section titled “Example”<form id="signup" novalidate> <holo-text-field name="name" label="Full name" required></holo-text-field> <holo-text-field name="email" type="email" label="Email address" required></holo-text-field> <holo-form-field label="Website" hint="Optional. Include https://."> <input type="url" name="website" /> </holo-form-field> <holo-select name="role" label="Role" placeholder="Choose a role" required></holo-select> <holo-checkbox name="terms" value="accepted" label="I accept the terms"></holo-checkbox> <holo-switch name="newsletter" label="Send me the newsletter"></holo-switch>
<holo-button id="submit" label="Create account"></holo-button> <holo-button id="reset" label="Reset" variant="secondary"></holo-button></form>const form = document.querySelector('#signup');const select = form.querySelector('holo-select');select.options = [ { value: 'designer', label: 'Designer' }, { value: 'developer', label: 'Developer' },];
// holo-button does not submit forms itself.document.querySelector('#submit').addEventListener('click', () => form.requestSubmit());document.querySelector('#reset').addEventListener('click', () => form.reset());
form.addEventListener('submit', event => { event.preventDefault();
// Validate, then set `invalid` and `errorMessage` on each failing control. const email = form.querySelector('holo-text-field[name="email"]'); email.invalid = !email.value.includes('@'); email.errorMessage = email.invalid ? 'Enter an email address, such as you@example.com.' : undefined;
if (!email.invalid) { const data = Object.fromEntries(new FormData(form)); console.log(data); // { name, email, website, role, terms?, newsletter? } }});Things to note:
- Add
novalidateto the form when you show your own messages, so the browser’s default bubbles do not compete with them. - Unchecked
holo-checkboxandholo-switchcontrols are left out ofFormData, like native checkboxes. A checked control submits itsvalue, which defaults toon. holo-selecttakes its choices from theoptionsproperty, which you set from JavaScript.- Clear
invalidas soon as the user fixes the value, so the error does not linger.
Writing error messages
Section titled “Writing error messages”- Say what is wrong and how to fix it: “Enter an email address, such as you@example.com”, not “Invalid input”.
- Do not rely on colour alone. The message text carries the meaning.
- Validate on submit, then re-validate as the user edits. Avoid flagging a field as invalid before they have touched it.
- On a failed submit, move focus to the first invalid control.