Accessibility testing methods: the layered pass that finds real barriers
Automated scanners settle only about a third of WCAG. Here is why that boundary is computable, and the layered accessibility testing methods that find the barriers a green report hides.
Why a green scanner report lies#
You ran axe. It reported zero violations. Then a real user, or an auditor, found barriers anyway. That gap is the pain most searchers arrive with, and it is not a bug in the tool. A scanner is doing exactly what it can do, which is less than a clean report implies.
Because the failure is structural, no better scanner fixes it. So the honest move is to see the gap first, before you trust any report. The demo below makes it observable.
Watch a scanner pass a page that is actually broken#
Here is a page built to break in five ways. Every element passes an automated scan. Every element is a real barrier for someone. Step through the elements, then flip the lens between what the scanner sees and what a keyboard or screen-reader user hits.
Promo hero image
<img src="summer-sale.png" alt="image"> (the words "SAVE 20%, code SPRING" live in the pixels)
Scanner: Pass, 0 violations
An alt attribute is present, so the missing-alt rule does not fire. The scanner cannot read the words painted into the image, so it reports no problem.
| Element | Scanner | Real barrier | WCAG 2.2 |
|---|---|---|---|
| Promo hero image | Pass, 0 violations | A screen reader announces only the word "image". The discount code exists solely as pixels, so a blind user and a low-vision user never learn it. Presence of alt is decidable; whether the alt is meaningful is not. | 1.1.1 Non-text Content (Level A) |
| Pay-now control | Pass, 0 violations | A keyboard user cannot reach or press it, because a div is not focusable and no key handler exists. Checkout dead-ends for anyone who does not use a mouse. Operability is a judgment a scanner cannot make. | 2.1.1 Keyboard (Level A) |
| Reordered form | Pass, 0 violations | Tab order and screen-reader order follow the DOM, so focus lands on Submit before the fields it submits. The sequence carries no meaning. Whether an order is logical is a human judgment. | 1.3.2 Meaningful Sequence, 2.4.3 Focus Order (Level A) |
| Labelled input | Pass, 0 violations | The word "Field" tells the user nothing. A screen reader announces "Field, edit text" and the user has no idea what to enter. Association is decidable; adequacy is not. | 3.3.2 Labels or Instructions (Level A) |
| Required field cue | Pass, 0 violations | A colourblind user sees no cue that the field is required or in error, because colour is the only signal. Nothing else carries the meaning. Use of colour as the sole channel is a human judgment. | 1.4.1 Use of Color (Level A) |
Element: Promo hero image. Lens: what the scanner sees. Verdict: pass, zero violations. An alt attribute is present, so the missing-alt rule does not fire. The scanner cannot read the words painted into the image, so it reports no problem.
This broken page is an illustrative teaching specimen, not a real site. Every element passes an automated scan, and every element is a genuine barrier for someone. That gap is the whole point: a clean report is necessary, never sufficient.
Notice what happened on the pay-now control. It has a valid role and an accessible name, so the scanner passes it. Still, no keyboard user can reach or press a plain div, so checkout dead-ends. The tool checked that a name and role exist. It could never check that the control actually works.
What every ranking page gets wrong: quoting the number as folklore#
Search this topic and every result repeats the same figure. Automated tools catch 30 to 40 percent of issues. Almost none say where the number comes from, and none map it to what a machine can actually decide. So the figure floats as folklore, and the reader is left to trust it.
The coverage number is not a statistic, it is a boundary you can compute#
This guide makes one core claim. The coverage figure is a property you can derive, not a statistic to memorize. Moreover, it falls out of one question asked of every success criterion. Can a machine settle this by checking presence or computing a value, or does it need a human to judge meaning or operability?
The decidability model: three classes of WCAG success criterion#
Sort every WCAG 2.2 A and AA success criterion into three classes. Fully decidable means a machine returns a real verdict, such as a contrast ratio or a present lang attribute. Partially decidable means a machine can flag a candidate but a person confirms it, such as whether an alt attribute that exists is actually meaningful. Human-only means the machine is blind, such as whether focus order is logical. Step through the three class tabs below and read what shifts.
Fully decidable: 4 of 21 sampled criteria (19%). A scanner returns a real pass or fail here on its own.
1.4.3Contrast (Minimum)Level AA
Machine decides: Computes the exact text-to-background contrast ratio against 4.5:1.
Human judges: Nothing extra for plain body text. The math is the whole judgment.
Caught at: Layer 1, automated scan
1.4.11Non-text ContrastLevel AA
Machine decides: Computes the contrast of a UI component or graphic against its adjacent colour.
Human judges: Only the rare case of deciding which graphics are meaningful.
Caught at: Layer 1, automated scan
2.5.8Target Size (Minimum)Level AANew in 2.2
Machine decides: Measures the computed target box against 24 by 24 CSS pixels.
Human judges: Only the spacing and inline exceptions, which are also mostly geometric.
Caught at: Layer 1, automated scan
3.1.1Language of PageLevel A
Machine decides: Checks that a lang attribute is present and holds a valid language tag.
Human judges: Nothing. Presence plus a valid tag settles it.
Caught at: Layer 1, automated scan
| SC | Name | Level | Class | Caught at |
|---|---|---|---|---|
| 1.4.3 | Contrast (Minimum) | AA | ● Fully decidable | Layer 1, automated scan |
| 1.4.11 | Non-text Contrast | AA | ● Fully decidable | Layer 1, automated scan |
| 2.5.8 | Target Size (Minimum) (new in 2.2) | AA | ● Fully decidable | Layer 1, automated scan |
| 3.1.1 | Language of Page | A | ● Fully decidable | Layer 1, automated scan |
| 1.1.1 | Non-text Content | A | ◐ Partially decidable | Layer 1 flags, Layer 3 confirms |
| 3.3.2 | Labels or Instructions | A | ◐ Partially decidable | Layer 1 flags, Layer 3 confirms |
| 4.1.2 | Name, Role, Value | A | ◐ Partially decidable | Layer 1 flags, Layer 3 confirms |
| 2.4.4 | Link Purpose (In Context) | A | ◐ Partially decidable | Layer 1 flags, Layer 3 confirms |
| 1.4.10 | Reflow | AA | ◐ Partially decidable | Layer 1 flags, Layer 4 confirms |
| 2.4.11 | Focus Not Obscured (Minimum) (new in 2.2) | AA | ◐ Partially decidable | Layer 2, keyboard pass |
| 2.4.13 | Focus Appearance (new in 2.2) | AA | ◐ Partially decidable | Layer 2, keyboard pass |
| 2.4.2 | Page Titled | A | ◐ Partially decidable | Layer 1 flags, Layer 3 confirms |
| 1.3.2 | Meaningful Sequence | A | ○ Human-only | Layer 3, screen-reader pass |
| 2.4.3 | Focus Order | A | ○ Human-only | Layer 2, keyboard pass |
| 2.1.1 | Keyboard | A | ○ Human-only | Layer 2, keyboard pass |
| 1.4.1 | Use of Color | A | ○ Human-only | Layer 3, screen-reader pass |
| 2.4.6 | Headings and Labels | AA | ○ Human-only | Layer 3, screen-reader pass |
| 2.5.7 | Dragging Movements (new in 2.2) | AA | ○ Human-only | Layer 6, real AT users |
| 3.2.6 | Consistent Help (new in 2.2) | A | ○ Human-only | Layer 6, real AT users |
| 3.3.7 | Redundant Entry (new in 2.2) | A | ○ Human-only | Layer 6, real AT users |
| 3.3.8 | Accessible Authentication (Minimum) (new in 2.2) | AA | ○ Human-only | Layer 6, real AT users |
Class Fully decidable, 4 of 21 criteria, 19%. A scanner returns a real pass or fail here on its own. Overall: fully decidable 19%, partially decidable 38%, human-only 43%. A scanner gates the fully-decidable set and flags candidates in the partial set, which is why audits land near 30% decided and roughly 70% waits for a person.
The criterion numbers, names, and levels are the real WCAG 2.2 Recommendation, including the seven criteria new in 2.2. The three-class split is a defensible teaching model of the presence-check versus judgment boundary, not an official W3C label. Use it to see why a scanner can only ever settle part of the standard.
Two of the sampled criteria are worth a closer look. Focus Not Obscured (2.4.11) and Focus Appearance (2.4.13) are partially decidable, because a tool can measure a focus indicator yet cannot confirm it is perceivable in context. For a worked micro-example of getting one focus indicator right, see how focus versus focus-visible actually behaves in CSS. That single selector is one row; this guide is the whole method.
Why the boundary sits exactly at presence-check versus meaning and operability#
The boundary is not arbitrary. A machine decides two things well. It decides existence, such as whether an alt attribute or a label is present. It decides computation, such as a contrast ratio or a target size in pixels. Both are formal properties of the markup and the styles.
Meaning and operability are different in kind. For example, whether alt text describes the image is a semantic judgment. Meanwhile, whether a custom widget is operable is a behavioral one. In contrast, whether the reading order carries sense is a judgment about intent. No static check settles any of these, because the answer lives in a human reading or a human interaction, not in the source. Therefore the boundary sits precisely at presence-check versus judgment, and the coverage figure is just the size of the decidable side.
Working the real numbers: WebAIM Million 2026, GDS, and Deque#
Now put real data on the boundary. The WebAIM Million 2026 report scanned the top one million home pages and found 56.1 detectable errors per page. Six machine-decidable classes account for 96 percent of all detected errors, so a scanner genuinely earns its place as the cheap first pass.
Show data table
| Item | Detectable errors per page |
|---|---|
| Low contrast text | 34 per page |
| Images missing alt | 10.8 per page |
| Unlabeled form inputs | 2.3 per page |
Low-contrast text dominates at 34 instances per page. Missing alt (about 10.8 images, 16.2% of the 66.6 images per page) and unlabeled inputs (about 2.3, 33.1% of 6.9 inputs) follow. These plus empty links, empty buttons, and missing document language make the six machine-decidable classes that are 96% of all detected errors.
Here is the twist the scan cannot show you. Apply the independent UK Government Digital Service tool audit. On a pass or fail basis, the best automated tool detected only about 30 percent of accessibility barrier types. So a page that scans to zero errors has cleared roughly a third of the criteria surface. The rest is unexamined.
~30%
Decided by the automated scan
~70%
Left entirely for human layers
Even Deque's optimistic vendor figure of 57% for axe still leaves 43% for manual layers. Whichever number you take, the majority of barrier types sit outside the scanner. That is where the manual budget belongs.
Show data table
| Option | of accessibility barrier types (UK GDS tool audit, pass or fail basis) |
|---|---|
| Decided by the automated scan | ~30% |
| Left entirely for human layers | ~70% |
The close: full conformance certainly lower than 4.1 percent#
WebAIM closes the loop with a hard number. Because only auto-detectable failures were counted, the true full-conformance rate was, in their words, certainly lower than 4.1 percent of pages. Read that carefully. Even most of the small share of pages that scanned clean still fail manual criteria. Consequently, a green report tells you almost nothing about whether the page actually works for a disabled user.
The layered accessibility testing methods: what to run, when, and in what order#
These layered accessibility testing methods run in a fixed order, cheapest first. Each layer catches what the one before it cannot. The automated gate runs on every commit, the manual passes run before release, and real users close the loop. The funnel below is the spine of the method.
Layer 1: automated scanning in CI (the cheap first pass)#
In practice, Layer 1 is the automated pass, wired into CI so it runs on every pull request. Tools like axe-core, Lighthouse, WAVE, and pa11y reliably catch the machine-decidable classes: contrast, alt presence, label association, ARIA validity, and page language. Gate the build on serious and critical findings, and warn on the rest so a moderate issue never buries the signal. Here are three real configurations you can lift.
name: accessibility
on: [pull_request]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- run: npx serve -l 4321 dist &
- run: npx wait-on http://localhost:4321
# pa11y-ci reads .pa11yci and fails the job on any error-level issue.
- run: npx pa11y-ci --config .pa11yci {
"defaults": {
"runners": ["axe"],
"standard": "WCAG2AA",
"timeout": 30000,
"threshold": 0
},
"urls": [
"http://localhost:4321/",
"http://localhost:4321/pricing/",
"http://localhost:4321/contact/"
]
} import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
// Block the build only on serious or critical impact. Warn on the rest, so a
// moderate finding never drowns the signal (an anti alert-fatigue baseline).
test('home page has no serious or critical violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
.analyze();
for (const v of results.violations) {
if (v.impact !== 'serious' && v.impact !== 'critical') {
console.warn(`warn ${v.impact}: ${v.id} (${v.nodes.length} nodes)`);
}
}
const blocking = results.violations.filter(
(v) => v.impact === 'serious' || v.impact === 'critical',
);
expect(blocking, JSON.stringify(blocking, null, 2)).toHaveLength(0);
}); The severity gate matters more than the tool choice. Because a raw scan surfaces every impact level, an unfiltered gate trains the team to ignore red builds. Instead, block on serious and critical, warn on moderate, and hold a baseline so known debt does not re-fail the build every run.
Layer 2: manual keyboard-only testing checklist#
Layer 2 needs no tool, only your keyboard. Unplug the mouse and drive the whole page. This one pass catches a huge share of operability barriers that no scanner reports.
Layer 3: screen reader testing (NVDA, VoiceOver, JAWS)#
Layer 3 is the screen-reader pass, and the choice of reader matters. Ground it in the WebAIM Screen Reader User Survey #10, which found JAWS is the primary desktop reader for 40.5 percent of users, with NVDA and VoiceOver making up most of the rest. Pair each reader with a browser its users actually run.
| Screen reader | Platform | Pair with | What to listen for |
|---|---|---|---|
| JAWS | PlatformWindows | Pair withChrome or Edge | What to listen forNames, roles, and state changes announced correctly |
| NVDA | PlatformWindows | Pair withChrome or Firefox | What to listen forForm labels, error messages, and live-region updates |
| VoiceOver | PlatformmacOS and iOS | Pair withSafari | What to listen forReading order, headings, and custom control operability |
| TalkBack | PlatformAndroid | Pair withChrome | What to listen forTouch exploration and focus on the mobile layout |
For each widget, know what you are listening for. When a modal opens, focus should move into it and the reader should announce its name. Next, when a field is invalid, the error should be tied to the input and read on focus. Finally, when a custom toggle flips, its new state should be announced, not silent.
Layer 4: zoom, reflow, reduced-motion, and cognitive load#
Layer 4 is the pass most guides skip in a line or two. Here it gets concrete pass or fail procedures. First, zoom the browser to 400 percent at a 1280 pixel width. The page passes only if content reflows to one column with no two-axis scrolling. Second, apply a 200 percent text-spacing bookmarklet. The page passes if no text is clipped or overlapped. Third, set the operating system to reduce motion. Any non-essential animation should stop or shorten. Finally, read the flow for cognitive load. A form that re-asks for data you already gave, or a login that forces a memory puzzle, is a real barrier under WCAG 2.2.
Layer 5: the WCAG 2.2 checklist, including the seven criteria competitors omit#
Layer 5 walks the full WCAG 2.2 checklist. This is the differentiator, because most guides still cite WCAG 2.1 and miss the seven criteria added in 2.2. Several of them are human judgments, so they belong on your manual checklist, not your scanner.
| Success criterion | What it requires | Decidability | Caught at |
|---|---|---|---|
| 2.4.11 Focus Not Obscured | What it requiresA focused control is not fully hidden by other content | DecidabilityPartial | Caught atKeyboard pass |
| 2.4.13 Focus Appearance | What it requiresThe focus indicator is large and contrasting enough | DecidabilityPartial | Caught atKeyboard pass |
| 2.5.7 Dragging Movements | What it requiresAny drag has a single-pointer alternative | DecidabilityHuman | Caught atReal AT users |
| 2.5.8 Target Size (Minimum) | What it requiresTargets are at least 24 by 24 CSS pixels | DecidabilityFull | Caught atAutomated scan |
| 3.2.6 Consistent Help | What it requiresHelp appears in a consistent place across pages | DecidabilityHuman | Caught atReal AT users |
| 3.3.7 Redundant Entry | What it requiresInformation is not asked for twice in a flow | DecidabilityHuman | Caught atReal AT users |
| 3.3.8 Accessible Authentication | What it requiresLogin needs no cognitive function test | DecidabilityHuman | Caught atReal AT users |
Layer 6: where real assistive-tech users are irreplaceable#
Finally, Layer 6 closes the funnel with real assistive-technology users. Still, automated checks and even your own manual passes are proxies. A daily screen-reader user hits barriers you will not predict, because they know the shortcuts, the habits, and the failure patterns of their tools. For the human-only criteria, such as consistent help and redundant entry, lived operability is the only true test. So budget for it on any flow that matters.
Triaging what you find: severity before you fix#
The accessibility testing methods above will surface more findings than you can fix at once. So triage by barrier severity, not by scanner impact score. A barrier that blocks task completion outranks one that merely annoys, and a barrier on a critical journey outranks one on a dead-end page.
| Finding | Barrier impact | On the journey | WCAG level | Fix order |
|---|---|---|---|---|
| Checkout button not keyboard operable | Barrier impactBlocks task completion | On the journeyCritical path | WCAG levelA | Fix orderFix now |
| Form error not tied to its input | Barrier impactBlocks task completion | On the journeyCritical path | WCAG levelA | Fix orderFix now |
| Reading order illogical on a key page | Barrier impactDegrades the task | On the journeyPrimary journey | WCAG levelA | Fix orderThis sprint |
| Low contrast on secondary text | Barrier impactDegrades the task | On the journeySecondary journey | WCAG levelAA | Fix orderThis sprint |
| Missing skip link | Barrier impactAnnoyance | On the journeySite-wide | WCAG levelA | Fix orderBacklog |
| Focus ring slightly thin on a footer link | Barrier impactAnnoyance | On the journeyDead-end page | WCAG levelAA | Fix orderBacklog |
How teams triage the bugs automated scans surface#
Still, a severity matrix only sets the order. A team must argue the edge cases. For instance, a low-severity barrier on a revenue path can outrank a high-severity one almost nobody hits. For the mechanics of that decision, and why the room matters, read how teams triage the bugs automated scans surface. The same forum logic applies to accessibility findings.
When NOT to lean on this, and the honest thesis#
These accessibility testing methods are not a checklist to game. So here is where the approach breaks, stated plainly.
Why manual QA still catches what automation misses#
This is the same argument shape you see across testing, not just accessibility. Automation has structural blind spots, and layered human review closes them. For the general version of the case, applied to test suites, read why manual QA still catches what automation misses. Accessibility is one sharp instance of that broader truth.
A broader look at layered testing techniques#
Layering is a general testing discipline, not an accessibility trick. Safety-critical fields formalize it into named stages for exactly the same reason. For the wider taxonomy of how testing techniques stack up, read a broader look at layered testing techniques. The pattern carries across domains: cheap automated checks first, expensive human judgment where it counts.
Here is the thesis to carry away. The coverage number is not a statistic to memorize, it is a boundary you can compute from what a machine can decide. Accessibility is therefore a design and development property you build in, not a test you bolt on at the end. Run the automated gate for what it settles cheaply, then spend your real effort on the judgment layers, because that is where the barriers live.
Accessibility testing methods: common questions
What are the best accessibility testing methods?
How much of WCAG do automated tools catch?
Is axe better than manual accessibility testing?
Which screen readers should I test on?
Does WCAG 2.2 change how I should test?
Want a second read on your accessibility test process, or help wiring the automated gate and the manual layers into one pipeline? This connects to how manual testing improves automation quality and to the broader question of where quality gates sit in delivery. No pressure and no lock-in.
See Atyantik's approach to quality and reliability