Skip to main content
Reachablr

AI-Generated Code Is Inaccessible by Default — and It's Now Your Largest Source of Accessibility Debt

7 min read
AI-Generated Code Is Inaccessible by Default — and It's Now Your Largest Source of Accessibility Debt

The volume of frontend code being written by AI assistants has changed the shape of the accessibility problem. Not the nature of it — the same six WCAG failures dominate as always — but the rate, and the reason.

A developer who writes a <div> with a click handler and no keyboard support has made one mistake, once, in one component. An AI assistant that produces the same pattern produces it every time it is asked for something clickable, across every component, at the speed of accepted autocomplete. The defect rate per line has not necessarily gotten worse. The lines have gotten much more numerous.

WebAIM's 2026 Million report found 95.9% of top home pages had detectable WCAG failures, up from 94.8% the year before, with average errors per page climbing 10.1%. That reversal has multiple causes, but it coincides precisely with the period in which AI-assisted development became the default workflow.

Why Generated UI Code Fails

The failure mode is not that models do not know about accessibility. It is that nothing in the request asks for it, and generated code optimizes for what was asked.

Ask an assistant for "a dropdown menu component" and you will typically get something that looks right, renders correctly, and works with a mouse. Whether it also gets role="menu", arrow-key navigation, aria-expanded that stays in sync with state, focus returned to the trigger on close, and Escape handling depends almost entirely on whether accessibility appeared in the prompt.

Three properties of generated code make this worse than an equivalent human mistake.

It looks finished. Code that renders correctly and passes a visual check reads as complete. A missing aria-label on an icon button is invisible in the browser. There is no red squiggle, no console warning, no visual artifact. The absence of an attribute does not announce itself.

The reviewer often did not write it. A developer who understands the code they are reading will notice an unlabeled input or a div acting as a button. Someone reviewing a large block of generated output they did not author is reviewing for behavior, not for semantics. The review question shifts from "is this right?" to "does this work?" — and inaccessible code works, for most reviewers.

It arrives in volume. Accessibility review scales with the reviewer's attention, and generated code arrives faster than attention does. A 400-line generated component gets a fraction of the per-line scrutiny of a 40-line hand-written one.

What the Research Actually Says

It is worth resisting the simple version of this story, because the research is more interesting than "AI writes bad code."

A comparative study of LLM accessible-code generation capability found that models — GPT-4o and Qwen2.5-Coder among them — frequently produced more accessible code than human baselines. Models have absorbed an enormous amount of correct accessibility practice from documentation, MDN, WAI-ARIA authoring practices, and well-written open source.

The capability is there. The finding that matters is that it is conditional. Accessible output depends on prompting and oversight. Benchmarking consistently shows that the difference between accessible and inaccessible generated output frequently comes down to a single sentence in the prompt.

So the problem is not model capability. It is defaults. Ask for a component and you get a component. Ask for an accessible component and you get a meaningfully better one. Most requests are the first kind, because most developers are thinking about the feature, not the success criterion.

This is genuinely good news, because defaults are much easier to change than capabilities.

Control One: Put Accessibility in the Default Context

The highest-leverage change is to stop relying on individual prompts and move accessibility requirements into the persistent context every generation sees.

Every major coding assistant supports project-level instruction files — CLAUDE.md, .cursorrules, copilot-instructions.md, and equivalents. A short, specific accessibility contract in that file applies to every generation without anyone remembering to ask.

What belongs in it:

  • Use semantic HTML elements before reaching for ARIA. Never <div role="button"> where <button> will do.
  • Every interactive element must have an accessible name, keyboard operability, and a visible focus indicator.
  • Every form input requires an associated <label>. Placeholder text is not a label.
  • Every image requires alt; decorative images require alt="".
  • State communicated visually must also be communicated programmatically — aria-expanded, aria-selected, aria-current, aria-invalid — and must stay synchronized with the component state that drives the visual.
  • Custom interactive components must specify their full keyboard interaction model.

Be concrete rather than aspirational. "Follow WCAG 2.2 AA" produces little; "every icon-only button must have an aria-label describing the action, not the icon" produces a different result.

One caution: this reduces the defect rate substantially. It does not eliminate it. Instructions are guidance, not enforcement, and they degrade over long sessions and large contexts. Treat this as the first control, not the only one.

Control Two: Enforce at the Pull Request

Guidance that is not enforced is guidance that drifts. The enforcement point that survives contact with a real team is the pull request, because it is the one gate every change already passes through and the one where a failure is cheap to fix.

The pattern that works:

  1. CI builds the branch and starts a preview server.
  2. An accessibility scanner runs against the changed pages.
  3. Results are compared against a baseline from the main branch.
  4. Only net-new violations fail the check.
  5. The report is posted as a PR comment naming the rule, the URL, and the element.

The baseline comparison is what makes this survivable. Gating on absolute violation count means an established codebase can never merge anything. Gating on new violations means existing debt does not block work but new debt cannot enter — and given that AI-assisted development is generating new code faster than any team is remediating old code, stopping the inflow is the more urgent of the two.

Static analysis on the repository catches a second class of defect that rendered-page scanning misses: aria-hidden on a container with focusable children, an onClick without a corresponding key handler, a component whose accessible name depends on a prop no caller provides. These are structural problems in the component source, visible in code but not always in a given rendered state.

Reachablr's PR scanning implements the baseline-diff workflow against preview deployments, and its code scanning runs the structural analysis against component source. For React and JavaScript codebases, auto-fix proposes concrete changes for the mechanical failures — missing labels, empty accessible names, incorrect or redundant roles — which covers a large share of what generated code gets wrong.

Control Three: Keep the Human Review Where It Matters

Automated tooling detects roughly 30–40% of WCAG issues. That share is disproportionately the mechanical failures that generated code produces most often, which makes automation unusually well-matched to this specific problem — but it leaves the harder 60%.

The judgment-dependent issues remain judgment-dependent: whether alt text is meaningful rather than merely present, whether focus order matches visual order, whether an error message actually explains how to fix the error, whether a custom widget's interaction model matches what a screen reader user would expect.

Generated code is particularly weak on the "meaningful" versions of these. An assistant will happily produce alt="image" — technically present, functionally useless, and invisible to any automated check.

What to do: Keep a quarterly screen reader pass on your primary user flows. Keyboard-only test every new interactive component before merge. These are the checks that no scanner replaces, and they are cheap relative to the volume of code they cover.

The Shape of the Problem

AI-assisted development did not introduce a new category of accessibility failure. It industrialized the existing ones.

The defects are the same six that WebAIM has been reporting for seven years. What changed is that they are now produced by a system that operates at machine speed, defaults to inaccessible output when accessibility is not requested, and generates code that looks finished to reviewers who did not write it.

That combination is only manageable with enforcement in the pipeline. Instructions in a context file shift the defaults. A PR gate catches what slips past. Human review covers what neither reaches. Teams running all three are shipping accessible code faster than they used to. Teams running none are accumulating debt at a rate no annual audit will catch up with.

Scan Your Site for Free

Reachablr scans live URLs, analyzes your source code, checks every pull request, and auto-fixes React and JavaScript — WCAG 2.1 AA coverage across your entire development workflow.

Get Started Free