May 26, 20267 min read

WCAG 2.2 level AA: what to check in an audit

A while ago I wrote about the accessibility features I built into government platforms: invert colors, grayscale, font size, highlight links. That article covers what’s in the widget.

This one is about something else, which is what gets checked in an audit: what a site is measured against.

A widget doesn’t make a site accessible

It’s worth starting here because the opposite gets sold a lot: an accessibility toolbar doesn’t meet WCAG. None of the features I listed in that article satisfies a criterion on its own.

A widget gives the user controls. WCAG asks for the site to work before anyone touches anything: that it can be navigated with a keyboard, that contrast is sufficient out of the box, that images have text alternatives and that forms say what went wrong.

If your site needs the user to turn on “high contrast” to be readable, it doesn’t meet criterion 1.4.3. The widget covers up the problem instead of solving it.

That doesn’t make it useless. It’s a legitimate convenience and on a public-facing portal people appreciate it, but it goes on top of compliance, never in its place.

What changed in WCAG 2.2

WCAG 2.2 has been a W3C Recommendation since October 2023. It’s backwards compatible: if you meet 2.2, you also meet 2.1 and 2.0. It adds nine new criteria and removes one.

The one removed is 4.1.1 (Parsing), and it’s worth mentioning because it causes confusion: it was deemed obsolete because modern browsers recover from malformed HTML on their own. If an audit still reports it as a failure, the audit is out of date.

Of the nine new ones, six are level A or AA, which is what the public sector usually requires:

Criterion Level What it’s about
2.4.11 Focus Not Obscured (Minimum) AA The focused element can’t be hidden
2.5.7 Dragging Movements AA Anything dragged needs an alternative without dragging
2.5.8 Target Size (Minimum) AA Touch targets of at least 24×24 CSS px
3.2.6 Consistent Help A Help in the same place on every page
3.3.7 Redundant Entry A Don’t ask for the same information twice
3.3.8 Accessible Authentication (Minimum) AA No cognitive tests to log in

The other three are AAA, and unless you have a specific requirement you won’t be chasing them.

The six new ones and how to check them

I’ve ordered them by how often they tend to fail.

2.5.8 Target Size

Any interactive element has to be at least 24×24 CSS pixels, with some exceptions, like links inside a paragraph.

It’s among the ones that fail most, and almost always in the same places: social icons in the footer, the close button on modals, and paginators. A 16px icon with 2px of padding doesn’t make it.

A quick way to find candidates, from the browser console:

[...document.querySelectorAll('a, button, [role="button"], input, select')]
  .filter(el => {
    const r = el.getBoundingClientRect();
    return r.width && (r.width < 24 || r.height < 24);
  })
  .forEach(el => console.log(el.getBoundingClientRect().width.toFixed(0)
    + '×' + el.getBoundingClientRect().height.toFixed(0), el));

That gives you candidates, not confirmed failures. There are valid exceptions and you have to review them by hand.

2.4.11 Focus Not Obscured

When you navigate with Tab, the element that has focus can’t end up hidden behind something else. The usual culprit is a very fashionable pattern: the sticky header. You tab down, the page scrolls, and the focused element ends up right under the header.

You check it with the keyboard alone: Tab from the top to the bottom of the page, without touching the mouse, watching whether you ever lose sight of the focus. It’s usually fixed with one rule:

:target, a, button, input, select, textarea {
  scroll-margin-top: 6rem; /* the height of your sticky header */
}

While you’re on the keyboard, also check 2.4.7 (Focus Visible), which comes from 2.1: that the focus indicator exists and can be seen. An outline: none with nothing replacing it is a failure.

2.5.7 Dragging Movements

Anything that works by dragging needs an alternative with a single click or tap. It affects reorderable lists, range sliders and maps.

On a government platform it usually shows up in two places: maps, which need zoom buttons as well as pinch and drag, and range selectors, which also need a field to type the number.

3.3.8 Accessible Authentication

You can’t require a cognitive test to log in, like remembering a password, solving a puzzle or copying characters, without offering another way to do it.

In practice it has two consequences. A CAPTCHA where you copy distorted text fails the criterion if there’s no alternative. And blocking paste in the password field fails it too, because it stops people from using a password manager:

<!-- fails 3.3.8: blocks password managers -->
<input type="password" onpaste="return false" />

Also, let the browser help with autocomplete="current-password".

3.3.7 Redundant Entry

If you already asked for something in step 1, don’t ask for it again in step 4: fill it in yourself or let the user select it. The classic example is the shipping and billing address, solved with a “same as shipping” checkbox.

In multi-step municipal procedures this is where it shows most, and where the most people give up.

3.2.6 Consistent Help

If you offer help (a phone number, chat, a contact form), it has to be in the same place on every page. The criterion doesn’t require you to have it, only not to move it around.

What to check even if it isn’t new

The new criteria aren’t the ones that fail most. These others, which come from 2.0, fail much more often:

  • 1.1.1 Non-text Content. Every image that carries information needs alt, and decorative ones get alt="". Watch out for the opposite too: an empty alt on an image that does communicate something is as much a failure as leaving it out. I know because it happened to me: the cover of my own accessibility article had an empty alt for months.
  • 1.4.3 Contrast (Minimum). 4.5:1 for normal text and 3:1 for large text. It’s where gradients and glassmorphism do the most damage.
  • 1.3.1 Info and Relationships. Headings in order, without jumping from h2 to h4. Tables with <th scope>. Forms with an associated <label>, not a <span> that looks like a label.
  • 2.1.1 Keyboard. Everything you can do with the mouse has to be doable with the keyboard. <div onclick> is the usual suspect.
  • 2.1.2 No Keyboard Trap. For example, a modal that traps focus and won’t let you out with Esc.
  • 3.3.1 Error Identification. The error has to say which field failed and why. “There’s an error in the form” doesn’t cut it.
  • 4.1.3 Status Messages. A “saved successfully” message that appears without reloading the page needs role="status" so a screen reader announces it.

How to audit in practice

Order matters, because each step finds things the previous one can’t see.

  1. An automated checker, for the obvious stuff. It catches contrast problems, missing alt, unlabeled form fields and out-of-order headings. It’s fast, and it’s the first thing to run.
  2. The keyboard, which is where the expensive issues show up. Tab through the whole page without touching the mouse: is focus always visible? Does the order make sense? Can you get out of every modal with Esc? Can you activate everything with Enter or the space bar? Ten minutes with the keyboard finds more than many reports.
  3. Zoom. At 200% the text has to stay complete and readable (criterion 1.4.4), and at 400%, which on a desktop is equivalent to a 320px-wide screen, the content shouldn’t force horizontal scrolling (1.4.10).
  4. A screen reader, even if you use it clumsily. NVDA on Windows and VoiceOver on Mac are free. You don’t need to be an expert: go through your own form with your eyes closed and you’ll find things no checker reports.

Why it can’t be fully automated

Before trusting an automated report, it’s worth being clear about its limit: automated tools only catch a small share of the real problems.

A checker verifies that the image has alt, but not whether the text describes the image. It verifies that a <label> exists, but not whether what it says makes sense. It measures the contrast of a solid color, but not that of text over a background that changes. And it can tell there’s a tab order, but not whether it’s logical.

Anything that depends on meaning slips past it, and that’s where most real accessibility lives.

In the next article I want to measure exactly that gap: run an automated auditor over a real site, then review it by hand and compare what each one finds.

Share