splitforms.com
← Back to the journal

Accessible Contact Forms: WCAG 2.2 Checklist (2026)

Make your contact form WCAG 2.2 compliant: visible labels, error messages, focus order, autocomplete, target size, and CAPTCHA pitfalls — with code examples.

Start free — 500 submissions/moSee pricing →No credit card. Paid plans from $5/mo.

TL;DR: why forms are the #1 accessibility complaint

Forms are where accessibility failures stop being theoretical. A missing altattribute on a decorative image is a minor miss; a contact form a screen reader user can't fill in, or a keyboard user can't submit, blocks the entire task the page exists for. That's why forms — login, checkout, signup, and contact — generate a disproportionate share of accessibility lawsuits and audit findings: they combine several WCAG success criteria (labeling, error handling, focus management, color contrast, input purpose) in one interactive component, and a single missed criterion can make the whole form unusable rather than just harder to use.

The good news is that contact forms are small enough to get right completely. This is a practical checklist mapped to the actual WCAG success criteria that apply, including the three new checks WCAG 2.2 added in October 2023 — target size, redundant entry, and accessible authentication (the one that governs CAPTCHAs). Each section below has a criterion number, what it requires, and working code.

Labels done right (WCAG 1.3.1, 3.3.2)

1.3.1 Info and Relationships (Level A) requires that information conveyed visually — like "this text describes that field" — is also available programmatically. 3.3.2 Labels or Instructions (Level A) requires that every input collecting user data has a visible label or instruction. Together they rule out the most common form-accessibility mistake: using placeholder text as the only label.

Placeholder-as-label fails for three concrete reasons. It disappears the instant a user starts typing, so anyone who looks away and back has lost the field's purpose. It isn't consistently announced by screen readers the way a real label is — support varies by browser and assistive-tech combination. And low-contrast placeholder styling (common by default) can fail 1.4.3 contrast on its own, on top of the labeling problem.

<!-- Before: placeholder as label — fails 1.3.1 and 3.3.2 -->
<input type="email" name="email" placeholder="Email address" />

<!-- After: a real, programmatically-associated label -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" required />

The for attribute on the label must match the input's idexactly — that pairing is what lets a screen reader announce "Email address, edit text" when the field receives focus, and it's also what makes clicking the label text focus the input. If you need compact visual styling, shrink the label's font size or reposition it — don't delete it. A placeholder can still add a supplementary hint (e.g. "you@company.com") alongside the label, just never in place of it.

Error messages that assistive tech can actually use

3.3.1 Error Identification (Level A) requires that when a submission fails validation, the error is described in text, not just implied by a color change. 3.3.3 Error Suggestion (Level AA) requires that where possible, the message says how to fix it — not just that something's wrong. Both criteria assume the error text is actually reachable by assistive tech, which is where aria-describedby and aria-invalid come in.

<label for="email">Email address</label>
<input
  type="email"
  id="email"
  name="email"
  required
  aria-invalid="true"
  aria-describedby="email-error"
/>
<p id="email-error" role="alert" style="color:#c0392b">
  Enter a valid email address, like you@company.com.
</p>

aria-describedby points from the input to the error paragraph's id, so a screen reader announces the error text right after the field's label whenever the field is focused — not just once, in passing, when the page first renders. aria-invalid="true"marks the field itself as failing validation, which most screen readers announce as "invalid" alongside the label. Toggle both attributes off once the field is corrected, and only add them after a real validation failure — marking every empty required field invalid before the user has even tried defeats the purpose.

On submit failure, move focus to a summary of what went wrong rather than leaving focus wherever it was (or resetting it to the top of the page). A short, focusable error summary above the form, linking to each broken field, satisfies both 3.3.1 and 2.4.3 focus order at once:

function handleSubmit(e) {
  e.preventDefault();
  const errors = validate(formData); // returns [{ id, message }]
  if (errors.length > 0) {
    renderErrorSummary(errors);
    document.getElementById("error-summary").focus();
    return;
  }
  form.submit();
}

// Error summary markup — tabindex="-1" makes it focusable via script
// without adding it to the normal tab order.
// <div id="error-summary" role="alert" tabindex="-1">
//   <p>2 fields need attention:</p>
//   <ul>
//     <li><a href="#email">Email address — enter a valid email</a></li>
//     <li><a href="#message">Message — this field is required</a></li>
//   </ul>
// </div>

role="alert"on the summary causes most screen readers to announce it immediately when it appears, without the user needing to navigate to find it — critical for anyone who can't visually scan the page for a new red box.

Keyboard, focus order, and target size

2.4.3 Focus Order (Level A) requires that tabbing through the form follows a sequence that preserves meaning — top to bottom, left to right for a typical single-column contact form. Don't reorder fields visually with CSS (like flex-direction: row-reverse or an order property) without also reordering the underlying DOM, or the tab sequence and the visual sequence diverge and confuse anyone tabbing through.

2.4.7 Focus Visible (Level AA) requires that whichever element currently has keyboard focus is visibly distinguishable from unfocused elements. The most common violation is a global * { outline: none; }reset with no replacement focus style — it makes the page unusable for anyone who navigates by keyboard, since there's no way to see where you are.

/* Never do this without a replacement: */
button:focus, input:focus { outline: none; }

/* Do this instead — visible, meets 3:1 contrast against the background */
input:focus-visible,
button:focus-visible {
  outline: 2px solid #1a73e8;
  outline-offset: 2px;
}

2.5.8 Target Size (Minimum), Level AA, is new in WCAG 2.2. It requires every interactive target — submit buttons, checkboxes, radio buttons — to measure at least 24 by 24 CSS pixels, unless it's inline in a sentence or has enough spacing from neighboring targets that a 24px circle around it doesn't overlap another target. A default unstyled checkbox is roughly 13×13px in most browsers — under the minimum — so pad the clickable area or the checkbox itself:

input[type="checkbox"],
input[type="radio"] {
  width: 24px;
  height: 24px;
}
/* Or keep the visual size smaller and pad the hit area: */
.checkbox-wrap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
}

Autocomplete attributes (WCAG 1.3.5)

1.3.5 Identify Input Purpose (Level AA) requires that fields collecting common user information — name, email, phone, address — carry the matching HTML autocompletetoken from the standard list. This isn't just an accessibility nicety: it's also what lets browsers and password managers autofill the field correctly, and it helps users with cognitive or motor disabilities who rely on saved profile data instead of retyping everything by hand.

<label for="name">Full name</label>
<input type="text" id="name" name="name" autocomplete="name" required />

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

<label for="phone">Phone number</label>
<input type="tel" id="phone" name="phone" autocomplete="tel" />

For a split name field, use autocomplete="given-name" and autocomplete="family-name" instead of two fields both set to "name". The full token list and browser autofill behavior are covered in more depth in the HTML autocomplete attribute guide — worth a read if your form collects address or payment-adjacent fields beyond the basic contact trio.

WCAG 2.2 specifics: redundant entry and CAPTCHA

3.3.7 Redundant Entry(Level A) requires that information a user already supplied earlier in the same process isn't asked for again from scratch — it should be auto-populated, selectable, or otherwise available without re-typing. For a single-page contact form this rarely applies, but it matters the moment you split a form across steps (name and email on step one, project details on step two) or add a "confirm your email" field that makes the user type the same address twice with no autofill or copy affordance.

3.3.8 Accessible Authentication (Minimum)(Level AA) is the criterion most relevant to spam-blocking, because it's the one a visible CAPTCHA usually trips. It prohibits a process step that relies solely on a "cognitive function test" — remembering a password, solving a puzzle, transcribing distorted text — unless there's a mechanism that doesn't require that test, assistance is available, or the test involves recognizing objects/content the user personally provided. A classic distorted-text image CAPTCHA with no fallback fails this outright; it demands a cognitive test (reading obscured characters) as the only path to submit.

This is exactly why server-side spam filtering is the accessibility-friendly answer for a contact form. A honeypot field, a time-trap that flags submissions completed faster than a human plausibly could, and IP-based rate limiting block the large majority of automated spam without asking any visitor — disabled or not — to prove anything. splitforms' endpoint (https://splitforms.com/api/submit) runs exactly that stack by default: no widget, no puzzle, no 3.3.8 conversation to have with an auditor.

<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />

  <label for="name">Full name</label>
  <input type="text" id="name" name="name" autocomplete="name" required />

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

  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <!-- Honeypot: hidden from sighted AND assistive-tech users -->
  <input
    type="checkbox"
    name="botcheck"
    style="display:none"
    tabindex="-1"
  />

  <button type="submit">Send message</button>
</form>

Notice how the honeypot is hidden: display:none removes it from both the visual layout and the accessibility tree, and tabindex="-1"takes it out of keyboard tab order as a second layer of protection. That combination matters — a honeypot hidden only with a "visually-hidden" utility class (clipped with CSS but still focusable) can trap a screen reader or keyboard user on a field they can't see and were never meant to interact with, which is its own accessibility bug. Bots that parse the raw DOM fill the field in regardless of CSS, so hiding it correctly costs nothing in spam-catching power.

If you still want a visible challenge on top — for a high-value form, or because a stakeholder wants the reassurance of a checkbox — optional reCAPTCHA or Turnstile remain available as an opt-in addition on splitforms rather than a default. Before reaching for either, see best CAPTCHA for contact formsfor how each one scores on accessibility, since some (Cloudflare Turnstile's non-interactive mode, hCaptcha's accessibility cookie) come closer to satisfying 3.3.8 than a classic image challenge does.

Compact checklist

CheckCriterionLevel
Every input has a real, associated <label for>1.3.1, 3.3.2A
Errors described in text, linked via aria-describedby3.3.1A
Error text suggests how to fix the problem3.3.3AA
Tab order matches visual reading order2.4.3A
Focus is visibly distinguishable on every control2.4.7AA
Text and UI contrast meets 4.5:1 / 3:11.4.3AA
Name/email/tel fields carry standard autocomplete tokens1.3.5AA
Interactive targets are at least 24×24px2.5.8 (new in 2.2)AA
No re-asking for data already given this process3.3.7 (new in 2.2)A
No cognitive-test-only CAPTCHA with zero alternative3.3.8 (new in 2.2)AA
Honeypot fields hidden with display:none + tabindex="-1"Best practice

All ten WCAG rows are Level A or AA success criteria; the eleventh row is an implementation best practice, not a numbered criterion.

Testing quickly: keyboard, screen reader, axe

Three passes catch most contact-form accessibility bugs in under fifteen minutes total. First, unplug the mouse mentally and tab through the entire form from the page's natural entry point: every field, every button, and any inline links should receive focus in a sensible order, with a visible focus indicator at each stop, and Enter should submit the form from any text field.

Second, run a screen reader smoke test. On a Mac, Cmd+F5toggles VoiceOver; on Windows, NVDA is free and industry-standard. Tab through the same form and listen: each field should announce its label, its type, and — after a failed submit — its error. If a field announces only "edit text" with no label, or an error appears silently with no announcement, that's a 1.3.1 or 3.3.1 failure to fix.

Third, run an automated scan — axe DevTools (browser extension) or Lighthouse's built-in accessibility audit in Chrome DevTools. Automated tools reliably catch missing labels, insufficient contrast, and missing autocompleteattributes, but they can't judge whether your error messages are actually helpful or whether focus lands in a sensible place — that's what the first two passes are for. Use all three; none of them alone is sufficient.

Next steps and where to get help

  • Need the HTML fast? The HTML form generator outputs labeled, autocomplete-tagged markup you can paste straight in rather than hand-rolling every attribute above.
  • See the full server-side spam stack — honeypot, time-trap, rate limiting, all included free — on the spam-protection feature page.
  • Accessibility fixes and conversion fixes overlap more than people expect: clearer labels and fewer dead-end errors are also core recommendations in contact form conversion rate benchmarks and fixes.
  • Weighing whether you need a CAPTCHA at all under 3.3.8? Best CAPTCHA for contact forms scores each vendor on accessibility, not just spam-blocking.

FAQ

What makes a contact form WCAG compliant?

Every field needs a programmatically-associated <label> (WCAG 1.3.1), errors must be announced in text and tied to the field with aria-describedby (3.3.1, 3.3.3), focus must move to the error on failed submit (2.4.3), interactive elements need a visible focus style (2.4.7) and sufficient color contrast (1.4.3), and inputs that map to a standard type need the matching autocomplete token (1.3.5). WCAG 2.2 adds three more: touch targets at least 24×24px (2.5.8), no re-asking for information already given in the same process (3.3.7), and no cognitive-function-test-only authentication like a puzzle CAPTCHA (3.3.8).

Is a placeholder enough instead of a label?

No. Placeholder text disappears the moment a user types, isn't reliably announced by every screen reader the same way a label is, and fails WCAG 1.3.1 and 3.3.2 on its own because it isn't a persistent, programmatically-associated instruction. Use a visible <label for="id"> and, if you want compact styling, style the label smaller rather than removing it.

Does WCAG 2.2 ban CAPTCHAs?

Not outright, but 3.3.8 Accessible Authentication (Minimum) bans authentication steps that rely solely on solving a cognitive function test — like transcribing distorted text — unless an equivalent non-cognitive alternative exists. A visible image or audio CAPTCHA with no alternative fails this. The practical fix most teams reach for is skipping visitor-facing challenges entirely and filtering spam server-side instead.

What's the WCAG 2.2 target size requirement?

Success Criterion 2.5.8 Target Size (Minimum), Level AA, requires interactive targets — buttons, checkboxes, radio inputs, links — to be at least 24 by 24 CSS pixels, unless the target is inline in a sentence, has an equivalent same-size alternative nearby, or the spacing to adjacent targets makes a 24px circle around it clickable without overlap. It's a new criterion in 2.2, not present in WCAG 2.1.

How do I test a form for accessibility quickly?

Three passes catch most issues in under 15 minutes: tab through the entire form with the mouse untouched and confirm a visible focus ring follows every stop in a logical order; run a screen reader smoke test (VoiceOver on Mac with Cmd+F5, NVDA on Windows) and listen for each field to announce its label and any error; and run axe DevTools or Lighthouse's accessibility audit in Chrome for automated checks like missing labels and contrast failures. Automated tools only catch a subset of issues by design — they can't judge whether an error message is actually helpful or whether focus lands somewhere sensible, so the keyboard and screen reader passes still matter.

What autocomplete value should a phone number field use?

Use autocomplete="tel" for a single combined phone field. For contact forms, the common trio is autocomplete="name" (or given-name / family-name if split), autocomplete="email", and autocomplete="tel" — all standard tokens defined in the HTML spec and required by WCAG 1.3.5 for fields that collect user identity information.

Is a hidden honeypot field an accessibility problem?

Only if you hide it the wrong way. A honeypot built with display:none and tabindex="-1" is invisible to sighted users, screen readers, and keyboard tab order alike — bots that parse the DOM still fill it in, but no human, assistive-tech or not, ever encounters it. The mistake to avoid is a "visually hidden but still focusable" pattern (like clip-path tricks without tabindex="-1"), which can trap a screen reader or keyboard user on a field they can't see and shouldn't fill in.

Do error messages need to be in red text only?

No — and they shouldn't be. Color alone fails WCAG 1.4.1 (Use of Color). Pair red or another accent color with an icon, the word "Error" or similar, and a text description of what's wrong, so a color-blind or low-vision user gets the same information as everyone else.

Want a contact form backend that skips visitor-facing CAPTCHAs by default? Get a free splitforms access key — server-side honeypot, time-trap, and rate limiting ship on every plan, and email notifications are free on all of them too.

Related articles

More practical guidance from guides.

Browse the journal →
Guides

Elementor Form Not Sending Email? 5 Fixes That Work (2026)

Why Elementor form notifications never arrive: the wp_mail root cause, how to confirm it in

8 min readRead →
Guides

Why mailto: Doesn't Work as a Form Action (and What Converts 3–10× Better)

Using action="mailto:you@example.com" opens the visitor's mail app instead of sending anythi

6 min readRead →
Guides

Why Your Form Works in Preview but Not on the Live Site

Form submits perfectly in your builder's preview but fails in production? The usual causes:

7 min readRead →

Explore this topic

Start with the overview, then move into focused guides.

OverviewContact Forms for Every FrameworkStart here →GuideContact Form for Next.jsRead →GuideContact Form for ReactRead →GuideContact Form for VueRead →ReferenceContact form guideOpen →

Building forms with ChatGPT, Claude, Cursor, or v0? Connect the native MCP server and give your agent a production form backend.

Explore the MCP server →

Give your form a production backend.

One endpoint adds delivery, spam filtering, storage, and integrations. Start with 500 submissions a month for free.

Create free accountRead the docs →
Secure checkoutSSL encryptionPrivacyProtected
VISAAMERICANEXPRESSstripe