WCAG 2.2: All 9 New Criteria Explained (Most Scanners Miss)

WCAG 2.2 added 9 new success criteria, but most automated scanners only catch 1-2 of them. Here's every new criterion in plain English, what it looks like in the wild, and how to test for each one.


WCAG 2.2 shipped on 5 October 2023. It added 9 new success criteria on top of WCAG 2.1. If you have used an automated accessibility scanner recently, there is a very good chance it only catches one or two of them, and reports the rest as "manual review required."

We just shipped full automated coverage for all 9 in AccessGuard. This post walks through every one, what it means for real users, and the detection approach we landed on.

If you are a developer, designer, or compliance owner, you can use this as a checklist regardless of which scanner you use. These are the things to audit before the next legal letter lands.

Why coverage was a problem

Most of the popular accessibility testing rules engines (including the one that ships inside Chrome DevTools) are very good at the deterministic stuff: missing alt text, bad heading order, low contrast, ARIA misuse. They are honest about what they do not check, and WCAG 2.2's new criteria sit almost entirely in the "do not check" bucket, because the new criteria require one of three things automated tools historically do not do well:

  1. Geometry. Real bounding boxes, real viewport math, real overlap detection.
  2. Behavioral analysis. What happens when the user actually focuses something, pastes into a field, or tries to drag.
  3. Context. Is this a login form? Is that element a chat widget? Is this the "help" link, or is it just a link that happens to say "help"?

Under the hood, AccessGuard already uses a headless Chromium via Ferrum to render pages with real CSS, real JavaScript, and real layout. We used that to actually solve the geometry and behavior problems instead of bolting on heuristics.

Here is what we now catch, and how.

The 9 new WCAG 2.2 criteria

2.4.11 Focus Not Obscured (Minimum) - Level AA

What it says, in plain English: When a keyboard user tabs to an element, that element must not be *entirely* hidden behind something else the author put on the page. The classic offender is a sticky header that covers the top 80 pixels of the viewport, so when a link near the top of the content scrolls into focus, the sticky header eats it.

Why it matters: Without this, screen reader and keyboard users can "focus" things they literally cannot see, and have no idea where they are on the page.

How we detect it: We grab the computed position, z-index, and bounding box of every element that has position: fixed or position: sticky. If it covers a significant horizontal slice of the viewport at the top or bottom edge and is taller than 40 pixels, we flag it as a potential obscurer. Narrow widgets like chat bubbles and back-to-top buttons are filtered out so we do not drown you in noise.

The fix is usually one line:

html {
  scroll-padding-top: 80px; /* match your sticky header height */
}

2.4.12 Focus Not Obscured (Enhanced) - Level AAA

Plain English: Same idea as 2.4.11, but stricter. *No part* of the focused element may be hidden, not just "not entirely hidden." If your sticky header clips even the top two pixels of a focused button's outline, you fail Enhanced.

How we detect it: We emit it alongside 2.4.11 as a separate Level AAA notice so you can see both signals without duplicating the scan.

2.4.13 Focus Appearance - Level AAA

Plain English: When something receives focus, the focus indicator must be big enough to actually see. The spec puts real numbers on this: the focus ring has to be at least 2 CSS pixels thick and have at least 3:1 contrast against the unfocused state.

Why it matters: A one-pixel light-grey outline on a white background technically exists but might as well not. Users cannot tell where focus is.

How we detect it: This is one of the trickiest. We had to teach our page fetcher to actually call element.focus({ preventScroll: true }) on every interactive element (up to a bounded sample per page for performance), read the computed :focus outline width, color, and background, then put the element back where it was. The checker then:

  1. Fails if there is no outline AND no box-shadow.
  2. Fails if the outline is thinner than 2 pixels.
  3. Runs the outline color through our existing contrast calculator against the element's background and fails if the ratio is below 3:1.

The thing most teams get wrong here is outline: none + a custom box-shadow that is too light. The shadow exists, but it is not 3:1, so it still fails. Our checker catches both.

The fix:

button:focus-visible {
  outline: 2px solid #005fcc; /* dark blue: ~7:1 on white */
  outline-offset: 2px;
}

2.5.7 Dragging Movements - Level AA

Plain English: If a user can do something by dragging (reorder a list, drag a card between columns, resize a panel), they must also be able to do that same thing with a single click or tap. Drag cannot be the only way.

Why it matters: Users with motor disabilities, tremors, people using switch controls, and anyone on a touch device without fine control, they cannot drag reliably or at all.

How we detect it: We look for the HTML5 draggable="true" attribute, inline drag event handlers (ondragstartondrag, etc.), touch-action: none on interactive elements, and the DOM markers left by common drag libraries: jQuery UI (.ui-sortable.ui-draggable), Sortable.js, react-beautiful-dnd (data-rbd-draggable-id), and dnd-kit. For each drag target, we walk the surrounding container looking for a non-drag alternative: buttons labeled "Move up"/"Move down"/"Reorder," a position select control, or an aria-keyshortcuts attribute documenting keyboard shortcuts. If nothing is there, we flag it.

Real talk: If you built a Trello clone and you have not added keyboard reorder, you are almost certainly failing this criterion. react-beautiful-dnd has keyboard support built in but you have to opt into surfacing the instructions. dnd-kit is similar.

2.5.8 Target Size (Minimum) - Level AA

Plain English: Interactive targets have to be at least 24 by 24 CSS pixels. There are four exceptions, and the spacing one is the one most people get wrong.

The exceptions, rapid-fire:

  • Inline: A link inside a sentence inherits its size from the line height. That is allowed.
  • Spacing: A smaller target is OK if it has 24 CSS pixels of space to the center of every adjacent interactive element. This is how icon toolbars are allowed to exist.
  • User agent control: A native checkbox you did not restyle is sized by the browser, not by you. Not your fault.
  • Essential: The target's size is legally required to be what it is. Rare.

How we detect it: We measure real bounding boxes from the browser, not CSS string widths. For undersized targets, we compute the center-to-center distance to every other interactive element on the page. If any neighbor is within 24 pixels, the spacing exception fails and we flag it. Truly isolated small targets get flagged too, since the spacing exception only makes sense when there are neighbors. Inline links inside <p><li>, table cells, and other text containers are exempted structurally. Native input[type=checkbox]radiorange, and color are treated as user-agent controlled and skipped. Elements with data-target-size-exempt="true" are treated as essential.

This last one matters because it removes a whole category of false positives that used to drown real signal. A 16-pixel icon inside a 32-pixel padded button is not a failure. A 16-pixel icon *is* the button? Different story.

3.2.6 Consistent Help - Level A

Plain English: If your site has help mechanisms (a contact link, a phone number, a chat widget, a FAQ link), and those mechanisms appear on multiple pages, they have to appear in the same relative place on each page. You cannot put "Contact" in the header on one page and halfway down the body on another.

Why it matters: Users who need help are often the most vulnerable and the least patient with your UI quirks. Making them hunt for the contact link every time is a real barrier.

How we detect it: Scoped to a single page, we cannot verify "same relative order across pages," but we can detect the situations that make that order *impossible* to keep consistent. We look for help mechanisms (tel:mailto:, visible "Help"/"Support"/"Contact" links, aria-labeled help buttons, chat widget launchers for Intercom, Crisp, Drift, Tawk, Zendesk, HubSpot) and check whether each one lives inside a persistent landmark: <header><footer><nav><aside>, or their ARIA equivalents (bannercontentinfonavigationcomplementary). Help links buried in mid-body article content are flagged as a notice.

The advice is simple: put your contact link in the footer, not mid-paragraph.

3.3.7 Redundant Entry - Level A

Plain English: If a user has already given you a piece of information in the same process, you cannot make them type it again. Either auto-populate it, or let them select it.

Why it matters: This one is about cognitive load and typing effort. It matters for everyone but it matters disproportionately for users with memory, attention, or motor disabilities.

How we detect it: This checker is opinionated and catches the three most common real-world violations:

  1. Duplicate field groups. We look for field name stems like billing_street and shipping_street in the same form. If neither has an autocomplete token and there is no "same as billing" checkbox, we flag it.
  1. Missing autocomplete on reusable fields. input[type=email]type=tel, and name-like fields without an autocomplete attribute defeat the browser's ability to auto-fill anything. We flag those at notice level.
  1. Double email entry. Two email inputs in the same form is the classic redundant-entry failure. If paste is blocked on the second one, it becomes impossible to complete without retyping. We flag it either way, and include "paste is blocked" in the error message if we detect it.

The fix is almost always autocomplete tokens:

<input name="billing-address" autocomplete="billing street-address">
<input name="shipping-address" autocomplete="shipping street-address">

3.3.8 Accessible Authentication (Minimum) - Level AA

Plain English: Logging in cannot require a cognitive test. If your auth flow asks the user to remember a password, solve a puzzle, or read distorted text, you have to provide an alternative or a way to help.

The big one: Relying on a remembered password is, technically, a cognitive function test. The way you comply is by allowing password managers to fill the password for the user. The way you break compliance is by blocking paste on the password field or setting autocomplete="off", both of which are still extremely common "security" anti-patterns that actively reduce security and fail WCAG AA.

How we detect it, specifically in auth forms:

  • Password fields with onpaste="return false" or oncopy / oncut restrictions: error, because it actively defeats password managers.
  • Password fields with autocomplete="off"error, same reason.
  • Password fields with no autocomplete attribute at all: warning, because many password managers still guess, but not reliably.
  • Username fields in auth forms with no autocomplete="username" or autocomplete="email"notice, because managers cannot link credentials to the right account.
  • Image-recognition CAPTCHAs, puzzle CAPTCHAs, and distorted-text CAPTCHAs in auth flows: error. These are the textbook cognitive function test.

We explicitly exempt Cloudflare Turnstile and hCaptcha's basic "I am human" checkbox, because neither requires any cognitive work. Turnstile is how we just wired up bot protection on our own forms last week, specifically because it is the one anti-bot mechanism that does not fail 3.3.8.

The fix for most login forms is tiny:

<input type="email" name="email" autocomplete="email">
<input type="password" name="password" autocomplete="current-password">

For signup or reset, swap current-password for new-password. That is it. No JavaScript, no CSS. A one-line change per field that, statistically, most sites still have not made.

3.3.9 Accessible Authentication (Enhanced) - Level AAA

Plain English: Same as 3.3.8, but even stricter. Object recognition (pick the traffic lights) and personal-content questions (what is your mother's maiden name) stop being acceptable as alternatives.

How we detect it: When we find a cognitive CAPTCHA in an auth flow, we emit 3.3.8 as an error AND 3.3.9 as an AAA-level warning on the same element. If you are chasing AAA, passkeys, magic links, and OAuth are the paths.

Why we bothered with AAA

You will notice we shipped detection for the AAA criteria (2.4.12, 2.4.13, 3.3.9) even though almost no regulatory regime enforces AAA. Here is why:

  1. Accessibility is not pass/fail. A site that clears AA and notices a few AAA problems is strictly better than one that only clears AA and ignores everything else. The AAA-level notices are quiet by default (they do not turn your score red), but they tell you what to fix next.
  1. Some regulated sectors actually do care. Government, healthcare, and education increasingly ask for AAA in specific contexts like authentication and focus visibility.
  1. Legal floors move. The European Accessibility Act enforcement began in 2025 and EAA interpretation of WCAG 2.2 is still settling. Being able to *see* AAA issues means you can make a risk call, not a guess.

What this means if you are already using AccessGuard

You do not need to do anything. All five new checkers are registered in the orchestrator and will run on your next scan. The stricter target size and focus obscuring logic will also run automatically. Your existing scan results will not change retroactively, but the next scan will surface any 2.2 issues that were previously being counted as "manual review."

If your score drops, that is not the scanner getting meaner. It is the scanner finally seeing things it always should have seen.

What this means if you are not

Two options.

Option 1: Start a free scan at /scan. No signup. It runs all 26 checkers and will flag WCAG 2.2 issues on whatever URL you give it.

Option 2: Use this post as a checklist and audit your own site by hand. Everything we described is detectable with 15 minutes of DevTools and a real keyboard. The three highest-impact things to check right now, in order:

  1. Your login form. Does autocomplete="current-password" exist? Does paste work? If no CAPTCHA, you are probably already compliant. If a CAPTCHA, is it Turnstile or hCaptcha checkbox, or the picture thing?
  2. Your help link. Is it in the footer on every page, in the same position? Good. Is it buried in an article body on one page and in the nav on another? Fix it.
  3. Any drag interaction. Can a keyboard user do the same thing?

That is three tiny changes that address four of the nine 2.2 criteria. The remaining five take more work, but honestly, none of them take more than an afternoon per feature.

What is next

This post only covers the WCAG 2.2 *additions*. We have full coverage of WCAG 2.1 A and AA already, plus axe-core 4.11 running under the hood for the industry-standard baseline. Our next focus is reducing false positives on the noisier existing checkers. target size's new spacing exception logic cut its false-positive rate significantly, and we want to do the same for contrast and ARIA.

If you want to see the checker source, everything we shipped is on GitHub under the MIT license. The five new checkers are:

  • DraggingMovementsChecker (2.5.7)
  • ConsistentHelpChecker (3.2.6)
  • RedundantEntryChecker (3.3.7)
  • AccessibleAuthenticationChecker (3.3.8 + 3.3.9)
  • FocusAppearanceChecker (2.4.13)

Plus rewrites of FocusChecker (2.4.11 + 2.4.12) and TargetSizeChecker (2.5.8).

Questions, corrections, or "your heuristic missed my site"? We would love to know.


Want to check your own site? Run a free accessibility scan - no signup, full WCAG 2.1 and 2.2 coverage, takes about 30 seconds.

Start scanning for free

Join thousands of developers making the web more accessible.

Get started