Skip to main content

How to Fix Common Accessibility Issues (With Code Examples)

Last updated: March 30, 2026

TLDR

The most common accessibility issues have straightforward fixes. Add alt attributes to images, associate labels with form inputs, ensure 4.5:1 color contrast, make all interactive elements keyboard-accessible, use proper heading hierarchy, add ARIA attributes to custom widgets, and manage focus when content changes dynamically.

DEFINITION

Alt Text
The text value of an image's alt attribute in HTML. Screen readers read alt text aloud so users who cannot see the image understand what it conveys. Decorative images use an empty alt attribute (alt='') to tell screen readers to skip them.

DEFINITION

ARIA Labels
HTML attributes from the Accessible Rich Internet Applications specification that provide accessible names and descriptions to elements. aria-label provides an invisible text label, aria-labelledby references another element's text as the label, and aria-describedby adds supplementary description.

DEFINITION

Focus Trap
A situation where keyboard focus enters a component (like a modal dialog) and cannot leave using the Tab key. Focus traps violate WCAG 2.1.2 and make the page unusable for keyboard-only users. Modal dialogs should trap focus intentionally but also provide an Escape key to close.

DEFINITION

Skip Navigation
A link placed at the top of the page (usually visually hidden until focused) that lets keyboard users skip past the header and navigation to reach the main content directly. Without skip navigation, keyboard users must Tab through every navigation link on every page.

Fixing the Issues That Matter Most

Accessibility violations cluster around the same problems across millions of websites. You do not need to memorize all 78 WCAG success criteria to make meaningful progress. Fix the seven issues covered in this guide and you address the overwhelming majority of automated scan findings and the highest-risk items in ADA enforcement.

Fix 1: Missing Alt Text on Images

Missing alt text is the most common accessibility violation on the web. Every meaningful image needs an alt attribute that describes what the image conveys.

<!-- Bad: no alt attribute -->
<img src="team-photo.jpg">

<!-- Bad: meaningless alt text -->
<img src="team-photo.jpg" alt="image">

<!-- Good: descriptive alt text -->
<img src="team-photo.jpg" alt="A11yProof engineering team in the office">

<!-- Decorative image: empty alt tells screen readers to skip it -->
<img src="decorative-divider.svg" alt="">

For functional images — icons inside buttons, linked logos — describe the function, not the image: alt="Search" for a magnifying glass icon, alt="A11yProof home" for a linked logo.

Fix 2: Low Color Contrast

WCAG requires a 4.5:1 contrast ratio for normal text and 3:1 for large text (18pt+). Common failures: light gray text on white backgrounds, white text on light-colored headers, placeholder text that barely differs from the background.

/* Bad: 2.5:1 contrast ratio */
.subtitle { color: #999999; background: #ffffff; }

/* Good: 7:1 contrast ratio */
.subtitle { color: #545454; background: #ffffff; }

Use a contrast checker (built into Chrome DevTools or standalone tools like WebAIM’s contrast checker) to verify ratios before deploying.

Fix 3: Missing Form Labels

Every form input needs a programmatically associated label. Placeholder text disappears when users start typing and is not recognized as a label by all screen readers.

<!-- Bad: placeholder only, no label -->
<input type="email" placeholder="Enter your email">

<!-- Good: visible label with for/id association -->
<label for="signup-email">Email address</label>
<input type="email" id="signup-email" placeholder="you@example.com">

<!-- Alternative: wrapping label -->
<label>
  Email address
  <input type="email" placeholder="you@example.com">
</label>

If your design hides labels visually, use a CSS class that hides the label from sight but keeps it available to screen readers — never use display: none or visibility: hidden on labels, as those hide them from assistive technologies too.

Fix 4: Keyboard Navigation

Every interactive element must be reachable and usable with only a keyboard. The most common failures: click handlers on <div> or <span> elements instead of <button> or <a> elements, and custom components that swallow keyboard events.

<!-- Bad: div with click handler, not keyboard accessible -->
<div onclick="submitForm()">Submit</div>

<!-- Good: button element, keyboard accessible by default -->
<button type="submit">Submit</button>

If you absolutely must use a non-semantic element, add keyboard support explicitly:

<div role="button" tabindex="0"
     onclick="submitForm()"
     onkeydown="if(event.key==='Enter'||event.key===' ')submitForm()">
  Submit
</div>

But the better fix is almost always to use the right HTML element in the first place.

Fix 5: Heading Structure

Screen reader users navigate pages by jumping between headings. A proper heading hierarchy tells them the document structure without requiring them to read every word.

<!-- Bad: skipped heading levels, headings used for styling -->
<h1>Our Product</h1>
<h4>Features</h4>  <!-- skipped h2 and h3 -->
<h2>Pricing</h2>

<!-- Good: logical heading hierarchy -->
<h1>Our Product</h1>
<h2>Features</h2>
<h3>AI Scanning</h3>
<h3>Code Fixes</h3>
<h2>Pricing</h2>

Use CSS for visual styling, not heading levels. If you want smaller text that is not a heading, use a <p> with a CSS class.

Fix 6: ARIA Attributes for Custom Widgets

Native HTML elements have built-in accessibility — a <button> announces itself as “button” to screen readers without any extra work. Custom widgets built from <div> elements have no inherent semantics. ARIA fills that gap.

<!-- Custom tab interface with ARIA -->
<div role="tablist" aria-label="Product information">
  <button role="tab" aria-selected="true" aria-controls="panel-features">
    Features
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel-pricing">
    Pricing
  </button>
</div>
<div role="tabpanel" id="panel-features">...</div>
<div role="tabpanel" id="panel-pricing" hidden>...</div>

Important: ARIA does not add behavior, only semantics. You still need to implement keyboard navigation (arrow keys between tabs, Tab into the panel) with JavaScript.

Fix 7: Focus Management

When content changes dynamically — a modal opens, a form submits, a notification appears — keyboard focus needs to be managed explicitly.

// When opening a modal, move focus to the first focusable element
function openModal() {
  const modal = document.getElementById('modal');
  modal.hidden = false;
  modal.querySelector('button, [href], input').focus();
}

// When closing, return focus to the trigger element
function closeModal(triggerElement) {
  document.getElementById('modal').hidden = true;
  triggerElement.focus();
}

For status messages that should not steal focus, use ARIA live regions:

<div aria-live="polite" aria-atomic="true">
  <!-- JavaScript updates this text when status changes -->
  Item added to cart
</div>

Automate What You Can

Manual fixes are essential, but you should not rely on remembering to check every page after every deployment. Tools like A11yProof automate scanning, generate specific code fixes for detected issues, and run scheduled rescans to catch regressions. Integrate accessibility testing into your build process and catch issues before they reach production.

Q&A

What are the most common accessibility issues found in automated scans?

The WebAIM Million annual analysis consistently finds the same issues: missing image alt text (SC 1.1.1), low color contrast (SC 1.4.3), empty links (SC 2.4.4), missing form labels (SC 1.3.1), missing document language (SC 3.1.1), and empty buttons (SC 4.1.2). These six issues account for the majority of automatically detectable WCAG violations. Fixing them addresses the bulk of your automated scan findings.

Q&A

Do I need to learn ARIA to fix accessibility issues?

For most fixes, no. Using proper semantic HTML — button elements for buttons, label elements for form labels, heading elements for headings — handles the majority of accessibility requirements without any ARIA. ARIA is needed only for custom interactive widgets (tabs, accordions, tree views, modals) that have no native HTML equivalent. The first rule of ARIA is: do not use ARIA if you can use a native HTML element instead.

Q&A

How do I test that my fixes actually work?

After applying fixes, run an automated scan to confirm the violations are resolved. Then test manually: Tab through the page to verify keyboard access, run a screen reader to verify labels and announcements, zoom to 200% to verify reflow. A11yProof's rescan feature lets you verify fixes immediately after deployment.

Like what you're reading?

Try A11yProof free — start scanning your site today.

Want to learn more?

How do I fix accessibility issues without breaking my design?
Most accessibility fixes are invisible to sighted users. Adding alt text, form labels, ARIA attributes, and keyboard handlers does not change visual appearance. Color contrast adjustments may require minor design tweaks, but tools like A11yProof suggest specific color values that meet ratios while staying close to your brand palette.
Should I fix all accessibility issues at once or prioritize?
Prioritize by legal risk and user impact. Fix keyboard traps, missing form labels, and inaccessible navigation first — these block users entirely and are commonly cited in ADA lawsuits. Then address missing alt text and contrast issues. Minor issues like redundant ARIA or heading level skips are lower priority.
Can A11yProof generate the actual code fixes for me?
Yes. A11yProof Pro and Agency plans include AI-generated code fixes. After scanning your site, it produces the specific HTML, CSS, or ARIA changes you need to apply to your source code. You review each fix, apply it to your codebase, and rescan to verify.
What is the difference between aria-label and aria-labelledby?
aria-label provides an inline text label that is read by screen readers but not visible on screen. aria-labelledby references the id of another element whose text serves as the label. Use aria-labelledby when a visible label exists elsewhere on the page; use aria-label when no visible label exists.
How do I make a modal dialog accessible?
Add role='dialog' and aria-modal='true' to the container. Set aria-labelledby to reference the modal's heading. When the modal opens, move focus to the first focusable element inside. Trap Tab within the modal. When the modal closes on Escape or button click, return focus to the element that opened it.

Keep reading