An accessibility audit checklist: what to test, in what order, and what counts as done
Most accessibility checklists are a flat list of forty things: they tell you what exists, not what to do first, and never when you're finished. This one is split by who can verify each item — a scanner or a person — because that split sets the order. Do the machine column, fix it, then spend your afternoon where judgement is required.
1. Before you test anything, write the scope down
Scope is one sentence, and it settles the argument you'd otherwise have later:
WCAG 2.2 level AA. Template: [name]. Tested [date] at 1440px, 200% zoom and 320px, in Chrome and Safari with NVDA.
Conformance is a claim about a defined scope on a defined date, so both belong in writing before you open a tool. A page is not a site: if the site has five templates, that sentence names the one this covers, and a snapshot can't tell you what the form does after submit.
Nothing here is legal advice. A checklist records what you tested; it doesn't decide what you're allowed to claim to a client.
2. The split that decides everything: what a machine can prove
Most checklists leave this part out. Twelve failures can be proven from the HTML alone, because the attribute is there or it isn't. Everything else needs a person.
| What you're checking | WCAG 2.2 | Why the HTML settles it |
|---|---|---|
| Every image has a text alternative | 1.1.1 (A) | alt is present or absent. An empty alt="" is valid for decorative images and is not a failure. |
| Every form field has a label | 3.3.2 (A) | A <label for>, wrapping <label>, aria-label, aria-labelledby or title exists — or none does. A placeholder is not a label. |
One <h1>, no skipped levels | 1.3.1 (A) | Count them. Two <h1>s, or an <h2> followed by <h4>, is wrong in a way no reading of the page changes. |
The page has a descriptive <title> | 2.4.2 (A) | The <title> element is empty or it isn't. |
| The page declares its language | 3.1.1 (A) | <html lang="en"> is there or it isn't. Without it a screen reader guesses the voice, and guesses wrong on names. |
| Links describe where they go | 2.4.4 (A) | A link whose entire label is "click here", "read more", "more" or a bare URL is flagged as a string. |
| Pinch-zoom isn't blocked | 1.4.4 (AA) | user-scalable=no, or maximum-scale capped below 2, in the viewport meta tag. A string match. |
| Buttons and links have an accessible name | 4.1.2 (A) | An icon-only button with no aria-label and no inner alt has nothing to announce. |
| Embedded frames have a title | 4.1.2 (A) | Every <iframe> — video, map, form, chat widget — has a title or it doesn't. |
| Ids are unique | 4.1.2 (A) | The same id twice breaks every <label for> and aria-labelledby pointing at it. |
No positive tabindex | 2.4.3 (A) | tabindex="1" or higher forces an artificial focus order. Present or absent. |
| Data tables have header cells | 1.3.1 (A) | A table with <td> cells and no <th> has no headers to associate. role="presentation" layout tables are excluded. |
Read that column as provable, not important. A scanner can prove a failure; it can't prove a pass. A page that clears all twelve has no missing alt attributes — it does not have good alt text, and is not accessible.
3. Pass 1: the machine pass, always first
Use more than one scanner: they disagree at the edges, and the disagreement is informative. WAVE draws problems onto the page so you see where they land, axe DevTools runs the axe-core engine most other tools embed, Lighthouse gives a repeatable score in DevTools and PageSpeed Insights, Pa11y runs scripted across a list of URLs. All free. Run two, fix what they agree on, then look at what only one found.
Then clear the mechanical failures before anything else. An unlabelled input makes a screen-reader pass useless — you're listening to "edit text, blank" and learning nothing — and a missing lang changes the voice of the page. Fixing the cheap, provable things first isn't tidiness; it stops you misdiagnosing the expensive ones.
AuditReady runs the twelve checks in the table above and lays them out as a printable report — one option among these tools, not a requirement for this page.
4. Pass 2: the keyboard-only pass
Put the mouse away. This pass finds what no scanner can see, and takes about twenty minutes per template.
- Click into the browser chrome, press Tab once. Is focus visible on the first focusable thing?
- Tab through the page. Does the order match the visual order, or does focus jump from the footer back to the header?
- Does focus ever land somewhere you can't see — a link in a collapsed menu, something behind a sticky header?
- On a page with a header, does the first Tab reveal a skip link, and does Enter actually skip?
- Open the main menu with Enter or Space, close it with Escape. Does focus return to the button that opened it?
- Do the same with any dialog or modal: focus moves inside, stays inside while it's open, and comes back to the trigger on close.
- Complete a form with the keyboard only, submit it with an invalid value, and read the error.
- Tab to the end. Is there anything clickable with a mouse but unreachable with Tab — a custom dropdown, a
<div>with a click handler, a carousel arrow?
Write down what you did and what happened, in that order. "Tab, 1–5 on the pricing card, nothing visible happened" is a finding someone can fix. "Focus not visible" is a complaint.
5. Pass 3: what only a person can judge
Alt text that means something. The scanner checked that alt exists. Does "image-01.jpg" tell a blind customer what the product looks like? Does a chart carry its numbers in text? Does a decorative flourish have alt="" so it gets skipped? For the wording, how to write alt text for images takes it image type by image type.
Link text in context. "Read more" gets flagged as a string. A person decides whether the paragraph around it makes the destination obvious — often the honest answer is "change it to 'read the warranty terms'".
Contrast against what's behind the text. A scanner measures text on flat colour. Text over a photo, gradient or video needs the pixels behind it measured, and the answer changes as the carousel moves.
Screen-reader behaviour. Turn on NVDA or VoiceOver and listen to a form, a dialog and the heading list. Does the order make sense? Does the error get announced? Is the menu called a menu?
Motion. Anything that autoplays, loops or parallaxes: does it respect prefers-reduced-motion, and is there a way to stop it?
200% zoom and 320px wide. Does the content survive without horizontal scrolling, or does the layout break where the client's customers actually read it?
Error messages. Is it useful, does it name the field, and is it where a person looking at the screen will find it?
This is where you'll disagree with the tools most. They test presence; you're testing usefulness.
6. Record it as you go, in the shape of the report
Don't save the write-up for the end — fill it in as you test and the report writes itself. One row per finding, eight columns: page, element, issue, WCAG criterion, severity, evidence, fix, status.
The severity rule that keeps the list honest: critical means it blocks a task — you can't submit the form, can't reach the menu, can't tell what the button does. Not "it looks bad". Evidence is what you did and what you saw, so the developer can reproduce it without asking. The record you keep is also what a public accessibility statement cites, so keep the dates and the scope where you can find them again — the accessibility statement guide covers that half of the job.
7. What counts as done
Not "all the boxes are ticked". Done looks like this:
- The machine pass is clean on every template in scope, on the live build.
- The keyboard pass is complete on every journey a customer can take, with a record of what happened.
- The human checks in pass 3 are done, and each finding has evidence and a fix.
- A dated list of what you could not verify: PDFs, content in languages you don't read, third-party widgets and iframes you don't control, pages behind a login, the screen-reader and browser combinations you didn't have.
- A date for the re-test.
That list is the honest ending, and the one a client should read first: "no issues found" on a page you half-tested invites people to stop looking.
Green means "nothing in this checklist failed on this scope on this date". It does not mean compliant, certified or finished. Put the machine column in CI with Pa11y or axe so it can't regress silently, and re-run the manual passes when a template changes. If you're handing this to a client, the deliverable and its limits are one conversation; the audit report guide goes further.
8. The one-page version
Print this page: section 2 is the machine column, sections 4 and 5 the human column, section 7 the stopping rule — the whole checklist on one sheet, no account, no download.
You're done when the page is clean, the keyboard works, and the list of what you couldn't check is short enough that you'd show it to the client.