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
idvalues on a page: association silently breaks and only the first match wins. - Using
aria-labeleverywhere, which removes the visible label that sighted and cognitive-disability users depend on. - A
<div>styled as a label with no real<label>element oraria-labelledbyreference. - 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.