Fix Guide

How to Build Accessible Forms in WordPress

August 5, 2026

Forms are where your website stops talking and starts listening: contact requests, orders, registrations, searches. They are also where accessibility fails most often. The WebAIM Million study, which scans the top million home pages every year, consistently finds missing form input labels on almost half of them, making it one of the most common accessibility failures on the web. The frustrating part: accessible forms are not hard. A handful of HTML attributes, correctly used, decides whether a blind user can send you a message or silently gives up, and whether your form meets WCAG, which the European Accessibility Act has made a legal requirement for many businesses. This guide walks through the five things that matter, with copy-paste markup, and ends with an honest look at WordPress form plugins.

Why are forms the biggest accessibility problem?

A sighted mouse user reads a form as a visual whole: the text next to a box obviously belongs to it, the red border obviously means an error. Assistive technology has none of this context. A screen reader user tabs from control to control and hears only what the markup explicitly connects to each field. If the connection is missing, the user hears edit text, blank, and has to guess what the field wants. Voice control users face a parallel problem: saying click email only works if the field is programmatically named email.

That is the core principle behind every rule in this guide: every visual relationship in a form must also exist in the markup. Label to field, hint to field, error to field, group to options. The relevant WCAG success criteria (1.3.1 Info and Relationships, 3.3.1 Error Identification, 3.3.2 Labels or Instructions) all reduce to this one idea.

How to label form fields correctly

The foundation of every accessible form is the label element, connected to its field with the for attribute pointing at the field's id:

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

<!-- Additional hint, connected via aria-describedby -->
<label for="password">Password</label>
<input type="password" id="password" name="password" aria-describedby="password-hint">
<p id="password-hint">At least twelve characters.</p>

This buys you three things at once: screen readers announce the label when the field receives focus, clicking the label focuses the field (a bigger tap target on mobile for everyone), and voice control can address the field by its name. Hints and format instructions go into a separate element referenced by aria-describedby, so they are announced after the label without cluttering it. The autocomplete attribute is a quiet bonus: for fields collecting personal data, WCAG 1.3.5 asks for it, and it lets browsers fill the field correctly, which helps users with motor and cognitive limitations most of all. The W3C forms tutorial covers every variation of these patterns.

Why a placeholder is not a label

The placeholder-only form field is the most common accessible-looking mistake on the web. It looks clean, and it fails four ways at once:

  • The text disappears on input. The description vanishes exactly when the user is working with the field. Anyone who has forgotten what a half-filled field was asking has felt this.
  • The default gray fails contrast. Typical placeholder styling does not reach the 4.5:1 contrast ratio WCAG requires for text.
  • Assistive technology support is inconsistent. Some screen reader and browser combinations announce placeholders, some do not. A label is announced by all of them.
  • Filled fields look empty, empty fields look filled. Users scanning for what is left to complete get it backwards.

Placeholders are fine as a supplement, for example a format sample like name@example.com inside a properly labeled field. As the only description of a field, they are a WCAG 3.3.2 failure with good typography.

How to mark required fields accessibly

Two channels, always together. The machine channel is the HTML required attribute, which makes browsers and screen readers announce the field as required and enables native validation. The human channel is visible text: the word required (or its opposite, optional) in the label. The popular red asterisk alone fails on both counts: color alone may not carry information (WCAG 1.4.1), and the asterisk convention, if you use it anyway, must at least be explained above the form and included in the label so it is announced.

<label for="name">Name (required)</label>
<input type="text" id="name" name="name" required>

A pragmatic inversion often produces the cleanest forms: if almost every field is required, mark only the optional ones, and say so above the form. And the best required field is the one you delete; every field a form does not ask for is one that cannot be filled in wrong.

How to make error messages accessible

Error handling is where forms lose the users they have not lost yet. WCAG 3.3.1 requires that errors are identified in text and described to the user. The robust pattern per field:

<label for="email">Email address (required)</label>
<input type="email" id="email" name="email" required
       aria-invalid="true" aria-describedby="email-error">
<p id="email-error" class="error">
	Please enter an email address, for example name@example.com.
</p>

The pieces: aria-invalid="true" marks the field as faulty so screen readers announce invalid entry on focus, the message is real text (not just a red border, not just an icon) and is tied to the field with aria-describedby, and the wording says how to fix the problem, not just that there is one. Error, please correct helps nobody; Please enter a date in the format DD.MM.YYYY does.

For validation that runs on submit, one more step matters: after a failed submission, move keyboard focus to the first faulty field, or render an error summary at the top with role="alert" and links to the affected fields. A screen reader user who submits and hears nothing assumes the form worked. Silence after a failed submit is the single cruelest form bug there is.

Fieldset and legend: grouping radio buttons and checkboxes

Radio buttons and checkbox groups have a two-level structure: the group has a question, each option has a label. Both levels must be in the markup, and the native elements for the group level are fieldset and legend:

<fieldset>
	<legend>Preferred contact method (required)</legend>
	<input type="radio" id="contact-email" name="contact" value="email">
	<label for="contact-email">Email</label>
	<input type="radio" id="contact-phone" name="contact" value="phone">
	<label for="contact-phone">Phone</label>
</fieldset>

Without the fieldset, a screen reader user tabbing into the group hears only Email, radio button, one of two, and has no idea what question the options answer. With it, the legend is announced first. The same structure serves checkbox groups and is worth keeping even when a design system restyles the elements beyond recognition; the semantics cost nothing and carry everything.

Are WordPress form plugins accessible?

Mostly, they can be, and none of them guarantees it. The honest per-plugin picture: Contact Form 7 renders whatever you write in its form template, so labels, fieldsets and error wiring are entirely in your hands; the flexibility that makes it popular also makes label-less forms easy to ship. Gravity Forms overhauled its markup with accessibility as an explicit goal a few years ago and produces solid output in current versions. WPForms has likewise invested in accessible defaults. In every case, three things can still break an accessible plugin: your theme's styling (focus outlines removed, contrast destroyed), CAPTCHAs (a fully inaccessible barrier; honeypot approaches avoid the problem), and custom field types added on top.

So test the rendered result, not the marketing page. The two-minute version: unplug your mouse and complete the form with the keyboard alone, checking that you can see focus at every step, then submit it empty and check whether the error messages are announced and reachable. Automated checkers like WAVE catch the structural half, including missing labels, in seconds.

How InspectWP helps you find form problems

The InspectWP report examines your rendered WordPress site the way a browser sees it, and several checks bear directly on forms. It flags forms whose action submits to an insecure HTTP address, a problem that browsers punish with warnings exactly at the moment a visitor is about to trust you with data. Beyond that, the report covers the accessibility fundamentals around your forms: images without alt attributes, the heading hierarchy that screen reader users navigate by, and console errors that often reveal broken validation scripts. A free report is a fast first pass over the page your form lives on.

Check your WordPress site now

InspectWP analyzes your WordPress site for security issues, SEO problems, GDPR compliance, and performance โ€” for free.

Analyze your site free