Docs › European Accessibility Act

EAA Form Lint: WCAG 2.2 AA for HTML

Names every WCAG 2.2 AA form failure on its line, with the criterion and the fix

Who it is for. Frontend and full-stack developers shipping consumer-facing web services into the EU

What it checks (18 rules: 14 error, 4 warn)

RuleLevelWhat it flagsWCAG 2.2Fix
auth_paste_blockederrorSign-in field blocks paste or drop3.3.8 (AA)delete the onpaste/oncopy/ondrop handler so a password manager can fill the field
auth_no_autocompleteerrorPassword field is hidden from password managers3.3.8 (AA)set autocomplete="current-password" (or "new-password" on sign-up)
input_purposeerrorIdentity field has no autocomplete token1.3.5 (AA)add the WCAG token, e.g. autocomplete="email" / "tel" / "postal-code"
no_labelerrorForm control has no accessible name4.1.2 (A)add <label for="id"> or aria-label on the control
placeholder_as_labelerrorPlaceholder is doing the job of a label3.3.2 (A)keep the placeholder as an example and add a visible <label for="id">
target_sizeerrorInteractive target is smaller than 24 by 24 CSS pixels2.5.8 (AA)raise the box to at least 24px, or leave 24px of clear space around it
redundant_entrywarnThe same information is asked for twice3.3.7 (A)drop the confirm field, or let the browser auto-fill it from the first one
focus_removederrorFocus outline removed with no visible replacement2.4.7 (AA)keep the outline, or style :focus-visible with a 2px visible indicator
zoom_blockederrorViewport blocks zoom1.4.4 (AA)remove user-scalable=no and maximum-scale from the viewport meta
lang_missingerrorPage language is not declared3.1.1 (A)add lang="en" (or the page language) to the <html> element
img_alterrorImage has no alt attribute1.1.1 (A)add alt="...", or alt="" when the image is decorative
icon_no_nameerrorIcon-only control has no accessible name4.1.2 (A)add aria-label="..." describing the action
positive_tabindexwarnPositive tabindex overrides the reading order2.4.3 (A)use tabindex="0" and let the DOM order carry the focus order
click_no_keyerrorClick handler that the keyboard cannot reach2.1.1 (A)use a <button>, or add role="button" plus tabindex="0" and a key handler
aria_hidden_focusableerrorFocusable element is hidden from assistive technology4.1.2 (A)remove aria-hidden, or take the element out of the tab order
meta_refresherrorPage refreshes or redirects on a timer2.2.1 (A)remove the meta refresh, or let the user turn it off or extend it
dragging_onlywarnDragging with no single-pointer alternative2.5.7 (AA)add a move up/down button, and mark the element data-dnd-alternative
error_no_descwarnInvalid field does not point at its error message3.3.1 (A)add aria-describedby="<id of the error text>" next to aria-invalid

Run it

ext install ReadyStack.eaa-form-lint-wcag22                       # VS Code ยท Cursor/VSCodium via Open VSX
npx -y @readystack/eaa-form-lint-wcag22 signup.html        # CLI, free, every rule
npx -y @readystack/eaa-form-lint-wcag22 --dir . --ci             # whole repository, exit 1 on errors (licence)
docker run --rm -v "$PWD:/w" getreadystack/eaa-form-lint-wcag22 /w   # Docker

MCP (Claude Code, Cursor, Windsurf): check_text and check_file are free, check_dir needs a key.

{ "mcpServers": { "eaa-form-lint-wcag22": { "command": "npx", "args": ["-y", "@readystack/eaa-form-lint-wcag22", "--mcp"] } } }

Sample report

A deliberately wrong sample file, then the real output of npx @readystack/eaa-form-lint-wcag22 signup.html on it. Nothing here is mocked.

Sample input (signup.html)

<form action="/signup" method="post">
  <input type="text" placeholder="Full name">
  <input type="email" placeholder="Email address">
  <input type="password" placeholder="Password" autocomplete="off" onpaste="return false">
  <input type="checkbox" id="tos"> I agree
  <button type="submit">Create account</button>
</form>

Output

/tmp/s152_flag/samples/signup.html
      2  error WCAG 2.2 3.3.2 (A) Placeholder is doing the job of a label: Full name - fix: keep the placeholder as an example and add a visible <label for="id">
      3  error WCAG 2.2 3.3.2 (A) Placeholder is doing the job of a label: Email address - fix: keep the placeholder as an example and add a visible <label for="id">
      3  error WCAG 2.2 1.3.5 (AA) Identity field has no autocomplete token: email - fix: add the WCAG token, e.g. autocomplete="email" / "tel" / "postal-code"
      4  error WCAG 2.2 3.3.8 (AA) Sign-in field blocks paste or drop - fix: delete the onpaste/oncopy/ondrop handler so a password manager can fill the field
      4  error WCAG 2.2 3.3.2 (A) Placeholder is doing the job of a label: Password - fix: keep the placeholder as an example and add a visible <label for="id">
      4  error WCAG 2.2 3.3.8 (AA) Password field is hidden from password managers: autocomplete="off" - fix: set autocomplete="current-password" (or "new-password" on sign-up)
      5  error WCAG 2.2 4.1.2 (A) Form control has no accessible name: <input id="tos"> - fix: add <label for="id"> or aria-label on the control

Free and licence

FreeLints the HTML, Vue or Svelte file you have open with all 18 rules and names every WCAG 2.2 AA form failure on its line, with the success-criterion number and the one-line fix.
With a key ($29 once)Scans every template in the workspace in one pass and writes a dated conformance evidence file you keep - one row per finding, per file, mapped to its WCAG 2.2 criterion.

The full run is free for the first 7 days. 7-day refund on every key. Worth it because: A WCAG audit from an accessibility vendor starts around $2,500 for one signup flow

Get the key, $29 once Team key, every linter, $149

What comes next

The accessibility statement and EN 301 549 conformance evidence a client's procurement team asks for before signing

Questions

What does EAA Form Lint actually check?

It reads an HTML, Vue or Svelte template as text and applies 18 rules drawn from WCAG 2.2 Level AA. Each finding names the line, the success criterion and the fix: a password field hidden from password managers, a placeholder standing in for a label, a 16px tap target, a click handler no keyboard can reach.

Who is this for?

Frontend and full-stack developers shipping consumer-facing web services into the EU: e-commerce, banking, ticketing, e-books. Also the freelancer who inherits a generated codebase and has to answer a client asking whether the signup flow meets the European Accessibility Act, which has applied to new services since 28 June 2025.

Why is a free browser audit not enough?

Browser audit panels score a rendered page you already deployed, and most still test WCAG 2.1. Six of the 28 findings in the sample form sit under criteria added in WCAG 2.2 - dragging, target size, redundant entry, accessible authentication. This runs on the template in your editor, before the page exists.

What is free and what needs a key?

Checking the file you have open is free and complete: all 18 rules, every line, the criterion and the fix, no key and no limit. The licence key adds a different job - scanning every template in the workspace in one pass and writing a dated evidence file you keep and hand to a client.

What would this cost otherwise?

A WCAG audit from an accessibility vendor starts around $2,500 for a single signup flow, and its answer is true for the day it was run. A specialist re-reviewing each pull request costs more. This extension is $29 once, and it answers again every time you save a template.

A linter tells you what the rule says and where your file disagrees. It is not legal advice; the rule set carries its own date and is updated when the regulation moves.

Support · Pricing · Changelog

Install free

Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.

VS Code MarketplaceOpen VSX (Cursor, VSCodium)npmMCP server (npx)Docker Hub
ext install ReadyStack.eaa-form-lint-wcag22
npx @readystack/eaa-form-lint-wcag22 <file>
npx @readystack/eaa-form-lint-wcag22 --mcp

Team? One key for every ReadyStack linter, 5 seats, $149 once