Plugin overview

Accessible form validation that keeps your real form markup.

A11y Form Validator enhances existing labels, inputs, selects, textareas, checkboxes, radio groups, and fieldsets. It reads native constraints, supports custom rules, async validation, and server errors, renders accessible inline messages, can add a focusable summary and character counts, supports opt-in locale packs, and cleans up generated DOM when destroyed.

What it does

Validation behavior, messages, and state in one small layer

The plugin is designed as progressive enhancement: your form stays useful HTML, and JavaScript adds validation state, messages, focus handling, and optional addons.

Native rules

Reads HTML constraints

Supports required fields, email inputs, minlength, maxlength, pattern, required checkboxes, radio groups, and checkbox groups.

Custom rules

Add rules and messages

Use data-validate, data-message-*, global messages, field messages, or registered rules for product-specific validation.

Errors

Inline messages

Generated errors connect to fields with aria-describedby and aria-errormessage, while preserving existing descriptions.

Summary

Focusable error summary

The summary addon creates a labelled, focusable section with links to invalid fields and can receive focus after blocked submits.

Addon

Character count

Controls with maxlength, minlength, or data-character-count can expose a polite count message.

Server

Server errors

Apply field-level and form-level server errors with setErrors() after failed sign-in, registration, checkout, or session checks.

Async

Remote validation

Async custom rules support flows such as username availability or invite-code checks. Request cancellation remains the application responsibility.

Lifecycle

Clean init and destroy

Duplicate initialization returns the existing instance, and destroy() removes listeners, generated errors, addon DOM, timers, classes, and ARIA state.

Locales

Message packs

English messages stay in the main bundle. Other locale JSON files are opt-in and can be imported or supplied inline during initialization.

Quick start

Enhance a normal form

Build the package before opening local demos. Demo pages import from ./dist/index.js and ./dist/styles.css; the direct browser demo imports ./dist/index.min.js.

1. Keep semantic HTML

<link rel="stylesheet" href="./dist/styles.css" />

<form data-a11y-form-validator>
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required />

  <label for="message">Message</label>
  <textarea
    id="message"
    name="message"
    required
    minlength="10"
    maxlength="180"
    data-character-count
  ></textarea>

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

2. Initialize the plugin

<script type="module">
  import {
    createDefaultPreset,
    createFormValidator
  } from "./dist/index.js";

  const form = document.querySelector(
    "[data-a11y-form-validator]"
  );

  if (form instanceof HTMLFormElement) {
    createFormValidator(form, createDefaultPreset());
  }
</script>

API at a glance

Start with one form, then add options only when the integration needs them

Use createDefaultPreset() when the page should include the error summary addon. Use createNoSummaryPreset() for the same submit and blur validation pattern without summary UI. Keep the returned instance when your app needs server errors, dynamic fields, custom rules, or validation events.

Start here

createFormValidator(form, options)

Enhances one HTMLFormElement and returns the validator instance. Calling it again on the same form returns the existing instance.

Many forms

initFormValidators(options, root)

Finds every [data-a11y-form-validator] form under root and initializes each one with the same options.

Presets

createDefaultPreset(), createNoSummaryPreset(), or createMinimalPreset()

Default includes the error summary addon. No-summary keeps submit and blur validation without summary UI. Minimal validates on submit and focuses the first invalid field.

Advanced

A11yFormValidator

Direct class construction is available for class-based integrations, but the factory is the simplest entry point for most pages.

Options you will usually touch first

  • validateOn chooses submit, blur, input, or change triggers.
  • focusOnError sends focus to the summary, first invalid field, or nowhere.
  • errorMode chooses inline, native, or both error presentation.
  • addons accepts imported addon objects such as the error summary or character count helpers.

Control validation from your app

  • validate() checks the whole form.
  • validateField() checks one field by name or element.
  • refresh() rescans fields after dynamic markup changes.
  • getErrors() and getState() expose the current state.

Handle server errors and cleanup

  • setErrors() renders field and form errors from your backend.
  • clearErrors() removes rendered validation messages.
  • focusOnError() moves focus according to your configured behavior.
  • destroy() removes listeners, generated DOM, classes, and ARIA state.

Extend behavior when native rules are not enough

  • rules adds field-specific validation configuration.
  • messages, locales, and locale customize error text.
  • registerRule() and unregisterRule() manage custom rules.
  • renderer replaces the default inline error rendering.

Accessibility behavior

Designed to preserve native form interaction

Messages and descriptions

Inline errors are tied to controls with generated IDs, polite live regions, and restored aria-describedby values when errors clear or the plugin is destroyed.

Focus after blocked submit

Depending on focusOnError, focus can move to the summary, the first invalid field, or stay under application control.

Groups stay grouped

Radio groups and checkbox groups sharing a name are treated as grouped fields, with errors rendered outside option labels.

Native keyboard behavior

Tab moves through fields and summary links, Enter submits forms or activates links, and Space toggles native checkboxes and radio buttons.

Demo gallery

Choose the example that matches the integration problem

Each demo focuses on one behavior so the markup, rule setup, and validation state are easy to inspect.

Start here

Basic

Semantic contact form with inline errors, a summary, grouped choices, and character count guidance.

Best for
Default preset and addon setup.
Try this
Submit the empty form, then fix each linked field.
View demo
Minified

Direct Browser Build

Same semantic form enhancement imported directly from the optional minified ESM file.

Best for
Static-site or CDN-style usage without a bundler.
Try this
Inspect the module import and submit the empty form.
View demo
Minimal

Contact Form

A small form using the minimal preset and native required, email, and minlength checks.

Best for
Seeing the smallest useful integration.
Try this
Submit with a short message, then add enough text.
View demo
CMS

CMS Markup

Data-attribute rules and messages for markup produced by content systems or templates.

Best for
data-validate and data-message-*.
Try this
Leave email blank, then enter an invalid address.
View demo
Summary

Error Summary

Labelled, focusable summary region with links that send users back to invalid fields.

Best for
Blocked submit recovery and field navigation.
Try this
Submit empty, then follow a summary link.
View demo
Custom

Registration

Password confirmation with a custom same-as rule and custom error copy.

Best for
Cross-field validation.
Try this
Use two different passwords and submit.
View demo
Auth flow

Login / Register

Switch between login and registration forms while only the visible form is active and validated.

Best for
Hidden form boundaries, focus movement, and scoped validation.
Try this
Submit login empty, switch to register, then test mismatched passwords.
View demo
Conditional

Checkout

Conditional billing validation plus required shipping and payment method choices.

Best for
Rules that depend on another control.
Try this
Check different billing, leave billing address empty, and submit.
View demo
Integration

Conditional Fields

Connect conditional sections with validation refresh, visible-only required fields, and summary updates.

Best for
Using a11y-conditional-fields with this validator.
Try this
Show Enterprise billing, submit empty, then hide it again.
View demo
Async

Remote Validation

Username availability checked with an async custom rule and debounced input validation.

Best for
Remote checks and pending state review.
Try this
Enter admin or editor.
View demo
Server

Server Errors

Apply server-provided field and form messages after a failed request.

Best for
setErrors() and form-level messages.
Try this
Use the mock server error button.
View demo
Locales

Localization

Compare English fallback, imported locale JSON, and inline locale messages.

Best for
Keeping non-English copy out of the main bundle.
Try this
Submit each empty form and compare the generated messages.
View demo
Lifecycle

Dynamic Locale

Destroy and reinitialize the validator to swap runtime locale packs and regenerate errors.

Best for
destroy(), locale options, and generated DOM cleanup.
Try this
Validate the empty form, switch languages, then destroy and reinitialize.
View demo

Integration notes

A few boundaries to keep in mind

  • Run npm run build before opening local demos because they import from dist.
  • Bundler users should keep the normal package entry; direct browser users can import dist/index.min.js or a11y-form-validator/min.
  • Custom widgets need custom rules, a custom renderer, or extra integration code.
  • Async validation can return promises, but request cancellation belongs to the application.
  • Final product forms should still be tested with target browsers and assistive technologies.