Contact forms, checkouts and logins are where a visitor becomes a customer, and where accessibility problems cost the most. A field with no real label may be announced with no useful name. A red border says nothing to someone who cannot see it. An error that says only "Invalid input" leaves everyone guessing.
This guide covers accessible forms from the ground up: labels, instructions, grouping, autocomplete, error messages and the WCAG 2.2 rules on re-entering information and logging in. It is for site owners and developers who can edit HTML. You will finish with a form pattern you can copy and a short list of tests. Passing every item does not prove a form is accessible or compliant, and this is not legal advice.
Short answer: Give every field a visible <label> tied to it with for and id, state required fields and formats in text, group related options in a fieldset with a legend, add autocomplete tokens, and report errors in plain text next to the field and in a summary, never by colour alone.
Before you begin
You need edit access to the form's HTML (or its template or plugin settings), a staging copy and about an hour per form. List the forms that matter: contact, checkout, sign-up, login, password reset. W3C's forms tutorial recommends asking only for what the task needs, so decide which fields are truly required. If a form builder generates the markup, inspect its output in the browser's developer tools. For a wider review of the site, see Website Accessibility Audit: Fix the Most Common Problems.
What does WCAG say about accessible forms?
| Criterion | Level | What it means for a form |
|---|---|---|
| 1.3.1 Info and Relationships | A | Labels, groups and structure exist in the code, not only in the visual design. |
| 3.3.2 Labels or Instructions | A | Fields that take input have a label or instruction, including format hints. |
| 2.5.3 Label in Name | A | The accessible name contains the visible label text. |
| 1.3.5 Identify Input Purpose | AA | Fields collecting information about the user expose their purpose, normally with autocomplete. |
| 3.3.1 Error Identification | A | Detected errors are identified and described in text. |
| 3.3.3 Error Suggestion | AA | If a correction is known, it is suggested (unless that would compromise security or purpose). |
| 3.3.4 Error Prevention (Legal, Financial, Data) | AA | Important submissions can be reviewed, corrected or reversed. |
| 3.3.7 Redundant Entry | A | Information already given in the same process is filled in or selectable. New in WCAG 2.2. |
| 3.3.8 Accessible Authentication (Minimum) | AA | Login does not depend on a cognitive function test without an alternative or help. New in WCAG 2.2. |
| 4.1.3 Status Messages | AA | Status messages can be announced without taking focus. |
Step-by-step instructions
Step 1: Give every field a visible, associated label
The <label> element gives a control its accessible name, and clicking the label focuses the control, a larger target that MDN notes helps people with motor control conditions. W3C and MDN both recommend the explicit pattern, where the label's for matches the control's id.
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email">
Placeholder text is not a label. MDN states that a placeholder should never be required to understand a form, that screen readers may not announce it, and that it disappears once the user types. A short format hint in a placeholder next to a real label is fine. Keep the contrast of placeholder text readable; W3C's example uses #767676 on white, and Color Contrast and WCAG explains how to measure it.
Two details catch people out. If a label must be visually hidden, use a "visually hidden" CSS technique, not display: none, which hides it from assistive technology too. And WCAG 2.5.3 means the accessible name should contain the visible text: a control showing "Search" with aria-label="Find items" fails speech-input users who say "click Search". MDN also advises against links or headings inside a label.
What success looks like: Clicking each label focuses its field, and a screen reader announces the label when you tab to the field.
Step 2: Mark required fields and state formats in text
Say which fields are required in the label text, for example "Phone (required)". Add the required attribute so the browser and assistive technology know; W3C's tutorial shows aria-required="true" as an addition for browsers that do not expose it. Do not rely on a coloured asterisk alone.
Tell people the format before they type. Put a short hint under the field and connect it with aria-describedby, so it is read after the label:
<label for="phone">Phone (required)</label>
<input type="tel" id="phone" name="phone" required autocomplete="tel"
aria-describedby="phone-hint">
<p id="phone-hint">Include the area code, for example 020 7946 0000.</p>
W3C advises accepting several formats for phone numbers and postal codes rather than forcing one. Choose the right type (email, tel, url, number) so mobile devices show suitable keyboards, and make buttons and checkboxes easy to hit (Mobile Usability covers tap targets).
What success looks like: A visitor can tell which fields are required and what format is expected without submitting the form first.
Step 3: Group related controls with fieldset and legend
Radio buttons and checkbox sets need a question as well as a label per option. Wrap them in a <fieldset> and use <legend> for the question. W3C says a legend acts as a heading for the group, and recommends separate fieldsets for shipping and billing addresses so identical labels stay distinguishable.
<fieldset>
<legend>How should we contact you?</legend>
<div><input type="radio" id="c-email" name="contact" value="email">
<label for="c-email">Email</label></div>
<div><input type="radio" id="c-phone" name="contact" value="phone">
<label for="c-phone">Phone</label></div>
</fieldset>
Screen readers differ in how often they read a legend, so keep it short and make each option label understandable alone.
What success looks like: A screen reader announces the question and then each option.
Step 4: Add autocomplete and do not block password managers
WCAG 1.3.5 asks that the purpose of fields collecting user information can be determined by software. The usual technique is the autocomplete attribute with a standard token. MDN lists tokens including name, given-name, family-name, email, tel, street-address, postal-code, country, cc-number, cc-exp, username, current-password, new-password and one-time-code. Invented values do not count, and MDN says autocomplete="off" removes a feature many users depend on.
For logins, use username, current-password and one-time-code, and avoid scripts that block pasting or fill. W3C's Understanding page for 3.3.8 lists password-manager support and copy-and-paste as mechanisms that satisfy the criterion, and treats blocking them as a failure unless another method is offered. It also says that split-digit code boxes that force transcription can fail.
What success looks like: Your browser's autofill fills name, email and address fields, and your password manager recognises the login.
Step 5: Write error messages that identify and explain
When validation fails, say in text which field is wrong and how to fix it. WCAG 3.3.1 requires the error to be described in text; colour, icons and highlighting can support the text but not replace it. For 3.3.3, give the correction when you know it: "Enter a date as DD/MM/YYYY, for example 31/01/2027" beats "Invalid date".
Apply three patterns together:
- Inline message tied to the field. Put the message next to the input and reference it with
aria-describedby. MDN'saria-invalidpage adds that you should setaria-invalid="true"only after validation, not on empty required fields before the user has tried to submit. - Error summary at the top. W3C suggests a list under a clear heading that states the number of errors, each item naming the field, the fix and a link to it, in a container with
role="alert"when it appears dynamically. - Focus management. After a failed submission, W3C advises moving focus to the first field that contains an error.
<div role="alert" id="error-summary">
<h2>There are 2 errors in this form</h2>
<ul>
<li><a href="#email">Email address: enter an address like name@example.com.</a></li>
<li><a href="#phone">Phone: this field is required.</a></li>
</ul>
</div>
<label for="email">Email address</label>
<input type="email" id="email" name="email" aria-invalid="true"
aria-describedby="email-error">
<p id="email-error"><strong>Error:</strong> Enter an address like name@example.com.</p>
Start messages with a word such as "Error:" so the signal is not only red text. web.dev notes that live regions usually announce only the first of several simultaneous errors, so a summary is more dependable. Validate on the server too, since W3C points out client-side checks can be bypassed. If your validation script silently fails, the console usually says why: Find and Fix JavaScript Console Errors on Page Load.
For success and progress messages (SC 4.1.3), W3C's Understanding page says role="status" suits results and progress and role="alert" suits important, time-sensitive warnings, and that neither needs to take focus. If the form submits to a new page, a clear heading and <title> such as "Message sent" works, because screen reader users hear the title at once.
What success looks like: A user who cannot see colour can find every error, understand it, jump to the field and hear the outcome of a successful submit.
Step 6: Reduce the work the form demands
WCAG 2.2 adds two rules that cut effort. Under 3.3.7 Redundant Entry (Level A), information the user already entered in the same process must be filled in or selectable; the W3C example is a checkbox "Billing address same as shipping address". Exceptions cover essential re-entry, security (such as confirming a new password) and information that is no longer valid. Under 3.3.8 (AA), a login may not require remembering or transcribing unless there is an alternative or a helper such as paste or a password manager. A CAPTCHA or login widget from a vendor is a third-party script on your page, so test it too (Third-Party Scripts and Performance shows how to find them).
For payments, legal commitments and data deletion, 3.3.4 asks for at least one safety net: a review step, a confirmation, or a way to correct or cancel.
What success looks like: No visitor retypes an address they just entered, and checkout offers a review step.
Step 7: Test with keyboard, zoom and a screen reader
Tab through the form (see Keyboard Accessibility: Test Focus, Tab Order and Skip Links). Submit it empty and with bad values. Zoom to 200 percent. web.dev also recommends testing with a screen reader such as NVDA or VoiceOver and at that zoom level. Automated tools cover only part of this: Crawvix's accessibility check draws on Lighthouse audits, including one for form labels, which cannot judge whether an error message is understandable.
What success looks like: You can complete the form, and fail it, using only the keyboard, and the messages make sense each time.
Final verification checklist
- Your accessible forms give every input, select and textarea a visible
<label>connected byforandid. - Placeholders are used only for hints, never as the only label.
- Required fields and formats are stated in text before submission.
- Radio buttons and checkbox sets sit in a
fieldsetwith alegend. -
autocompletetokens are set on name, email, phone, address and login fields. - Pasting into login and code fields works.
- Errors are described in text beside the field and in a summary, with a suggested fix.
- Focus moves to the first field in error after a failed submit.
- Success messages are announced without moving focus.
- Known details are not requested twice in one process.
- You tested with the keyboard, at 200 percent zoom and with a screen reader.
Common mistakes to avoid
- Using placeholders as labels. Add a real label and keep the placeholder only as an example.
- Marking errors with a red border only. Add text that names the problem and the fix.
- Clearing the form after an error. Keep what the user typed, so they correct it rather than start over.
- Blocking paste or autofill on login fields. Remove the script; it also puts you at odds with 3.3.8.
- Putting
aria-invalid="true"on every empty field at page load. Set it after validation only. - Building custom dropdowns from
divelements. Use a nativeselectwhere you can. - Writing "Invalid input". Say what is wrong and how to fix it.
Frequently asked questions
Is placeholder text enough as a label?
No. MDN says the placeholder is not a label and should not be a substitute. It vanishes on typing, may not be announced, and may be skipped by translation tools.
Should I validate when the user leaves a field or on submit?
Either can be accessible if the messages are announced and tied to the field. W3C suggests validating strict formats such as dates when the user leaves the field, not while typing. A summary after submit is the safer base.
Do CAPTCHAs fail WCAG 2.2?
Not automatically. Under 3.3.8 a cognitive function test is allowed if there is an alternative, a helper mechanism, or the test is object recognition or identifying content the user provided. Check which applies before choosing one.
Related guides
- Keyboard Accessibility: Test Focus, Tab Order and Skip Links
- WCAG 2.2 Checklist: Level AA Items for Small Business Websites
- Website Accessibility Audit: Fix the Most Common Problems
- Mobile Usability: Fix Viewport, Tap Targets and Readable Text
- Color Contrast and WCAG: Ratios, Checks and Fixes
Official references
- https://www.w3.org/WAI/tutorials/forms/
- https://www.w3.org/WAI/tutorials/forms/labels/
- https://www.w3.org/WAI/tutorials/forms/validation/
- https://www.w3.org/WAI/tutorials/forms/notifications/
- https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html
- https://www.w3.org/WAI/WCAG22/Understanding/redundant-entry.html
- https://www.w3.org/WAI/WCAG22/Understanding/accessible-authentication-minimum.html
- https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html
- https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete
- https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/label
- https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input
- https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-invalid
- https://web.dev/learn/forms/accessibility
Next step: Try the free website sample to see how your own site does on a few of these checks. This guide provides technical information, not a guarantee of rankings, traffic or index inclusion.
Crawvix Editorial Team
· Published November 4, 2026 · Last updated November 4, 2026 · 10 min read