Accessible forms: labels, errors and keyboard

Associated labels, errors described in text, keyboard, visible focus and autocomplete: what WCAG 2.2 asks of a form, with HTML examples.

Check my site freeSee the plans
On this page 11 min read
  1. The criteria that apply
  2. Labels
  3. Buttons and custom components
  4. Errors: identify, describe and suggest
  5. Keyboard and visible focus
  6. Autocomplete and the field's purpose (1.3.5)
  7. New WCAG 2.2 criteria that affect forms
  8. How to test a form
  9. Forms in Brazil's NBR 17225
  10. How to check your site

In an accessible form, each field has a visible label that is tied to the field in the code and says what the field is for. Errors are reported in text, the form points to the wrong field and suggests the fix when it is known. It works entirely by keyboard, with focus always visible. Fields that collect the person's own data declare their purpose with autocomplete. And, since WCAG 2.2, the form does not ask again for what the person already entered in the same process, and the login does not lock out people who use a password manager.

This guide follows WCAG 2.2 (December 2024 edition), level AA, and the W3C forms tutorials. The examples are plain HTML; in React, Vue or any other framework, the final HTML needs the same structure.

The criteria that apply

CriterionLevelWhat it asks of a form
1.3.1 Info and RelationshipsALabel associated with the field and groups of fields marked up in the code
1.3.5 Identify Input PurposeAAFields that collect user data have their purpose declared in the code
2.1.1 KeyboardAEverything works by keyboard
2.4.6 Headings and LabelsAALabels describe the field's purpose
2.4.7 Focus VisibleAAThe keyboard focus indicator shows
3.3.1 Error IdentificationAThe field in error is identified and the error is described in text
3.3.2 Labels or InstructionsAThere are labels or instructions when content requires user input
3.3.3 Error SuggestionAAWhen the fix is known, it is suggested, unless that would compromise security
3.3.4 Error Prevention (Legal, Financial, Data)AASubmissions with legal or financial effect can be reversed, checked or confirmed
3.3.7 Redundant Entry (new in 2.2)ADo not ask again for what was already entered in the same process
3.3.8 Accessible Authentication (Minimum) (new in 2.2)AALogin without a memory or transcription test unless there is an alternative
4.1.2 Name, Role, ValueAName and type of each control, and its states, available to assistive technologies

The official wording of each criterion is in WCAG 2.2.

Labels

Associate the label with the field

A label element whose for attribute matches the field's id is the approach the W3C labels tutorial recommends (Labeling Controls). With it, the screen reader announces the label when the field gets focus, and clicking the label text moves to the field, which makes the click area larger. When the form collects personal data, what happens to that data needs to be explained in the site's privacy policy.

<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email">

Placing the field inside the label (an implicit label) also works, but the W3C notes that the explicit label has broader support.

A placeholder is not a label

The HTML specification says the placeholder attribute should not be used as an alternative to a label (HTML Living Standard). The text disappears when the person starts typing, so anyone who gets distracted or has memory difficulties loses the reference. If you use a placeholder as a format example, it also needs a 4.5:1 contrast, as the color contrast guide explains.

<!-- Avoid: the only label disappears when typing -->
<input type="tel" placeholder="Phone">

<!-- Prefer: visible label and a separate example -->
<label for="tel">Phone number with area code</label>
<input id="tel" type="tel" autocomplete="tel" aria-describedby="tel-hint">
<p id="tel-hint">Example: 11 91234-5678</p>

The aria-describedby attribute links the field to supporting text, which the screen reader announces after the label.

Labels that say what the field is for

Criterion 2.4.6 asks for labels that describe the purpose. "Name" on a gift form could be the buyer's name or the recipient's; "Recipient's name" settles it. The expected format belongs here too: if the date is DD/MM/YYYY, say so in the label or the hint.

Radio buttons and groups of checkboxes need a title for the group. The W3C recommends fieldset with legend and asks for a short legend, because some screen readers repeat it for every field (Grouping Controls).

<fieldset>
  <legend>Shipping method</legend>
  <input type="radio" id="shipping-standard" name="shipping" value="standard">
  <label for="shipping-standard">Standard (up to 7 business days)</label>
  <input type="radio" id="shipping-express" name="shipping" value="express">
  <label for="shipping-express">Express (up to 2 business days)</label>
</fieldset>

Required fields

Mark required fields in text, in the label itself, and not only with color or an unexplained asterisk. The required attribute exposes the state to assistive technologies and turns on browser validation.

<label for="name">Full name (required)</label>
<input id="name" name="name" autocomplete="name" required>

Hidden labels and aria-label

A search field with a magnifying glass icon can go without a visible label, as long as its purpose is clear from context. The W3C allows hiding the label visually, but warns that display: none and visibility: hidden also hide it from screen readers. The aria-label and aria-labelledby attributes give the field a name without visible text and, according to the tutorial, should only be used when the label is clear from the surrounding content.

Buttons and custom components

Criterion 4.1.2 asks for each control's name, role and state to be available to assistive technologies. Native elements (button, input, select, textarea) already provide this. A button gets its name from its own text.

<!-- Avoid: it does not receive focus and is not announced as a button -->
<div class="btn" onclick="submitOrder()">Submit</div>

<!-- Prefer -->
<button type="submit">Place order</button>

Brazil's NBR 17225, in Annex A (A.1.6), points out that custom components, such as modals, pop-ups and carousels, have no semantics or behavior defined by the browser, and that this responsibility shifts to the developer. Date pickers, searchable dropdowns and "styled" upload fields are the ones that need the most care in forms.

Errors: identify, describe and suggest

Three criteria cover errors. 3.3.1 asks that, when an error is detected automatically, the field be identified and the error described in text. A red border alone is not enough. 3.3.3 asks for a suggested fix when one is known, unless that would compromise security or the purpose of the content. 3.3.4 applies to forms with legal or financial effect, forms that change or delete user data, and forms that submit test responses: the submission must be reversible, checked with a chance to correct, or confirmed in a review step (WCAG 2.2, 3.3.4).

The W3C notifications tutorial (User Notifications) recommends:

  • listing the errors at the top of the form, under a clear heading;

  • also giving the message next to each field;

  • linking each field to its message with aria-describedby;

  • moving focus to the first field with an error;

  • also flagging the error in the page title, which the screen reader announces on load;

  • confirming a successful submission, not only the errors.

The aria-invalid="true" attribute marks the field in error for assistive technologies; it is W3C technique ARIA21 for criterion 3.3.1 (ARIA21).

<!-- Summary at the top, after submission -->
<div id="errors" tabindex="-1">
  <h2>The form has 2 errors</h2>
  <ul>
    <li><a href="#postcode">Postal code: use 8 digits</a></li>
    <li><a href="#email">Email: the @ is missing</a></li>
  </ul>
</div>

<!-- Field with an error -->
<label for="postcode">Postal code (required)</label>
<input id="postcode" name="postcode" autocomplete="postal-code" inputmode="numeric"
       aria-invalid="true" aria-describedby="postcode-error">
<p id="postcode-error">Error: the postal code must have 8 digits. Example: 01310-100.</p>

After a submission with errors, keep what the person typed. Clearing the form forces them to type everything again and, in a multi-step process, may fail criterion 3.3.7, covered further down.

Keyboard and visible focus

Criterion 2.1.1 asks for every function to work by keyboard. In a form, that includes reaching each field with Tab, selecting options with Space and the arrow keys, opening and choosing from lists, using the date picker and submitting with Enter or the button. Native components already do this; custom ones have to be programmed to.

Criterion 2.4.7 asks for keyboard focus to be visible. The most common mistake is removing the default outline with outline: none and putting nothing in its place. If you change the style, the new indicator needs a 3:1 contrast with the background (criterion 1.4.11), and Brazil's NBR 17225 makes that contrast a requirement (item 5.11.6).

/* Custom indicator: #1a5fb4 on white is 6.3:1 */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible {
  outline: 3px solid #1a5fb4;
  outline-offset: 2px;
}

In WCAG 2.2, criterion 2.4.11 also asks that the focused field not be entirely hidden by a sticky header, a cookie banner or a chat. The details are in the WCAG 2.2 level AA guide.

Autocomplete and the field's purpose (1.3.5)

Criterion 1.3.5 asks that fields collecting data about the user have a purpose that can be identified from the code. The list of purposes is in section 7 of WCAG and follows the HTML autofill values: name, given-name, family-name, email, tel, street-address, postal-code, bday, username, new-password, current-password, among others (WCAG 2.2, section 7).

According to Understanding 1.3.5:

  • The criterion applies only to data about the user. A "friend's email" field on a referral form is not included.

  • What counts is that the purpose is in the code. Whether the browser fills it in or not is irrelevant to the evaluation, and the browser guessing the field is not enough.

  • The shipping and billing prefixes separate shipping and billing addresses.

  • The benefit is greatest for people with language and memory disabilities, who do not have to remember and type their data.

<label for="shipping-name">Recipient's name</label>
<input id="shipping-name" autocomplete="shipping name">

<label for="shipping-postcode">Shipping postal code</label>
<input id="shipping-postcode" autocomplete="shipping postal-code" inputmode="numeric">

<label for="birthdate">Date of birth</label>
<input id="birthdate" type="date" autocomplete="bday">

New WCAG 2.2 criteria that affect forms

3.3.7 Redundant Entry

Within the same process, what the person already entered cannot be asked for again, unless it is prefilled or available to select. The exceptions are: re-entering is essential, the information is required for security, or the earlier information is no longer valid (WCAG 2.2, 3.3.7). Browser autofill does not count as a solution, according to Understanding 3.3.7; the site itself has to offer the information. Asking the user to confirm a new password that is not shown on screen is allowed.

<input type="checkbox" id="same-address" checked>
<label for="same-address">Billing address same as shipping</label>

3.3.8 Accessible Authentication (Minimum)

No step of the login may require a cognitive function test, such as remembering a password, transcribing characters or solving a puzzle, without an alternative or a mechanism to assist (WCAG 2.2, 3.3.8). WCAG itself names password manager autofill and copy and paste as accepted mechanisms. In practice, according to Understanding 3.3.8:

  • Blocking paste in the password field or the verification code field is a failure.

  • Blocking autofill, for example with a script, is a failure if there is no alternative.

  • A CAPTCHA that asks you to type distorted characters fails without an alternative; an object recognition CAPTCHA is accepted at level AA.

  • An email login link (technique G218) and login through an external provider via OAuth are ways to comply.

<label for="username">Email</label>
<input id="username" type="email" autocomplete="username">

<label for="password">Password</label>
<input id="password" type="password" autocomplete="current-password">

<label for="code">Verification code</label>
<input id="code" inputmode="numeric" autocomplete="one-time-code">

<!-- Avoid: blocks the password manager and pasting -->
<input type="password" autocomplete="off" onpaste="return false">

The one-time-code value is part of the autofill list in the HTML specification (HTML Living Standard, Autofill), although it is not in the list in section 7 of WCAG.

2.5.8 Target Size

Checkboxes, radio buttons and show password or clear field icons are small targets. WCAG 2.2 asks for at least 24 by 24 CSS pixels or enough spacing. Native controls at the browser's default size, unchanged by the site, fall under the exception (WCAG 2.2, 2.5.8). If the site restyled these controls and they ended up smaller than 24 pixels, measure the spacing around each one.

How to test a form

  1. Fill in and submit the form with the keyboard only. Note every field or control that does not receive focus, traps focus or loses visible focus.

  2. Check each field's name. In Chrome, the Accessibility tab in DevTools shows the accessibility tree and the element's computed properties. With a screen reader (NVDA on Windows, VoiceOver on Mac and iPhone, TalkBack on Android), listen to what is announced for each field.

  3. Submit it empty and then with invalid data. Check whether each error appears in text, points to the field and suggests the fix, and whether focus moves to the error or to the summary.

  4. Check that what was typed is still there after the error.

  5. Inspect the autocomplete attributes on fields with personal data.

  6. Test the login with a password manager and try pasting the password and the code.

  7. Go through multi-step flows and note any data requested twice.

  8. Measure small targets and check the contrast of borders, placeholders and the focus indicator.

Automated tools find unlabeled fields and missing attributes, but the W3C warns that they cannot determine on their own whether a site is accessible (Selecting Web Accessibility Evaluation Tools). The clarity of messages, focus order and multi-step flows need human testing.

Forms in Brazil's NBR 17225

ABNT NBR 17225:2025, Brazil's web accessibility standard, groups its form items in section 5.9, "Forms and data entry" (ABNT NBR 17225:2025). Some requirements and the WCAG criterion the standard associates with them (item names are our translations from Portuguese):

NBR item (requirement)WCAG criterion cited in the standard
5.9.1 Field label3.3.2 and 4.1.2
5.9.3 Associated field label1.3.1
5.9.4 Descriptive field label2.4.6
5.9.7 Required fields3.3.2
5.9.8 Determined data type1.3.5
5.9.9 Descriptive error message3.3.1
5.9.10 Correction suggestion3.3.3
5.9.12 Error prevention for critical forms3.3.4
5.9.15 Data re-entry3.3.7
5.9.18 Accessible authentication (minimum)3.3.8

The standard also lists CAPTCHA among the critical items in Annex A. Who is bound by Brazil's Inclusion Law (LBI) and how the standard fits in is covered in the guide LBI and NBR 17225: what Brazilian law requires of a website. This article explains the technical standard; it is not legal advice.

How to check your site

The WebVyze free check analyzes the home page, with no sign-up, and points out accessibility risks under the law. Forms usually live on other pages (contact, sign-up, checkout); the free plan audits up to 5 pages of one site every 30 days. The manual tests in this list, such as keyboard, screen reader and multi-step flows, are still needed. The same goes for accessibility widgets installed on top of the site, discussed in accessibility overlays.

Frequently asked questions

Can I hide the label and keep only the icon, as in a search field with a magnifying glass?
Yes, as long as the field's purpose is clear from context and the field has an accessible name, for example with a visually hidden label or aria-label. The W3C warns that display: none and visibility: hidden also hide the label from screen readers, so the hiding technique has to keep the text in the code.
Is validating while the person types forbidden?
WCAG does not forbid it. The criteria ask for the error to be identified and described in text, with a suggestion when one is known. Messages that appear on every keystroke, before the person finishes typing, tend to get in the way; validating when the person leaves the field or submits is a calmer choice.
Does the order of the fields matter?
Yes. The focus order must preserve meaning and operability (criterion 2.4.3), and the reading order must be programmatically determinable (1.3.2). In practice, the HTML order should follow the visual order.
Is a submit button that stays disabled until everything is filled in a problem?
The disabled button's contrast is exempt, because inactive components have no contrast requirement. The problem is usually something else: the person does not know what is missing. Keeping the button active and showing the errors on submit gives them the information they need.
About the author

Daniel Paz

WordPress, performance and SEO specialist, founder of WebOption

Daniel Paz is a WordPress, performance and SEO specialist, the founder of WebOption and an international speaker. With more than 18 years of experience in web development, he helps companies turn their websites into fast, efficient, results-driven platforms.

See what your site needs to fix.

A free check by law, with the cause and the fix guide for each problem.

Check my site freeCreate a free account

Keep reading

Partners

Partner benefits with one coupon.

WPLIGHT

Partner links. We may earn a commission at no extra cost to you.