Free Tools · Quick Fixes · Life Hacks

The 12 Copy-Paste Fixes That Solve 90% of WCAG Failures

Illustration of a code editor window with twelve small numbered code-snippet tags stacked beside it, in an oxblood and cream editorial style
  • Free Tools
  • Quick Fixes
  • Life Hacks

Last time, we walked through the 12 failures that show up on almost every site we test, and exactly who each one locks out of the experience. A lot of you read it, nodded, and then asked the same question in different words: "okay, but what do I actually paste into the file?"

Fair. Diagnosis without a fix is just anxiety with extra steps.

So here is the companion piece. No stories about screen reader users hitting dead ends this time (we already told those). Just twelve real, working snippets, in the same order as the original list, ready to drop into whatever you are building right now. Keep this tab open next to your editor.

The Stat: WCAG SC 3.3.2 Labels or Instructions (Level A) requires a programmatic label/for association on form fields, not just visible text sitting nearby on the page. (Source: W3C)

The 12 copy-paste fixes A two-column numbered list of the twelve accessibility code snippets covered in this article, numbered 1 through 12 in oxblood circles. Column A Column B 1 label for/id
<circle cx="32" cy="100" r="14" fill="#7A2727" />
<text x="32" y="105" text-anchor="middle" fill="#FFFFFF" font-weight="700">2</text>
<text x="56" y="105">aria-label icon button</text>

<circle cx="32" cy="145" r="14" fill="#7A2727" />
<text x="32" y="150" text-anchor="middle" fill="#FFFFFF" font-weight="700">3</text>
<text x="56" y="150">focus-visible outline</text>

<circle cx="32" cy="190" r="14" fill="#7A2727" />
<text x="32" y="195" text-anchor="middle" fill="#FFFFFF" font-weight="700">4</text>
<text x="56" y="195">Escape key handler</text>

<circle cx="32" cy="235" r="14" fill="#7A2727" />
<text x="32" y="240" text-anchor="middle" fill="#FFFFFF" font-weight="700">5</text>
<text x="56" y="240">descriptive link text</text>

<circle cx="32" cy="280" r="14" fill="#7A2727" />
<text x="32" y="285" text-anchor="middle" fill="#FFFFFF" font-weight="700">6</text>
<text x="56" y="285">skip link</text>

<circle cx="352" cy="55" r="14" fill="#7A2727" />
<text x="352" y="60" text-anchor="middle" fill="#FFFFFF" font-weight="700">7</text>
<text x="376" y="60">alt text pattern</text>

<circle cx="352" cy="100" r="14" fill="#7A2727" />
<text x="352" y="105" text-anchor="middle" fill="#FFFFFF" font-weight="700">8</text>
<text x="376" y="105">role=status toast</text>

<circle cx="352" cy="145" r="14" fill="#7A2727" />
<text x="352" y="150" text-anchor="middle" fill="#FFFFFF" font-weight="700">9</text>
<text x="376" y="150">aria-describedby error</text>

<circle cx="352" cy="190" r="14" fill="#7A2727" />
<text x="352" y="195" text-anchor="middle" fill="#FFFFFF" font-weight="700">10</text>
<text x="376" y="195">autocomplete attribute</text>

<circle cx="352" cy="235" r="14" fill="#7A2727" />
<text x="352" y="240" text-anchor="middle" fill="#FFFFFF" font-weight="700">11</text>
<text x="376" y="240">contrast-safe color pair</text>

<circle cx="352" cy="280" r="14" fill="#7A2727" />
<text x="352" y="285" text-anchor="middle" fill="#FFFFFF" font-weight="700">12</text>
<text x="376" y="285">heading order fix</text>

If you haven't seen the reasoning behind this list yet, read the diagnostic version of this list first, if you haven't - it explains who gets locked out by each of these twelve gaps and why they keep recurring. This post assumes you already believe the problem is real and just want the code.

1. Label for/id

The single most common form failure. A <label> sitting visually next to an input means nothing to assistive technology unless it is programmatically tied to that input.

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

This is exactly what WCAG 3.3.2 Labels or Instructions requires: a real association, not just proximity on the page.

2. aria-label on icon-only buttons

A trash-can icon with no text means a screen reader announces "button" and nothing else.

<button aria-label="Delete item">
  <svg aria-hidden="true">...</svg>
</button>

3. Restoring focus-visible after an outline reset

Somewhere in your CSS history, someone wrote * { outline: none; } to kill the "ugly" focus ring, and nobody put anything back. Keyboard users have been navigating blind ever since.

:focus {
  outline: none;
}
:focus-visible {
  outline: 2px solid #7A2727;
  outline-offset: 2px;
}

4. Escape key handler for modals and menus

Any custom dropdown, modal, or popover needs an escape hatch that doesn't require a mouse.

element.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') {
    closeModal();
    triggerButton.focus();
  }
});

Note the last line: after closing, focus goes back to whatever opened the thing. Skipping that step is its own small failure.

5. Descriptive link text

"Click here" and "Learn more" are meaningless when a screen reader user pulls up a list of every link on the page, which many do routinely.

<!-- Instead of this -->
<a href="/pricing">Click here</a>

<!-- Do this -->
<a href="/pricing">See our pricing plans</a>

6. Skip link

A keyboard user forced to tab through fifteen nav items before reaching the content, on every single page, will not stick around.

<a class="skip-link" href="#main-content">Skip to main content</a>
...
<main id="main-content">
.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: 8px;
  top: 8px;
}

7. Alt text pattern

Two patterns cover most images: decorative and meaningful.

<!-- Decorative: hide it from assistive tech entirely -->
<img src="divider.svg" alt="">

<!-- Meaningful: describe the content, not the fact it's an image -->
<img src="q3-revenue-chart.png" alt="Q3 revenue up 18% over Q2">

8. role="status" for toasts

A confirmation toast that appears and fades in two seconds is invisible to a screen reader unless it is announced through a live region.

<div role="status" aria-live="polite">
  Changes saved successfully.
</div>

This satisfies WCAG 4.1.3 Status Messages, which exists specifically so transient messages like this get announced without stealing keyboard focus away from what the user was doing.

9. aria-describedby for inline errors

A red border on an invalid field tells a sighted user something is wrong. It tells a screen reader user nothing.

<input type="password" id="pw" aria-describedby="pw-error" aria-invalid="true">
<span id="pw-error">Password must be at least 8 characters</span>

10. autocomplete attribute on common fields

This one is almost free. Standard HTML autocomplete values satisfy WCAG SC 1.3.5 Identify Input Purpose (Level AA) and help every user, not just assistive tech users.

<input type="text" name="name" autocomplete="name">
<input type="email" name="email" autocomplete="email">
<input type="tel" name="phone" autocomplete="tel">

11. Contrast-safe color pair

Instead of guessing at hex codes, keep a small palette of pairs you already know pass, and reuse them.

:root {
  --text-on-cream: #241A17;   /* passes AA on #F5EEE4 */
  --link-on-white: #7A2727;   /* passes AA on #FFFFFF */
}

12. Heading order fix

Headings are a navigation structure, not a font-size shortcut. Skipping from <h2> to <h4> because the <h4> "looked right" breaks that structure for anyone navigating by heading.

<h1>Page title</h1>
  <h2>Section</h2>
    <h3>Subsection</h3>
  <h2>Next section</h2>

The full checklist

  • Every form input has a <label for> matching its id
  • Every icon-only button has an aria-label
  • :focus-visible outline restored after any outline: none reset
  • Escape key closes modals/menus and returns focus to the trigger
  • Every link reads sensibly out of context (no "click here")
  • A working skip link exists before the main nav
  • Every image has appropriate alt (empty for decorative, descriptive for meaningful)
  • Toasts and transient messages use role="status" or role="alert"
  • Inline form errors are tied to their field via aria-describedby
  • Common fields (name, email, phone, address) use standard autocomplete values
  • Text and background color pairs are pre-verified for contrast
  • Headings descend in order with no skipped levels

A quick summary table

# Fix Primary WCAG hook
1 label for/id 3.3.2 Labels or Instructions
3 focus-visible outline 2.4.7 Focus Visible
8 role=status toast 4.1.3 Status Messages
10 autocomplete attribute 1.3.5 Identify Input Purpose

None of these twelve require a redesign, a sprint, or a meeting about a meeting. Most are a single attribute or a few lines of CSS. That is the whole point of the list: the highest-leverage fixes are also the cheapest ones, and they are sitting in your codebase right now waiting to be pasted in.

If you want to know which of these twelve actually apply to your site before you start copying and pasting blind, check your own site against these with our free tools and get a concrete list instead of a guess.