How-To · Developers · Code

How to Fix the 10 Most Common WCAG Failures (With Copy-Paste Code)

Editorial illustration in oxblood and cream of a checklist transforming from red x marks into check marks
  • How-To
  • Developers
  • Code

Most WCAG failures are not exotic. After enough audits, you stop expecting rare edge cases and start recognizing the same handful of patterns, showing up again on a brand new codebase, a different framework, a completely unrelated industry. The names change. The bugs do not.

That repetition is actually good news. It means you do not need to memorize the entire WCAG spec to make a real dent in your site's accessibility. You need to fix ten patterns, well, everywhere they occur, and most of them take under a minute once you know the fix.

We see this play out constantly in our own audits: a team worries about some obscure edge case in a data table, while the same missing form label or low-contrast gray text quietly fails on every page of the site. Fixing the ten patterns below first gets you disproportionate value, because they rarely live in isolation. They tend to sit in a shared component, so one fix often clears the failure everywhere at once.

Here they are, with the exact code to paste in, and the WCAG success criterion each one maps to, so you know exactly what you are satisfying when the fix ships.

The Stat: More than 1 in 4 images on popular home pages have missing, questionable, or repetitive alt text. (Source: WebAIM Million, 2026)

Common WCAG failures, before and after A two column comparison. The left column, labeled Common failure, lists missing alt, low contrast, no focus style, div-as-button, and unlabeled input, each marked with a red x. The right column, labeled Fixed, lists the same five items marked with a check mark, indicating each has been corrected. Common failure Fixed

x missing alt v missing alt

x low contrast v low contrast

x no focus style v no focus style

x div-as-button v div-as-button

x unlabeled input v unlabeled input

Why the same ten patterns keep showing up

Accessibility bugs cluster because most sites are built the same way: a component library, a design system, a handful of templates copied across pages. Fix a pattern once in the shared button component or the base form styles, and the fix propagates everywhere that component is used. Miss it once, and the same failure ships on every page that touches it. That is exactly why hunting these ten down is worth the hour it takes.

1. Missing or lazy alt text (WCAG 1.1.1, Non-text Content)

An empty alt="" is correct for decorative images. A missing alt attribute, or one that just repeats the filename, is not. Screen reader users get either silence or noise like "IMG_4021.jpg," and neither tells them what is on the page. This is the failure the stat above describes, and it usually happens the same way: teams get the hero image right, then forget product thumbnails, meaningful icons, and images buried in blog posts.

<!-- Bad -->
<img src="team-photo.jpg">

<!-- Good -->
<img src="team-photo.jpg" alt="Five members of the support team at their desks">

<!-- Decorative, correctly hidden from assistive tech -->
<img src="divider-swirl.png" alt="">

Write the alt text as if you were describing the image over the phone. WebAIM's alt text guide is the best reference for edge cases like images inside links or complex charts.

2. Insufficient color contrast (WCAG 1.4.3, Contrast Minimum)

Text that blends into its background is the single most common failure in large-scale audits. It is rarely intentional, it is a light gray picked because it looked "soft" on one bright monitor, then shipped to screens with very different brightness and glare. This deserves its own deep dive, so if contrast ratios and formulas are what you are after, we cover that in detail in WCAG Color Contrast Requirements Explained. Here, the quick fix:

/* Bad: light gray on white, fails at nearly every size */
.muted-text { color: #999999; background: #ffffff; }

/* Good: passes WCAG AA for normal text */
.muted-text { color: #595959; background: #ffffff; }

3. No visible focus indicator (WCAG 2.4.7, Focus Visible)

outline: none is one of the most damaging lines of CSS in circulation. It usually gets added because a browser's default blue focus ring clashed with the brand palette, and nobody replaced it before shipping. Remove it without replacing it, and keyboard users lose all sense of where they are: they tab forward and nothing visibly changes.

/* Bad */
button:focus { outline: none; }

/* Good */
button:focus-visible {
  outline: 2px solid #7A2727;
  outline-offset: 2px;
}

4. A div or span standing in for a button (WCAG 4.1.2, Name Role Value, and 2.1.1, Keyboard)

Divs do not receive keyboard focus, do not fire on Enter or Space, and are not announced as buttons by assistive technology, no matter how convincingly they are styled to look like one. This pattern shows up constantly in custom design systems, where a designer hands over a component that a developer wires up with an onClick handler and forgets the years of built-in behavior a real button element provides for free. Following the ARIA Authoring Practices button pattern, the actual <button> element already handles all of this: focus, keyboard activation, and the correct accessible role.

<!-- Bad -->
<div class="btn" onclick="submitForm()">Submit</div>

<!-- Good -->
<button type="submit" class="btn">Submit</button>

5. Form inputs with no label (WCAG 1.3.1, Info and Relationships, and 3.3.2, Labels or Instructions)

A placeholder is not a label. It disappears the moment someone starts typing, and many screen readers never announce it at all, which leaves a user with no idea what the field they just focused on is actually for. A properly associated <label> also gives everyone a larger, easier click target, since clicking the label text focuses the input, not just the input itself.

<!-- Bad -->
<input type="email" placeholder="Email address">

<!-- Good -->
<label for="email">Email address</label>
<input type="email" id="email" name="email">

6. Errors that only exist visually (WCAG 3.3.1, Error Identification)

A red border around a bad field means nothing to someone who cannot see red, or cannot see the border at all, and a message that appears silently somewhere else on the page will never reach a screen reader user who has already moved focus elsewhere. Errors need to be announced and tied to the field programmatically, following patterns like those in WebAIM's form validation guide.

<label for="email">Email address</label>
<input type="email" id="email" aria-describedby="email-error" aria-invalid="true">
<span id="email-error" role="alert">Enter a valid email address</span>

7. Link text that says nothing (WCAG 2.4.4, Link Purpose In Context)

"Click here" and "Read more," repeated a dozen times on one page, are indistinguishable to anyone navigating by a screen reader's links list, a feature most screen reader users rely on heavily to scan a page quickly. Pulled out of context and read on their own, ten identical "click here" links tell you nothing about where any of them actually go.

<!-- Bad -->
<a href="/pricing">Click here</a>

<!-- Good -->
<a href="/pricing">See pricing plans</a>

8. Missing lang attribute (WCAG 3.1.1, Language of Page)

Without it, screen readers guess the language, often wrong, which mangles pronunciation for the entire page and makes even correctly written content sound garbled. It is one line, set once in a template, and it is astonishing how often it is simply left out entirely.

<!-- Bad -->
<html>

<!-- Good -->
<html lang="en">

9. No skip link (WCAG 2.4.1, Bypass Blocks)

Keyboard users without a skip link have to tab through the entire header and navigation on every single page load just to reach the content, which on a site with a large mega menu can mean dozens of tab presses before anything useful happens. A skip link fixes this in one move, and it is fine for it to be visually hidden until it receives focus.

<a href="#main-content" class="skip-link">Skip to main content</a>
...
<main id="main-content">

10. Heading levels that skip around (WCAG 1.3.1, Info and Relationships, and 2.4.6, Headings and Labels)

Jumping from an <h2> straight to an <h4> because it "looked right" visually breaks the outline screen reader users rely on to navigate the page. Many screen readers let a user pull up a full list of headings and jump straight to the section they want, the same way a sighted user skims a table of contents. When the levels skip around, that outline stops making sense, even though nothing looks wrong on screen. Headings should step down one level at a time, in order, and any visual size adjustment should be handled with CSS, not by picking a different heading tag.

Ship the fixes, then check your work

None of these ten fixes require a redesign. Most live in a shared component, a base stylesheet, or a form template, which means fixing one instance often fixes dozens. Start with whatever touches the most pages: your button component, your form fields, your base CSS. Then work outward, page type by page type, until you have covered the ones that get the most traffic.

It also helps to build a habit of checking for these ten specifically, rather than waiting for a full audit to surface them. A quick keyboard-only pass through your site, tabbing from the top of the page to the bottom, will surface the focus indicator, skip link, and div-as-button failures in a couple of minutes. A contrast checker run against your CSS variables will surface the low-contrast text. Reading through your image and form markup will catch the rest.

If you want a fast read on which of these ten are actually present on your site right now, run it through our free accessibility tools and get a page-by-page breakdown before you start patching blind.

Got a tricky case that does not match any of the ten above, or want a second set of eyes on a fix before it ships? Email experts@wcag.world and talk to an actual person on our team, or head straight to our free tools to get started.