Skip to content

Claude Code Production EngineeringStep 3 of 8

Claude Code Accessibility Audit: Complete WCAG Workflow

W3C is explicit that tools cannot do it all. This is the division of labour: what a scanner answers, what reading components adds, and the manual passes that decide whether any of it worked.

Claude Code Guides: Claude Code Accessibility Audit. Two panels dividing checks a tool can answer from the ones only a person can.

What a scanner reaches, and what it cannot

Accessibility is the area where automation is most oversold, so it is worth being precise about the division of labour before anything else.

Two columns dividing accessibility checks. A tool can answer mechanical questions such as a missing alt attribute, an empty button, a contrast ratio, a missing form label, a skipped heading level, a duplicate id, a missing lang attribute and an invalid ARIA value. Only a person can answer whether the alt text is meaningful, whether the tab order is logical, whether focus is visible where it lands, whether an announcement makes sense, whether an error is recoverable and whether a shortcut is discoverable.
Zero errors on the left says nothing about the right. W3C is explicit that tools cannot do it all.

The W3C Web Accessibility Initiative states it plainly: "tools can't do it all. Some accessibility checks just cannot be automated and require manual intervention", and warns that "in some cases tools can provide inaccurate results".

What Claude Code adds sits between the two columns. It reads components rather than rendered pages, so it catches structural problems a scanner misses — a click handler on a div across forty components, an ARIA pattern applied inconsistently, a focus trap that only exists in one branch of a conditional. What it cannot do is tell you whether the experience makes sense, because that requires being a person using it.

Which standard you are auditing against

Name it before you start, because "accessible" on its own is not checkable.

The current W3C Recommendation is WCAG 2.2, published 5 October 2023 with an update on 12 December 2024. WCAG 2.0, 2.1 and 2.2 are all still existing standards, and they are backwards compatible — content conforming to 2.2 also meets the earlier versions. WCAG 3 remains an early draft and is not something to audit against.

Most organisations target Level AA. That is what regulations generally reference and what the criteria in this guide assume.

The workflow

Inventory → Automated scan → Claude Code review → Fix
  → Automated retest → Keyboard test → Screen reader test
  → Manual validation

The order matters in one specific way: the automated scan comes before the code review, and the manual passes come last. Running the scan first clears the mechanical noise so the review is spent on structural questions. Doing the manual passes last means you are testing what you shipped rather than what you started with.

Inventory first, though, or you will audit the homepage and call it done:

List every distinct page template and every interactive component
in this project. For each component, note whether it involves
focus management, keyboard interaction, or dynamic content
updates. I want the audit scope, not an assessment yet.

Automated tools, used properly

axe is the most widely used engine, available as a browser extension and as axe-core for CI. It is deliberately conservative: it reports issues it can determine with confidence, which is why a clean axe run is a floor and not a result.

Lighthouse includes an accessibility category built on axe. The number it produces is a proportion of the automated checks that passed. It is not a WCAG conformance score and does not claim to be.

Browser accessibility inspectors — the Accessibility panel in Chrome or Firefox DevTools — show the accessibility tree, which is what assistive technology actually receives. When a component behaves oddly with a screen reader, this is where you find out why.

Run one of these in CI and fail the build on new violations. It is cheap and it stops regressions. It does not tell you the site is accessible.

Semantic HTML, first and mostly last

Most accessibility problems are not missing ARIA. They are HTML that describes the wrong thing.

Pattern Problem Fix
<div onclick> Not focusable, not announced as a control, no Enter or Space <button>
<a> with no href Not in the tab order <button>, or give it a real destination
A button that navigates No middle-click, no open-in-new-tab, wrong announcement <a href>
Layout tables Announced as data with meaningless relationships CSS grid or flexbox
Bold text as a heading Invisible to heading navigation A real <h2><h6>
No landmarks No way to skip to the main content header, nav, main, footer

The rule to write into your CLAUDE.md: semantic HTML first; a div with a click handler is not a button. It prevents more accessibility work than any audit removes.

Headings

Headings are the document outline, and for a screen-reader user they are the primary means of navigating a page. One h1 describing the page. Levels descend without skipping — an h2 followed by an h4 implies a level that does not exist. And headings are chosen for structure, not for the size they happen to render at; if a heading looks too large, that is a CSS problem.

Keyboard navigation

The single highest-value manual test, and it takes minutes. Put the mouse down and Tab through the primary journey.

What you are checking: everything interactive is reachable; the order follows the visual order; focus is always visible; nothing traps you; Escape closes what it opened; and Enter and Space do what they should on each control type.

Focus visibility is the one most often broken deliberately. A stylesheet containing outline: none with no replacement is a failure — the ring was removed because it looked untidy, and now nobody can tell where they are. Replace it, do not delete it. :focus-visible exists so that keyboard users get an indicator without mouse users seeing one.

Tab order follows DOM order, so a CSS reorder — order in flexbox, or grid placement — can make focus jump around the screen unpredictably while looking fine.

Focus management

Anything that changes what is on screen has to decide where focus goes. This is where component libraries most often fall short.

For a modal: focus moves into the dialog when it opens, is trapped inside while it is open, Escape closes it, and focus returns to the element that opened it. The same pattern applies to drawers and off-canvas navigation.

For content that appears in place — an expanded accordion, a revealed panel — focus usually should not move, but the control needs aria-expanded so its state is announced.

In a single-page application, a route change that updates the DOM without moving focus leaves a screen-reader user on the previous page's content with no indication anything happened.

Forms

Every input has a programmatically associated label — a <label for> pointing at the input's id. A placeholder is not a label: it disappears on focus, frequently fails contrast, and is not reliably announced.

Beyond labels: mark required fields in text as well as with an asterisk or colour; put instructions before the input rather than after it; associate error messages with their field via aria-describedby; describe errors in words rather than only in red; move focus to the first error on failed submission; and set autocomplete on name, email, address and payment fields, which is both a usability and a WCAG matter.

Links, buttons and accessible names

Every control needs a name that makes sense read out of context, because that is exactly how assistive technology presents them — a list of links, one after another.

So: no bare "click here" or "read more" repeated down a page; icon-only buttons get an aria-label or visually hidden text; a link wrapping an image needs alt text on the image or a label on the link; and a link that opens a new tab says so in its name rather than only through an icon.

One pattern worth knowing because it looks like a bug and is not: a card whose image and heading both link to the same place is often built with the image link marked aria-hidden="true" and tabindex="-1". That is correct — it removes a duplicate, unlabelled stop from the tab order. An audit script that flags "link with no accessible name" without checking for that will report a correct implementation as a fault, which is worth knowing before you act on a scanner's output.

Images and alternative text

The decision is what the image is for, not what it depicts:

Kind Alt
Informative What it conveys, in context. Not a description of every pixel
Decorative alt="". Empty, not missing — missing makes screen readers read the filename
Functional (a link or button) What it does, not what it looks like
Complex (chart, diagram) A short alt plus a longer description in the page
Text in an image The text, verbatim

This is the clearest case of something an agent can flag and cannot resolve. It can tell you an image has no alt. It cannot tell you whether "chart showing quarterly results" conveys what the chart conveys — only someone who knows what the chart is for can.

And do not keyword-stuff alt text. It is worse for the person relying on it and it is not an SEO technique.

Colour and contrast

At Level AA: normal text needs a contrast ratio of at least 4.5:1, large text at least 3:1, and non-text user interface components and graphical objects at least 3:1.

Three things a checker will not catch:

Composited colour. Text at 60% opacity over a gradient has an effective ratio the stylesheet does not state. Compute it against the actual rendered backdrop.

Focus indicators. A focus ring that fails 3:1 against its surroundings is not an indicator. This is easy to miss because it is only visible in one state.

Colour as the only signal. A required field marked only in red, a chart distinguished only by hue, a link identified only by colour within body text. Add a shape, a label, an icon, or an underline.

A real example from this site: a primary call-to-action rendered at 1:1 — text and background the same colour — because a generic link rule outranked the button rule by one point of CSS specificity. Nothing in the source looked wrong, and it was only found by computing contrast against the actual composited backdrop in a rendered DOM. The full account is in the case study for this site.

Target size

WCAG 2.2 added Success Criterion 2.5.8 Target Size (Minimum) at Level AA: "The size of the target for pointer inputs is at least 24 by 24 CSS pixels", with five exceptions.

The exception that matters most in practice is Inline: "The target is in a sentence or its size is otherwise constrained by the line-height of non-target text." A link inside a paragraph is exempt. A standalone link in a list item, a navigation item, or an icon button is not.

The others are Spacing (undersized targets far enough apart that 24px circles centred on them do not intersect), Equivalent (the same function is available through a conforming control on the same page), User Agent Control (the size is the browser's and unmodified by the author), and Essential (a particular presentation is required).

Worth knowing because a naive check produces a wall of false positives: an inline <a> boxes to the font's content height regardless of the line-height around it, so every in-sentence link in an article will measure around 20px tall and every one of them is exempt.

ARIA, and why it is last

The first rule of ARIA is not to use it: if a native element does the job, use the native element. ARIA changes what is announced without changing behaviour, so a div with role="button" is announced as a button and still does nothing when you press Enter unless you wrote that yourself.

Common misuse worth auditing for: roles on elements that already have them (<button role="button">); aria-label overriding perfectly good visible text, which breaks voice control because the spoken name no longer matches the visible one; aria-hidden="true" on something focusable, which is a genuine fault — hidden from screen readers but still reachable by keyboard; and live regions on everything, so nothing stands out.

Where ARIA is genuinely needed: aria-expanded on disclosure controls, aria-current for the current page in navigation, aria-live for status messages that appear without a page change, and the dialog pattern, where getting it right by hand is hard enough to justify a well-tested library.

Dynamic interfaces

The components where accessibility is usually lost. For each: modals need focus moved in, trapped, Escape to close, and focus returned; toasts need an appropriate live region and enough time to be read; accordions and tabs need state announced and the documented keyboard interactions; menus need arrow-key navigation and Escape; carousels need pause control and must not move focus on their own; and async updates need a way to announce that something changed.

The ARIA Authoring Practices Guide documents the expected keyboard behaviour for each of these. Follow the pattern rather than inventing one.

The accessibility prompt

Review this project for accessibility problems against WCAG 2.2
Level AA.

Do this in order:
1. Inspect the components and templates. Change nothing yet.
2. For each issue: the component, the file and line, what a user
   experiences, and the success criterion if you are confident
   which applies. If you are not confident, describe the problem
   and say the criterion is uncertain rather than guessing one.
3. Rank by user impact: blocks a task, makes it harder, or is a
   polish issue.
4. Propose the smallest fix. Prefer changing the element to
   adding ARIA. Do not add ARIA where a native element would do.
5. Separately, list what you could NOT determine from the code and
   that needs a person: anything about tab order in the rendered
   page, whether alt text is meaningful, whether an announcement
   makes sense, and anything requiring a screen reader.

Do not claim any of this proves conformance.

Step 5 is what makes the output usable. Without it you get a list that reads as complete and silently omits everything automation cannot reach.

A finding shape that keeps the manual half visible:

Issue:
Component:
Impact:                blocks | hinders | polish
Evidence:              file:line, or what was observed
Recommended fix:
Automated validation:  what a tool can confirm
Manual validation:     what a person still has to check

The manual pass

None of this is optional, and none of it can be delegated to a tool.

  • Keyboard only. Complete the primary journey without touching the mouse.
  • Focus visibility. At every stop, can you see where you are?
  • Zoom to 200%. Nothing lost, nothing overlapping, no horizontal scrolling of the page.
  • Reflow at 320px. The mobile width where things break.
  • A screen reader. VoiceOver on macOS or iOS, NVDA on Windows, TalkBack on Android. Half an hour of genuine use teaches more than any checklist.
  • Forms. Submit an empty one. Can you tell what went wrong and fix it without sight of the colour?
  • Dialogs. Open, Tab around, Escape, and confirm focus came back.
  • Reduced motion. Is anything still moving, and is anything lost?

What you must not claim

This matters legally as well as ethically, so state it precisely.

  • A Lighthouse accessibility score of 100 does not mean WCAG conformance. It means the automated subset passed.
  • Zero axe violations does not mean accessible. It means no violations the engine can detect with confidence.
  • Claude Code cannot certify conformance. It can find problems and propose fixes. It cannot make a conformance claim.
  • None of this replaces testing with disabled users. A pattern can satisfy every criterion and still be unusable. Only people who rely on the technology can tell you.

The honest formulation for a report is what was tested, by what method, what was found, and what remains untested. An audit that claims more than it checked is worse than a short one that admits its edges.

Where to go next

Accessibility is one pass of a wider inspection — the website audit guide covers the others, and the performance workflow and the security audit are its siblings. For building this in from the start rather than retrofitting it, the complete website workflow is the place to begin. The free website audit checklist and launch checklist both carry the accessibility items in short form.

Sources and further reading

Read these next

See how this fits into Claude Code Production Engineering

Continue your learning path

  1. Next

    Mention @claude and it implements changes. Give it a prompt and it runs on every pull request without anyone asking. ...

  2. Then

    Continuous deployment is usually sold on speed, which is the least interesting thing about it. The reason to build on...

  3. Going deeper

    "Make the website faster" is not a task an agent can complete. This is the loop that works: measure, find the one bot...

The whole sequence: Claude Code Production Engineering

Free download

The CLAUDE.md Starter Kit, free

Four working CLAUDE.md files you can drop into a project today, plus the one-page checklist for what belongs in one and how to tell whether yours is actually working.

  • CLAUDE.md for a static marketing site
  • CLAUDE.md for a web application, with security and migration rules
  • CLAUDE.md for a Shopify theme, including the gotchas that cost hours
  • CLAUDE.md for a shared package in a monorepo
  • A one-page checklist, and how to test the file is actually working
What are you working on?

The download appears here as soon as you submit. I will also email you when there is a new guide worth reading. No fixed schedule, no selling your address, unsubscribe from any email. See the privacy policy.