AccessScanRun a free scan

Guide

How to Build Accessible Forms: A Developer's Guide

Forms are where accessibility most often breaks down, and where the cost of getting it wrong is highest: a contact form a blind user cannot complete, a checkout an audit flags, a sign-up that traps keyboard users. Accessible forms are not about ARIA sprinkles. They come down to five concrete, testable practices: visible labels, programmatic association, clear required-field handling, error identification with suggestions, and correct autocomplete.

This guide walks through each with working markup. Everything maps to specific WCAG 2.2 success criteria at Level A and AA, the baseline referenced by EN 301 549 for the European Accessibility Act, which applies from 28 June 2025. When you are done, run your form through the free AccessScan accessibility scanner to catch what manual review misses.

Visible labels: never rely on placeholders

Every input needs a persistent, visible label. The most common anti-pattern is using placeholder text as the label: it vanishes the moment the user types, offers no way to recover the field's purpose, and usually fails the 4.5:1 contrast minimum because placeholder grey is too light. Floating labels are acceptable only if the label remains visible after the field is filled.

A visible label also enlarges the target: clicking the label text focuses the input, which helps users with motor impairments. Use the placeholder, if at all, only for a genuine format hint like DD/MM/YYYY, never as a substitute for the label.

Programmatic association: wire labels to inputs

A label that only looks associated is not associated. Screen readers announce a field's name from its programmatic label, so the connection must exist in the markup. The most robust method is a <label> whose for attribute matches the input's id:

<label for="email">Email address</label> paired with <input id="email" type="email" name="email">. Wrapping the input inside the <label> also works. This satisfies WCAG 1.3.1 (Info and Relationships) and 4.1.2 (Name, Role, Value).

Watch for these failure modes:

  • Duplicate id values on a page: association silently breaks and only the first match wins.
  • Using aria-label everywhere, which removes the visible label that sighted and cognitive-disability users depend on.
  • A <div> styled as a label with no real <label> element or aria-labelledby reference.
  • Composite controls (date pickers, custom selects) built from <div>s without a name, role, or value: these need a role, an accessible name, and keyboard support, or a native element instead.

Required fields: state it in text and in code

Mark required fields in two channels. Programmatically, add the native required attribute (or aria-required="true" on custom widgets) so assistive technology announces the state. Visually, say so in the label: "Email (required)" is unambiguous. If you use an asterisk, it must not be the only signal, because colour and symbols alone fail WCAG 1.4.1 (Use of Colour) and 3.3.2 (Labels or Instructions).

Explain any constraints before the user hits submit. If a password needs 12 characters, say so in associated help text wired up with aria-describedby, not only in the error that appears after a failed attempt.

Error identification and suggestion

When validation fails, three things must happen. First, identify the error in text: "Enter a valid email address," not a red border alone (WCAG 3.3.1, Error Identification). Second, point to the offending field by associating the message with aria-describedby and setting aria-invalid="true". Third, suggest a fix when you know one: "Use the format name@example.com" or "Date must be in the future" (WCAG 3.3.3, Error Suggestion).

Practical guidance for error UX that actually reaches assistive tech:

  • Move focus to the first invalid field, or to a summary at the top of the form that links to each error.
  • Render a single error summary in a container with role="alert" so screen readers announce it without the user hunting for it.
  • Validate on submit or on blur, never character-by-character, which fires a barrage of announcements mid-typing.
  • Keep the error message adjacent to its field and inside the field's accessible description, not floating elsewhere on the page.

Autocomplete: identify the input purpose

WCAG 1.3.5 (Identify Input Purpose, Level AA) asks you to declare what common personal-data fields collect, using the standard HTML autocomplete tokens. This lets browsers and password managers fill fields correctly and lets assistive tools present them in a familiar way, a real benefit for users with cognitive and motor disabilities.

Use specific tokens: autocomplete="email", autocomplete="given-name", autocomplete="family-name", autocomplete="tel", autocomplete="street-address", autocomplete="postal-code", autocomplete="cc-number". Avoid a blanket autocomplete="off" on personal-data fields: it defeats the criterion and frustrates users. Pair the token with the matching input type (type="email", type="tel") so mobile keyboards adapt too.

Test it, then verify against the standard

Manual testing finds what linters cannot. Tab through the entire form with the keyboard alone: every field reachable, focus visible, logical order, no traps. Then run a screen reader and confirm each field announces its name, its required state, and any error. This is the same keyboard-first discipline covered in our keyboard accessibility guide.

Forms are only one part of compliance. Work through the full accessibility checklist for structure, colour, and media, and when you are ready to publish, the accessibility statement generator produces the statement the EAA expects. A note on scope: microenterprises that provide services (fewer than 10 staff and under EUR 2,000,000 annual turnover) are largely exempt for those services, though building accessible forms remains good practice regardless.

Check your site against AccessScan

See your issues ranked by impact in seconds — free.

Run a free accessibility scan

FAQ

Is a placeholder a valid replacement for a label?

No. Placeholder text disappears as soon as the user types, leaving no persistent label, and many screen readers do not announce it reliably as the field's name. It also tends to fail the 4.5:1 contrast requirement because placeholder grey is usually too light. Always provide a real, visible <label> and use the placeholder only for an optional example of the format.

How do I mark a field as required accessibly?

Add the native required attribute (or aria-required="true" for custom widgets) so assistive technology announces the state, and also indicate it visually in the label text, for example "Email (required)" or an asterisk with a legend explaining it. Do not rely on colour or an asterisk alone, since that fails WCAG 1.4.1 (Use of Colour) and 3.3.2 (Labels or Instructions).

Which WCAG success criteria govern accessible forms?

The core ones are 1.3.1 Info and Relationships and 4.1.2 Name, Role, Value (label association), 3.3.1 Error Identification, 3.3.2 Labels or Instructions, 3.3.3 Error Suggestion, and 1.3.5 Identify Input Purpose (autocomplete). All are Level A or AA in WCAG 2.2, the baseline referenced by EN 301 549 for the European Accessibility Act.

Does aria-label remove the need for a visible label?

It satisfies the programmatic name requirement, but it removes the visible label that sighted users, including people with cognitive disabilities, rely on, and it cannot be clicked to focus the field. Prefer a visible <label> with a matching for/id. Reserve aria-label for cases like a search field where the visual design genuinely has no room for text.

More guides