Quick Answer
AI code generators routinely produce images without alt attributes and interactive buttons without aria-label, failing WCAG 2.1 Success Criteria 1.1.1 and 4.1.2. Screen reader users encounter unlabelled controls that are announced as "button" with no context. Both issues are mechanical fixes that take seconds per element but are almost never present in first-generation AI output.
The Scale of the Accessibility Problem in AI-Generated Code
Accessibility is the category most consistently skipped by AI coding tools. When Bolt, Lovable, v0, or Cursor generate a UI component, they optimise for visual appearance and functional correctness - the two dimensions the developer can immediately evaluate. Accessibility attributes are invisible during development and don't cause build failures, so they land at the bottom of the priority stack.
The numbers are stark. According to WebAIM's 2024 Million report, 95.9% of home pages had detected WCAG 2 failures, with missing alternative text on images (54.5% of home pages) the second most common failure after low-contrast text, and empty buttons (28.2%) also in the top six. For AI-generated applications, those figures are likely higher because the tools have no automatic WCAG validation in their output pipeline.
The legal exposure is real. In the United States, digital accessibility lawsuits under the ADA topped 4,000 in 2024, according to UsableNet's 2024 year-end lawsuit report, and passed 5,000 in 2025. The failures this article covers are among the most common: in WebAIM's data, missing alt text and unlabelled form inputs are the second and third most common failures.
Missing Alt Text: What WCAG Requires
WCAG 2.1 Success Criterion 1.1.1 (Level A) requires that every non-text content element - primarily images - has a text alternative. For images that convey information, the alt text must convey the same information. For decorative images, the alt attribute should be set to an empty string (alt=""), which signals to screen readers to skip the image entirely.
AI tools produce three distinct failure patterns:
// โ BAD - No alt attribute at all (WCAG 1.1.1 failure)
<img src="/product.jpg" className="product-image" />
// โ BAD - Filename as alt text (meaningless to screen reader users)
<img src="/product.jpg" alt="product.jpg" />
// โ BAD - Generic filler text
<img src="/product.jpg" alt="image" />
When an alt attribute is missing entirely, many screen readers fall back to announcing the filename - so users hear "product dot jpg" instead of any meaningful description. Generic values like "image" or "photo" are no better: they tell the user that content exists but nothing about what it is.
// โ
GOOD - Descriptive alt text that conveys meaning
<img src="/product.jpg" alt="Blue running shoes with white sole, size 10" />
// โ
GOOD - Empty alt for purely decorative images
<img src="/decorative-swoosh.svg" alt="" role="presentation" />
// โ
GOOD - Dynamic alt text from data
{products.map(product => (
<img
key={product.id}
src={product.imageUrl}
alt={product.imageAltText || product.name}
/>
))}
Missing ARIA Labels on Buttons and Interactive Elements
WCAG 2.1 Success Criterion 4.1.2 (Level A) requires that all user interface components have names that can be programmatically determined. A button with only an icon and no text has no accessible name unless you provide one via aria-label or aria-labelledby.
AI tools generate icon buttons constantly - close buttons, menu toggles, social share buttons - and almost never include accessible labels:
// โ BAD - Icon-only button with no accessible name
<button onClick={closeModal}>
<XIcon className="w-4 h-4" />
</button>
// โ BAD - Generic label that adds no context
<button aria-label="button" onClick={openMenu}>
<MenuIcon />
</button>
// โ BAD - aria-label on non-interactive element instead of the button
<div aria-label="Close dialog">
<button onClick={closeModal}><XIcon /></button>
</div>
Screen readers announce the first example as "button" - giving keyboard users and screen reader users no indication of what the button does. In a modal with three icon buttons (close, minimize, expand), all three are announced identically as "button".
// โ
GOOD - aria-label on the interactive element
<button aria-label="Close dialog" onClick={closeModal}>
<XIcon className="w-4 h-4" aria-hidden="true" />
</button>
// โ
GOOD - Visible text that also serves as the accessible name
<button onClick={openMenu}>
<MenuIcon aria-hidden="true" />
<span>Open menu</span>
</button>
// โ
GOOD - aria-labelledby referencing visible text
<button aria-labelledby="delete-label" onClick={deleteItem}>
<TrashIcon aria-hidden="true" />
<span id="delete-label">Delete item</span>
</button>
Note the aria-hidden="true" on the icon in the good examples. SVG icons should be hidden from the accessibility tree when a separate text label exists, otherwise screen readers announce both the label and any title embedded in the SVG.
WCAG Failure Reference for Common AI-Generated Patterns
| Pattern | WCAG Criterion | Level | Fix |
|---|---|---|---|
| Image without alt attribute | 1.1.1 Non-text Content | A | Add descriptive alt text |
| Icon-only button, no label | 4.1.2 Name, Role, Value | A | Add aria-label to the <button> |
| Form input without label | 1.3.1 Info and Relationships | A | Add <label for> or aria-label |
| Low color contrast (<4.5:1) | 1.4.3 Contrast (Minimum) | AA | Adjust foreground/background colors |
| Link with text "click here" | 2.4.4 Link Purpose (In Context) | A | Use descriptive link text |
| No keyboard focus styles | 2.4.7 Focus Visible | AA | Restore :focus-visible outline |
How to Audit Your AI-Generated App for Accessibility
Manual review catches the obvious cases, but a systematic approach is more reliable:
- Run axe DevTools or Lighthouse accessibility audit: Both tools run in Chrome DevTools and automatically flag missing alt text, missing form labels, and insufficient color contrast. Lighthouse's accessibility score is a weighted average of its automated audits, not a measure of WCAG conformance, so a perfect score still needs manual testing.
- Navigate with a keyboard only: Tab through every interactive element on each page. If you can't reach a control with Tab, or if focus disappears into an unlabelled region, you have a keyboard accessibility failure.
- Test with a screen reader: VoiceOver (macOS/iOS), NVDA (Windows, free), or TalkBack (Android) will reveal every missing label. Navigate through your app with the screen on and rely entirely on what's announced.
- Run automated scanning: Tools like VibeDoctor's Vibe Check automatically scan your codebase for images without an alt attribute and buttons with no visible text, aria-label, aria-labelledby or title (FE-005) and flag specific file paths and line numbers. Free to sign up.
- Add ESLint jsx-a11y: The
eslint-plugin-jsx-a11ypackage catches accessibility violations at write-time in your editor. Configure it with the recommended ruleset in your ESLint config.
Supabase, Vercel, and Next.js Specific Notes
Next.js includes a small accessibility check via its ESLint configuration. The eslint-config-next package turns on six jsx-a11y rules, such as alt-text and aria-props, all set to "warn" rather than "error", and none of them flags an icon-only button without a label. Since Next.js 16, next build no longer runs ESLint at all, so these warnings only appear if you run ESLint yourself.
When deploying to Vercel, the build does not run accessibility audits. Vercel's toolbar includes an accessibility audit tool that flags issues while you browse a deployment, but a site with hundreds of WCAG failures still deploys without being blocked. Accessibility validation must be added to your CI pipeline explicitly - either via axe-core in your test suite or via a Lighthouse CI GitHub Action.
FAQ
Does missing alt text affect SEO as well as accessibility?
Yes. Google uses alt text to understand image content for Google Images search. Missing or generic alt text means your images won't rank for relevant searches. Descriptive alt text that matches the surrounding content context is both an accessibility requirement and an SEO best practice. The two goals are almost always aligned.
What is the difference between aria-label and aria-labelledby?
aria-label provides an accessible name directly as an attribute value. aria-labelledby points to another element whose text content becomes the accessible name. Use aria-labelledby when a visible label already exists on the page - this avoids duplication and keeps the visible and accessible names in sync. Use aria-label when no visible label is present or practical.
Do I need alt text on SVG icons?
Decorative SVG icons used alongside text labels should have aria-hidden="true" so screen readers skip them. If an SVG is the only content conveying information (a standalone logo or informational diagram), it needs either a <title> element inside the SVG, or role="img" and aria-label on the <svg> element.
What WCAG level should I target?
WCAG 2.1 Level AA is the standard referenced in most accessibility legislation globally, including the European Accessibility Act and ADA case law precedent in the US. Level A is the minimum floor - meeting only Level A still leaves significant barriers for disabled users. Target Level AA as your baseline and treat Level AAA criteria as enhancements where practical.
Can Tailwind CSS components be made accessible?
Yes. Tailwind applies only visual styles - it has no inherent accessibility implications. Accessibility comes from the HTML structure and ARIA attributes, not CSS classes. Headless UI (from the Tailwind team) provides unstyled accessible component primitives specifically designed to be combined with Tailwind. Radix UI is another solid option for accessible primitives in React apps built with Cursor or Bolt.