# Forms

Forms can be simple or complex, and may be presented as dedicated pages, side panels, or modals depending on the use case and the situation.

Tabs: [About](/patterns/forms/forms.about.md) · [Accessibility](/patterns/forms/forms.accessibility.md) · [Resources](/patterns/forms/forms.resources.md)

> We recommend using our [Form control component](/components/web/form-control/form-control.guidelines) to automatically handle accessibility for form controls. The guidelines on this page have been used to inform the implementation of our Telescope form components and should only be necessary to use outside of Telescope when creating custom form components.

## Naming

To satisfy [WCAG 4.2.1](https://www.w3.org/TR/WCAG21/#name-role-value) ("For all user interface components, the name and role can be programmatically determined"), an input must be named.

An accessible name is a short string of text that’s programatically associated with a UI element. Its purpose is to help assistive technology users understand the UI element’s purpose and distinguish it from other UI elements on the page.

> This is unrelated to the `name` attribute in HTML.

### Providing an accessible name

Use a `<label>` element and give its `for` attribute a value matching the form control’s `id`.

> Avoid nesting form controls within a `<label>`. This works in theory, but [assistive technology support for this pattern is inadequate](https://www.tpgi.com/should-form-labels-be-wrapped-or-separate/).

```html
<label for="expense-category">Expense category</label> <input type="text" id="”expense-category" />
```

### Using `aria-labelledby`

Use `aria-labelledby` when the form control’s label is present in the markup but can't be changed to a `<label>` element, such as when it needs to be a `<div>` or `<span>`. Give the form control’s `aria-labelledby` attribute a value matching the label’s `id` attribute.

```markdown
<span id="expense-category">Expense category</span>
<input type="text" aria-labelledby="expense-category" />
```

### Using `aria-label`

> In general, avoid using `aria-label` to give form controls an accessible name.

Use `aria-label` when the form control’s label is not in the markup and you can’t add it to the markup. Or when the label is in the markup, but you can’t change it to a `<label>` element or use `aria-labelledby` to associate it with the form control.

In the latter case, make sure the visible label matches the value of the `aria-label`. Note, in addition to [other issues with using aria-label](/utils/visually-hidden/visually-hidden.guidelines#what-about-aria-label), this means that screen reader users may hear the same label twice.

```html
<span>Expense category</span> <input type="text" aria-label="Expense category" />
```

### Hiding a label visually

Without a visible label, it can be difficult for people to understand or remember what they should fill in or select. This especially impacts people with cognitive impairments.

It’s only acceptable to hide a visible label when a common design pattern is used in a context with visual cues that clarify the form control’s purpose. An example is a search field placed near the top of a page, featuring a magnifying glass icon and/or a button labelled “Search”.

Note, form controls without visible labels still need accessible names. We recommend using our [VisuallyHidden](/utils/visually-hidden/visually-hidden.guidelines) component for this use case. Wrap it around a `<label>` with a `for` attribute matching the form control’s `id`.

```html
<VisuallyHidden>
    <label for="search-field">Search</label>
</VisuallyHidden>
<input type="search" id="search-field" />
```

## Names for multiple related form controls

Related form controls are form controls that share a theme or purpose. For example, a set of radio buttons to select the payment status of an expense. Or a group of text fields used to input an address (e.g., street, apartment number, city, zip code).

To provide an accessible name to related form controls, start by wrapping them in a `<fieldset>` element. Then, use the `aria-labelledby` attribute on the `<fieldset>` to reference the `id` of the text element that serves as the visible label.

```html
<span id="payment-status">Payment status</span>
<fieldset aria-labelledby="payment-status">
    <input type="checkbox" id="pending" />
    <label for="pending">Pending</label>
    <input type="checkbox" id="settled" />
    <label for="settled">Settled</label>
    <input type="checkbox" id="Refund" />
    <label for="Refund">Refund</label>
</fieldset>
```

## Descriptions

An accessible description is a text string that provides extra context about a UI element, helping assistive technology users to understand its purpose or requirements. Typically longer than an accessible name, it can include instructions or clarifications.

To provide a form control with an accessible description, use the `aria-labelledby` attribute on the `<input>` element (or `<fieldset>` for groups of related form controls) to reference the `id` of the text element containing the description.

```html
<label for="password">Create a password</label>
<input type="password" id="password" aria-describedby="password-requirements" />
<p id="password-requirements">
    Your password must be at least 8 characters long and include a mix of letters, numbers, and
    special symbols.
</p>
```

## Required form controls

To inform assistive technology users that a single form control is required, add `aria-required=”true”` to the form control.

```html
<label for="name">Your name</label> <input type="text" id="name" aria-required="true" />
```

> Avoid using `required` instead of `aria-required=”true”`, as [some browser and screen reader pairings announce the form control as invalid](https://www.tpgi.com/required-attribute-requirements/) before the user has started typing.

For a group of related form controls, mention it’s required in its accessible name.

```html
<span id="expense-category">Expense category (select at least one)</span>
<fieldset aria-labelledby="expense-category">
    <input type="checkbox" id="software" />
    <label for="software">Software</label>
    <input type="checkbox" id="travel" />
    <label for="travel">Travel</label>
    <input type="checkbox" id="training" />
    <label for="training">Training</label>
</fieldset>
```

> Avoid adding `aria-required=”true”` to a `<fieldset>` element. It only works with some browser and screen reader pairings. More importantly, it’s invalid, as `aria-required` should only be used on interactive elements and the `<fieldset>` element maps to the `group` role, which represent non-interactive [document structure elements](https://www.w3.org/TR/wai-aria-1.1/#document_structure_roles).

## Invalid form controls

When a form control becomes invalid, assistive technology users need clear, accessible feedback to resolve the error.

### Marking a form control as invalid

1. Add the `aria-invalid` attribute to the form control and set its value to `“true”`
2. Add an error message after the form control and its optional description
3. Add an `aria-describedby` attribute to the form control and an `id` to the error message. If `aria-describedby` is already being used, prepend the `id` of the error message, so the user first hears what's wrong and then hears the instructions from the hint text.
4. Match the values of the `aria-describedby` and `id` attributes
5. When the form control is no longer invalid, remove the `aria-invalid` attribute completely instead of setting it to `“false”`

```html
<label for="password">Create a password</label>
<input
    id="password"
    aria-describedby="error password-requirements"
    aria-required="true"
    aria-invalid="true"
/>
<p id="password-requirements">
    Your password must be at least 8 characters long and include a mix of letters, numbers, and
    special symbols.
</p>
<p id="error">The password you entered is too short.</p>
```

> Avoid using `aria-errormessage`, due to inconsistent support across browsers and screen readers ([source 1](https://a11ysupport.io/tech/aria/aria-errormessage_attribute), [source 2](https://adrianroselli.com/2023/04/exposing-field-errors.html?replytocom=254999)), despite its [semantic appropriateness](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-errormessage).

### Handling form submission errors

If a user has tried to submit a form with a single invalid form control, [mark it as invalid and provide it with an error message](/patterns/forms/forms.accessibility#19053cae7a668059bfc7df5dc0081ad5). Additionally, move the user’s focus to the invalid form control.

If multiple form controls are invalid, display an error summary at the top of the form. It should satisfy the following criteria:

- Each error links to its corresponding invalid form control
- The errors are worded the same as those next to the invalid form controls
- The error summary has a descriptive heading
- The user’s focus is moved to the error summary’s heading. (As the heading is not interactive, it’s not inherently focusable. To make it focusable without adding it to the natural tabbing order of the page, set its `tabindex` attribute to `-1`.)
- Every invalid form control is [marked as invalid and provided with an error message](/patterns/forms/forms.accessibility#19053cae7a668059bfc7df5dc0081ad5)

```html
<div>
    <h2 tabindex="-1">Please correct the following 3 errors</h2>
    <ul>
        <li><a href="#full-name">Enter your full name</a></li>
        <li><a href="#email">Enter your email address</a></li>
        <li><a href="#address">Enter your address</a></li>
    </ul>
</div>
```

> Linking to invalid form controls is [recommended by the W3C](https://www.w3.org/WAI/WCAG21/Techniques/general/G139.html) as an advisory technique to comply with WCAG Success Criteria [3.3.1 Error Identification (Level A)](https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#error-identification) and [3.3.3 Error Suggestion (Level AA)](https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#error-suggestion).

### Implementing live validation

> As our [Forms guidelines](/patterns/forms/forms.about#validation) state, only use live validation for longer forms and strict input fields. Also, validate on blur instead of during interaction with the form control.

Before the form control becomes invalid, make sure a container is present in the markup with `role=”status”`. The `“status”` role ensures that when content, such as an error message, gets added to the container, screen readers will announce it

```html
<label for="password">Create a password</label>
<input id="password" aria-describedby="password-requirements" aria-required="true" />
<p id="password-requirements">
    Your password must be at least 8 characters long and include a mix of letters, numbers, and
    special symbols.
</p>
<div role="status"></div>
```

When the form control becomes invalid, add an error message to the `role=”status”` container. Also, follow the guidelines about [marking the form control as invalid](/patterns/forms/forms.accessibility#19053cae7a668059bfc7df5dc0081ad5).

```html
<label for="password">Create a password</label>
<input
    id="password"
    aria-describedby="error password-requirements"
    aria-required="true"
    aria-invalid="true"
/>
<p id="password-requirements">
    Your password must be at least 8 characters long and include a mix of letters, numbers, and
    special symbols.
</p>
<div role="status">
    <p id="error">The password you entered is too short.</p>
</div>
```
