Threestudio

Bold ideas, shipped with quiet precision.

Loading 000
Threestudio

Design & UI Resources

How to Audit AI-Generated Frontend Code: A 2026 Checklist

hi3n

Think about the last time you accepted a building's keys without an inspection. You would not. The lobby looks great, the contractor finished early, and nobody…

Cover Image

How to Audit AI-Generated Frontend Code: A 2026 Checklist

Think about the last time you accepted a building's keys without an inspection. You would not. The lobby looks great, the contractor finished early, and nobody has checked the wiring. That is roughly where most teams now stand with AI-generated frontend code.

The numbers explain why. DORA's 2025 report found 90% of developers use AI at work, and more than 80% report productivity gains. Yet nearly 30% trust the code it produces little or not at all. Those facts coexist because the speed is visible and the rework is not. A METR randomized controlled trial made that concrete: experienced developers were actually 19% slower with AI tools, while believing they were 20% faster.

💡 Tip: This post is about auditing the code. If you want the visual-polish side, that is a different problem with a different checklist. See how to make AI-generated UI look professional.

Here is how to audit AI-generated frontend code in three passes that catch what a normal code review will not.

Key Takeaways

- 95.9% of the top million home pages had detected WCAG 2 failures in WebAIM's March 2026 Million analysis. That is the first regression in six years.- Automated tools catch only 57.38% of accessibility issues (Deque, 2,000+ audits). The highest-value checks are manual: keyboard, screen reader, alt-text judgment.- Field data is what ranks, and it is slow. CrUX reports a 28-day rolling average, so never judge a performance fix the same day.- Audit in three passes: accessibility, performance, semantics. Then count the five failure patterns to decide rescue or rebuild.

Why auditing AI-generated frontend code is different

Before the checklist, the diagnosis. AI-generated frontend code does not fail randomly. It fails in patterns, and those patterns cluster in three places.

It passes the eye test and fails the keyboard test

The most recent WebAIM Million, published in March 2026, analyzed the top one million home pages. A staggering 95.9% had detected WCAG 2 failures. That is up from 94.8% the previous year, and the first regression in six years. The average page carried 56.1 errors, a 10.1% increase, and 1,437 elements, up 22.5% in a single year.

The six most common failures account for 96% of all detected errors:

Failure

Share of home pages

Low contrast text

83.9%

Missing image alt text

53.1%

Missing form labels

51%

Empty links

46.3%

Empty buttons

30.6%

Missing document language

13.5%

That is the baseline for the whole web, not just AI output. But AI output has its own signature. The GAAD Foundation's AIMAC benchmark, an open leaderboard launched in May 2025, found that 35 of 37 current AI models produce multiple critical or serious accessibility issues by default when asked to generate HTML.

The failure mode is subtle. As the researchers behind the W4A 2024 study of GitHub Copilot observed, generated output tends to mirror the accessibility of whatever codebase it is dropped into. On an inaccessible site, it produces inaccessible elements. It sometimes invents them outright, adding unlabeled video links that were never requested.

It is confidently, plausibly wrong

Stack Overflow's 2025 survey of roughly 49,000 developers found the top frustration with AI tools is not that the code is broken. It is that the code is "almost right, but not quite," cited by 66% of respondents. A further 45.2% said debugging AI-generated code takes more time than writing it themselves.

This is the hardest class of defect to catch in review, because plausible code reads as correct. Hardcoded ARIA state is the clearest example: aria-expanded="false" written into JSX that never updates does not fail loudly. It quietly tells a screen reader the opposite of the truth.

The governance gap is real, and it is wide

Checkmarx's 2026 application security outlook, a Censuswide survey of 2,350 security and engineering professionals across 14 countries, found that 75% of organizations knowingly deploy code with known vulnerabilities. That is down from 81% the prior year. Some 93% acknowledged a recent breach tied to their own applications. Roughly a third said more than 60% of their code is now AI-generated, and only 22% have a formal AI governance policy.

Veracode's 2025 GenAI Code Security Report tested more than 100 large language models across Java, JavaScript, Python, and C#. AI-generated code introduced a security flaw in 45% of tests. The report's most uncomfortable finding: bigger and newer models were not more secure.

⚠️ Warning: Do not repeat the claim that AI-built websites are slower on Core Web Vitals. I looked for primary data comparing AI-generated sites to human-built ones on performance, and it does not appear to exist yet. Slowness tracks architecture and JavaScript payload, not authorship. The audit below targets the architecture.

One honest caveat before we start. The picture is contested. A peer-reviewed study in IEEE Transactions on Software Engineering in June 2026 analyzed 151 repositories with self-admitted generative AI usage. It found no general increase in code churn, contradicting the vendor research that claims AI is degrading maintainability. Treat any single study here, including the ones above, as one data point rather than a verdict.

The audit itself is mechanical, though. Start where the evidence is strongest.

Pass one: accessibility

Accessibility is your highest-yield pass, because it is the category where AI output is most reliably deficient and where automation gets you furthest.

Run the automated layer first

Four tools cover this well, and you do not need all of them.

axe-core (currently 4.13.0, released August 2026) is the engine most other tools are built on. It ships roughly 90 rules covering ARIA validity, accessible names, contrast, labels, landmarks, and heading order. Wire it into whichever layer you already test:

// axe-core in an end-to-end test: fails the build on WCAG 2.2 AA violations
import AxeBuilder from '@axe-core/playwright';

const results = await new AxeBuilder({ page })
  .withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
  .analyze();

expect(results.violations).toEqual([]);

IBM Equal Access Accessibility Checker (4.0.34, September 2026) is the strongest alternative, and it is notably good at landmark nesting and table header relationships, the two areas axe handles least well.

WAVE from WebAIM is the fastest visual check and the best tool for showing a non-technical stakeholder what is wrong.

pa11y-ci gives you a crawl-and-gate workflow:

# Crawl a sitemap and fail CI if more than 10 issues appear
npx pa11y-ci --sitemap https://your-domain.example/sitemap.xml --threshold 10

One scheduling note: axe-core 4.11.1 changed its luminance threshold constant and fixed oklch and oklab contrast handling in January 2026. If you are re-running an old audit, expect contrast results on modern color palettes to shift.

Then accept that automation stops at 57%

This is the number most teams never see. Deque's coverage research, drawn from more than 2,000 audits and roughly 300,000 issues, found automated testing identifies 57.38% of total issues and covers 16 of the 50 WCAG 2.1 Level AA success criteria.

Lighthouse's accessibility score makes this worse in a subtle way. It is a weighted average, not a pass rate, and each audit is binary: partially passing scores zero. Critically, Lighthouse's own documentation lists trapped user focus, logical tab order, and keyboard-focusable controls as manual checks, which means the highest-value checks are the ones it explicitly will not score for you.

W3C states the limit plainly: no tool alone can determine whether a site meets accessibility standards, and knowledgeable human evaluation is required.

The manual checks that matter

Three checks catch most of what automation misses, in this priority order.

1. Keyboard operability. Put the mouse away. Tab through the page. Can you reach every interactive element, in a logical order, and see where focus is at all times? This is where div-as-button fails hardest: a styled div with no role, no tabIndex, and no onKeyDown handler passes mouse review and is completely unreachable by keyboard.

2. Screen reader pass. Run one flow, the highest-value one, in NVDA with Firefox or VoiceOver with Safari.

3. Alt text judgment. Automation tells you alt text is missing. Only a human can tell you whether the alt text is right. W3C's own guidance is explicit that determining whether alternative text is appropriate requires seeing the image and judging it in context.

The five failure patterns to grep for

These recur constantly in AI-generated components. The priority order matters: keyboard and focus problems outrank ARIA attribute errors, which outrank preference for native elements.

  1. Div-as-button. A div doing a button's job with none of the semantics.

  2. Hardcoded ARIA state. aria-expanded="false" that never updates is worse than no ARIA at all, because it lies.

  3. Broken overlay focus management. No focus moved into the dialog, no trap, no return to the trigger on close.

  4. Native-element reconstruction. Rebuilding button, dialog, or select elements out of divs instead of using the platform primitive.

  5. Design-system stripping. Accessible wiring present on a base component, dropped during composition.

The principle underneath all five: a role without the matching keyboard interaction and state management is worse than no role at all. It promises assistive technology a contract the code does not honor.

Keyboard access is the foundation. Performance is what decides whether anyone stays long enough to use it.

Pass two: performance

Never judge a fix the same day

Field data is what ranks, and field data is slow. Chrome's CrUX API reports a 28-day rolling average at the 75th percentile, and the data runs approximately two days behind today. Fix a Core Web Vitals problem on Monday and you will not see the result for weeks.

This matters because lab and field routinely disagree. PageSpeed Insights shows a lab score from a single throttled simulated load. The pass or fail at the top of the page is field data. A site can score 100 in the lab and fail in the field. In the Web Almanac 2025 dataset, only 48% of mobile and 56% of desktop sites passed Core Web Vitals. Home pages performed worst: 45% mobile, 47% desktop.

INP is the metric to audit. It replaced First Input Delay in March 2024 and measures every interaction, not just the first. The thresholds are 200 ms for good, 200 to 500 ms for needs improvement, and above 500 ms for poor, measured at the 75th percentile.

Debug it in three phases: input delay, processing duration, and presentation delay. Each has a different fix, so attributing correctly is most of the work. The web-vitals library gives you field attribution:

// Capture INP attribution in the field: reportAllChanges is required for
// console debugging, since INP otherwise only reports on page unload
import { onINP } from 'web-vitals';

onINP(console.log, { reportAllChanges: true });

Then look for main-thread contention

Two thresholds do most of this work.

Forced reflow passes only when no reflow exceeds 30 ms. It happens when JavaScript reads geometry after a style or DOM change has invalidated layout. In a loop, it becomes layout thrashing:

// Thrashing: every iteration forces a synchronous layout recalculation
for (const box of boxes) {
  box.style.width = box.offsetWidth + 10 + 'px';
}

// Fixed: batch all reads first, then all writes
const widths = boxes.map((box) => box.offsetWidth);
boxes.forEach((box, i) => { box.style.width = widths[i] + 10 + 'px'; });

Third-party scripts are the other culprit. Lighthouse fails a page when summed third-party blocking time exceeds 250 ms. Since AI-generated pages tend to accumulate analytics tags, chat widgets, and embedded media by default, this is where the weight usually sits. Use the DevTools Coverage tab to find shipped code that never executes.

Reachability and speed handled, the final pass covers what the browser and the screen reader actually parse.

Pass three: semantics and code quality

Validate the rendered DOM, not the template

This is the most commonly skipped step, and it is a one-liner once you know it.

The W3C Nu Html Checker validates HTML, CSS, and SVG, and it is the official replacement for the legacy W3C validator. The mistake is validating your source template. Anything injected at runtime through innerHTML or after hydration is invisible to that check.

Open the Elements panel, copy the outer HTML of the live page, and validate that. You will catch malformed structure that never appears in your source files.

Layer static analysis and bundle inspection

eslint-plugin-jsx-a11y catches a specific and useful set of issues in JSX: alt-text, anchor-has-content, aria-props, click-events-have-key-events, label-has-associated-control, tabindex-no-positive, and about a dozen more. Its documented limitation matters: it only catches errors in static code, which is why it complements rather than replaces the rendered-DOM work above.

For bundles, run webpack-bundle-analyzer or rollup-plugin-visualizer and look for four specific things: the same package resolving to two node_modules paths, a full library imported for one function, CommonJS modules that cannot be tree-shaken, and development-only dependencies leaking into the client bundle.

Then close the loop with the Coverage tab. A large module with near-zero execution coverage is dead weight that a bundle analyzer alone will not flag as dead.

Check for token drift

Hardcoded values are the quiet maintainability killer in AI-generated UI, and here is the honest state of the tooling: no authoritative detector exists. Named community tools are unverified, so do this one manually and reliably.

Lowercase every hex value to six digits, exact-match against your token file, and sort results into two buckets: values that match a token, and values that match nothing. The second bucket is the finding. Either a token is missing from your design system, or the component has gone off-system. Do not fuzzy-match; you want a clean split, not a similarity score.

For the system-level view of why drift happens, see advanced UI design principles.

Watch for orphaned event listeners

removeEventListener only detaches a listener when the function reference, event type, and capture flag all match. This cleanup silently does nothing:

// Broken: passes a new function, so nothing is ever removed
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', () => handler());

// Fixed: stabilize the reference so both calls receive the same function
const stableHandler = useCallback(handler, []);
window.addEventListener('resize', stableHandler);
return () => window.removeEventListener('resize', stableHandler);

To detect leaks, take a heap snapshot, mount and unmount the component about ten times, force garbage collection, take a second snapshot, and compare sorted by delta. One caveat worth stating: a resource still alive after unmount is retention evidence, not proof of a leak, because garbage collection is not observable from a page.

With all three passes complete, the only question left is what to do about what you found.

Rescue or rebuild?

Three passes done. Now the decision, which is usually clearer than it feels.

Rebuild when the failure is structural: keyboard operability is absent across the whole app, semantics are unrecoverable, or the layout layer fights the browser. Patching a fundamentally unreachable interface costs more than regenerating it, and regenerating with an explicit accessibility constraint in the prompt is cheap.

Rescue when the failures are localized and enumerable: contrast, missing labels, a handful of hardcoded ARIA states. These are mechanical fixes with an obvious end state, and the automated tools will tell you when you are done.

The signal that decides it: count how many of the five failure patterns appear. One or two means rescue. Four or five means the generation prompt never specified accessibility, and it will keep producing the same defects every time you regenerate. Fix the prompt before you fix the code.

Conclusion

AI-generated frontend code is not uniquely broken. It is uniquely unaudited. WebAIM's 2026 data shows the web failing accessibility at a rate that got worse for the first time in six years, and Checkmarx found only 22% of organizations have a formal policy governing AI code use.

The fix is not to stop using AI. It is to audit AI-generated frontend code the way you would audit anything else that ships to production. Three passes, in order, with automation doing the first 57% and a human doing the rest.

Start with accessibility, because that is where the yield is highest and the automation is best. Add performance once the keyboard works. Finish with semantics, validating the rendered DOM rather than the template. Then decide whether you are rescuing or rebuilding, and if it is a rebuild, fix the prompt.

If you take one thing from this: put the mouse away and tab through your last AI-generated page. You will learn more in ninety seconds than any dashboard will tell you.

What did the keyboard test catch on your last build? Tell us, or read next on designing agentic UX workflows.

h

hi3n

Keep reading

Related Posts