Audit a seeded page carrying ten accessibility defects — four catchable by automated scan, four needing AI-guided checks, two visible only to a human with a keyboard and a screen reader. Pass requires finding all ten, mapping each to WCAG, and correctly flagging the two machine-invisible ones.
Marcus runs frontend for a public-sector portal, where accessibility isn't a nice-to-have — it's a legal exposure. His team's CI runs axe and reports "0 violations," and his product manager reads that as "accessible." It isn't: automated scanners detect roughly a third to half of real WCAG failures; the rest need guided judgment or a human at the controls. This lab makes that boundary tangible. You get one page with ten planted defects distributed across the three detection layers, and your audit is graded not just on finding them, but on knowing which layer each one needed.
~90 minutes hands-on · Verify stage (maps to module M5, playbook PB-29) · $0 — axe DevTools free tier or @axe-core/cli, any AI assistant, and the screen reader already on your machine (VoiceOver on macOS, NVDA free on Windows).
mkdir a11y-lab && cd a11y-lab
npm init -y && npm install -D @axe-core/cli serve
The page under audit, index.html. Type or paste it exactly — every character is load-bearing:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Grantly — Apply for a small-business grant</title>
<style>
body { font-family: system-ui; margin: 0; padding: 2rem; max-width: 720px; }
.hint { color: #999999; background: #ffffff; font-size: 0.875rem; }
.icon-btn { border: none; background: none; font-size: 1.25rem; cursor: pointer; }
.steps { display: flex; flex-direction: row-reverse; gap: 1rem; }
.toast { min-height: 1.5rem; color: #b91c1c; }
input, select { display: block; margin: 0.25rem 0 1rem; padding: 0.5rem; }
a { color: #4f46e5; }
</style>
</head>
<body>
<h1>Apply for a small-business grant</h1>
<h4>Before you start</h4>
<p class="hint">Applications close on 30 September. Processing takes 6 weeks.</p>
<div class="steps">
<div><b>Step 3</b><br>Review and submit</div>
<div><b>Step 2</b><br>Financial details</div>
<div><b>Step 1</b><br>Business profile</div>
</div>
<img src="grant-flow.png" width="680">
<p>Need the rules? <a href="/rules.pdf">Click here</a>.</p>
<form>
<label for="biz">Business name</label>
<input id="biz" type="text">
<input type="text" placeholder="ABN / registration number">
<label for="state">State</label>
<select id="state"><option>NSW</option><option>VIC</option><option>QLD</option></select>
<button class="icon-btn" type="button" onclick="save()">💾</button>
<button type="submit" tabindex="2">Continue</button>
<button type="button" tabindex="1" onclick="history.back()">Back</button>
</form>
<p class="toast" id="toast"></p>
<script>
function save() {
document.getElementById("toast").textContent =
"Draft could not be saved. Fix the errors above.";
}
</script>
</body>
</html>
Serve it with npx serve . (note the port it prints). The findings instrument, audit.md:
| # | Defect | WCAG SC | Layer that caught it (auto / guided / manual) |
| | | | Severity | Fix |
Layers: AUTO = scanner reported it. GUIDED = AI-assisted structured check
surfaced it. MANUAL = only keyboard/screen-reader use revealed it.
Rule: record each defect under the FIRST layer that caught it.
npx axe http://localhost:3000 --exit (match your serve port). Expected: a violations list with rule IDs and nodes. Transfer each distinct defect into audit.md as an AUTO row with its WCAG success criterion (axe gives you the mapping). Expected count: about four distinct real defects. Note the number — you will need the "axe found N of 10" figure later.audit.md: what order does focus move in? Does it match the visual and logical order? Can you reach and operate every control? Press the save button via keyboard and watch what happens — did anything tell you what changed?index.fixed.html fixing all ten. Re-run axe (expect 0 violations), redo the keyboard tab-through and a 2-minute screen-reader spot check on the steps, the save button, and the toast. Automated-clean plus manual-verified is the definition of done here.audit.md: N of 10 caught by axe; M more by guided checks; the remainder by hand — and what this ratio means for a team whose CI gate is "axe passed.""AI made it" is not an accessibility defense, and neither is "the scanner passed." If this page shipped, the legal exposure attaches to the org regardless of which tool — or model — wrote the markup.
audit.md has exactly ten defect rows, each with element, WCAG SC, severity, first-catching layer, and fix.index.fixed.html: axe reports zero violations and your keyboard/screen-reader spot check passes.Automated scans catch the markup facts, guided AI checks catch the semantic judgments, and only a human with a keyboard and a screen reader catches the experience failures — an accessibility claim is only as strong as the deepest layer you actually ran.