Skip to main content

How to Fix Accessibility Issues

Every issue our tool can detect: what's wrong, why it matters, and how to fix it with code examples.

Media & Images

Missing Image Alt Text WCAG 1.1.1

Issue ID: media-img-no-alt

Screen readers announce images using their alt attribute. Without it, blind users hear the filename or nothing at all. Every meaningful image needs alt text that conveys its purpose. Decorative images should have an empty alt="" to be skipped.

โŒ Bad

<img src="team-photo.jpg">
<img src="icon-search.svg">

โœ… Good

<img src="team-photo.jpg"
     alt="Our team at the 2025 conference">

<!-- Decorative icon next to text -->
<img src="icon-search.svg" alt="" aria-hidden="true">

Suspicious Alt Text WCAG 1.1.1

Issue ID: media-img-suspicious-alt

Alt text like "image", "photo", "untitled", or the filename itself provides no useful information. Screen readers already announce the element as an image. The alt text should describe what is in the image, not that it is an image.

โŒ Bad

<img src="chart.png" alt="image">
<img src="hero.jpg" alt="photo.jpg">
<img src="logo.png" alt="logo">

โœ… Good

<img src="chart.png"
     alt="Q3 revenue chart showing 20% growth">
<img src="hero.jpg"
     alt="A woman using a screen reader on her laptop">
<img src="logo.png"
     alt="Acme Corp logo">

Alt Text Too Long WCAG 1.1.1

Issue ID: media-img-alt-too-long

Alt text over ~125 characters gets cut off or becomes tedious for screen reader users. For complex images like charts or infographics, use a short alt and provide a longer description elsewhere using aria-describedby or a visible caption.

โŒ Bad: alt is a paragraph

<img src="chart.png" alt="This chart shows the total
revenue for each quarter of 2025 including Q1 at 2.3
million, Q2 at 2.8 million, Q3 at 3.1 million...">

โœ… Good: short alt + long description

<img src="chart.png"
     alt="2025 quarterly revenue chart"
     aria-describedby="chart-desc">
<p id="chart-desc">Revenue grew each quarter:
  Q1 $2.3M, Q2 $2.8M, Q3 $3.1M, Q4 $3.6M.</p>

SVG Missing Accessible Name WCAG 1.1.1

Issue ID: media-svg-no-name

Inline SVGs are invisible to screen readers unless given an accessible name. Use a <title> element inside the SVG, or aria-label on the <svg> element. For decorative SVGs, use aria-hidden="true".

โŒ Bad

<svg viewBox="0 0 24 24">
  <path d="M12 2L2 22h20L12 2z"/>
</svg>

โœ… Good

<svg viewBox="0 0 24 24" role="img"
     aria-label="Warning triangle">
  <title>Warning triangle</title>
  <path d="M12 2L2 22h20L12 2z"/>
</svg>

<!-- Decorative SVG -->
<svg aria-hidden="true" viewBox="0 0 24 24">...</svg>

Video Missing Captions WCAG 1.2.2

Issue ID: media-video-no-captions

Videos without captions exclude deaf and hard-of-hearing users. Captions also help users in noisy environments, non-native speakers, and people who prefer reading. Provide a <track> element with a WebVTT file.

โŒ Bad

<video src="talk.mp4" controls></video>

โœ… Good

<video src="talk.mp4" controls>
  <track kind="captions" src="talk-en.vtt"
         srclang="en" label="English" default>
</video>

Video Missing Audio Description WCAG 1.2.5

Issue ID: media-video-no-description

Blind users cannot see visual-only content in videos (charts, actions, scene changes). An audio description track narrates important visual information during natural pauses in dialogue.

โŒ Bad

<video src="demo.mp4" controls>
  <track kind="captions" src="demo.vtt">
  <!-- No audio description track -->
</video>

โœ… Good

<video src="demo.mp4" controls>
  <track kind="captions" src="demo.vtt"
         srclang="en" label="English">
  <track kind="descriptions" src="demo-ad.vtt"
         srclang="en" label="Audio Description">
</video>

Video Autoplays WCAG 1.4.2

Issue ID: media-video-autoplay

Autoplaying video with audio interferes with screen readers and is disorienting for many users. If autoplay is needed, ensure the video is muted and provide an easy way to stop it.

โŒ Bad

<video src="promo.mp4" autoplay></video>

โœ… Good

<!-- If autoplay is required, mute it -->
<video src="promo.mp4" autoplay muted controls>
</video>

<!-- Better: don't autoplay at all -->
<video src="promo.mp4" controls></video>

Video Missing Controls WCAG 1.4.2

Issue ID: media-video-no-controls

Without controls, users cannot pause, stop, or adjust volume. This is especially critical for keyboard-only users who can't right-click for a context menu.

โŒ Bad

<video src="bg-video.mp4" autoplay loop></video>

โœ… Good

<video src="bg-video.mp4" controls></video>

Audio Autoplays WCAG 1.4.2

Issue ID: media-audio-autoplay

Autoplaying audio conflicts with screen readers and startles users. Audio should never autoplay. If it must, provide a mechanism to stop it within the first 3 seconds.

โŒ Bad

<audio src="music.mp3" autoplay></audio>

โœ… Good

<audio src="music.mp3" controls></audio>

Audio Missing Controls WCAG 1.4.2

Issue ID: media-audio-no-controls

Audio elements without visible controls cannot be paused or volume-adjusted. Always add the controls attribute.

โŒ Bad

<audio src="podcast.mp3"></audio>

โœ… Good

<audio src="podcast.mp3" controls></audio>

Audio Missing Transcript WCAG 1.2.1

Issue ID: media-audio-no-transcript

Deaf users and users who prefer reading need a text transcript of audio content. Provide a visible link to the transcript near the audio player.

โŒ Bad

<audio src="podcast.mp3" controls></audio>
<!-- No transcript provided -->

โœ… Good

<audio src="podcast.mp3" controls></audio>
<p><a href="podcast-transcript.html">
  Read the transcript
</a></p>

Canvas Missing Alternative WCAG 1.1.1

Issue ID: media-canvas-no-alternative

<canvas> elements render pixel-based content that is completely invisible to screen readers. Provide fallback content inside the canvas tag or describe the content with ARIA attributes.

โŒ Bad

<canvas id="chart"></canvas>

โœ… Good

<canvas id="chart" role="img"
  aria-label="Sales chart: Q1 $2M, Q2 $3M">
  <p>Sales chart showing Q1 at $2M and Q2 at $3M.</p>
</canvas>

Iframe Missing Title WCAG 2.4.1

Issue ID: media-iframe-no-title

Screen readers announce iframes by their title attribute. Without it, users hear "frame" with no context about what's embedded. Every iframe needs a descriptive title.

โŒ Bad

<iframe src="https://maps.google.com/..."></iframe>

โœ… Good

<iframe src="https://maps.google.com/..."
        title="Google Map showing our office location">
</iframe>

Embedded Video Missing Title WCAG 2.4.1

Issue ID: media-embedded-video-no-title

Embedded videos (YouTube, Vimeo, etc.) use iframes. Without a title, screen reader users cannot distinguish between multiple embedded videos or know what the video is about.

โŒ Bad

<iframe src="https://youtube.com/embed/abc"></iframe>

โœ… Good

<iframe src="https://youtube.com/embed/abc"
        title="Introduction to Web Accessibility">
</iframe>

Color Contrast

Contrast Fails WCAG AA WCAG 1.4.3

Issue ID: contrast-aa-fail

Text with insufficient contrast is hard to read for people with low vision or color blindness. WCAG AA requires 4.5:1 for normal text and 3:1 for large text (18px bold / 24px regular).

โŒ Bad: ratio 2.1:1

.subtitle {
  color: #aaaaaa;        /* light grey */
  background: #ffffff;   /* white */
  /* Contrast ratio: 2.1:1 - FAILS AA */
}

โœ… Good: ratio 4.6:1

.subtitle {
  color: #767676;        /* darker grey */
  background: #ffffff;   /* white */
  /* Contrast ratio: 4.6:1 - PASSES AA */
}

Contrast Fails WCAG AAA WCAG 1.4.6

Issue ID: contrast-aaa-fail

WCAG AAA requires enhanced contrast: 7:1 for normal text and 4.5:1 for large text. This is the highest standard and ideal for critical content.

โŒ Bad: ratio 4.6:1 (passes AA, fails AAA)

.text {
  color: #767676;
  background: #ffffff;
  /* Ratio 4.6:1 - fails AAA for normal text */
}

โœ… Good: ratio 8.6:1

.text {
  color: #404040;
  background: #ffffff;
  /* Ratio 8.6:1 - passes AAA */
}

Non-Text Contrast Failure WCAG 1.4.11

Issue ID: contrast-nontext-fail

UI components (buttons, inputs, icons) and graphical objects need at least 3:1 contrast against adjacent colors. A light grey border on a white input, for example, can be invisible to low-vision users.

โŒ Bad

input {
  border: 1px solid #e0e0e0;  /* barely visible */
  background: #ffffff;
}

โœ… Good

input {
  border: 2px solid #767676;  /* 4.5:1 ratio */
  background: #ffffff;
}

WAVE: Contrast Error WCAG 1.4.3

Issue ID: contrast_fail (WAVE)

The WAVE scanner detected text that does not meet WCAG AA contrast requirements. Use a contrast checking tool to find a color pair that achieves at least 4.5:1 for normal text or 3:1 for large text. See the fixes above for contrast-aa-fail.

Forms

Missing Form Label WCAG 1.3.1 WCAG 3.3.2

Issue ID: form-missing-label

Screen readers cannot tell users what a form field is for without a <label>. Placeholder text alone is not accessible. It disappears on typing and has poor contrast.

โŒ Bad

<input type="email" placeholder="Email">

<label>Password</label>
<input type="password"> <!-- not linked -->

โœ… Good

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

<!-- Or wrap input inside label -->
<label>
  Password
  <input type="password">
</label>

WAVE: Missing Form Label WCAG 1.3.1

Issue ID: label_missing (WAVE)

Same issue as form-missing-label. The WAVE scanner detected a form control without a programmatic label. See the fix above.

Placeholder Used as Label WCAG 1.3.1 WCAG 3.3.2

Issue ID: form-placeholder-only

Placeholders disappear when typing begins, leaving users unsure what the field expects. They also have insufficient contrast by default. Always use a visible <label>.

โŒ Bad

<input type="text" placeholder="Your name">

โœ… Good

<label for="name">Your name</label>
<input type="text" id="name"
       placeholder="e.g. Jane Smith">

Radio/Checkbox Group Missing Fieldset WCAG 1.3.1

Issue ID: form-radio-no-group

Related radio buttons or checkboxes need to be grouped in a <fieldset> with a <legend> so screen readers announce the group's purpose.

โŒ Bad

<label><input type="radio" name="size"> Small</label>
<label><input type="radio" name="size"> Large</label>

โœ… Good

<fieldset>
  <legend>Choose a size</legend>
  <label><input type="radio" name="size"> Small</label>
  <label><input type="radio" name="size"> Large</label>
</fieldset>

Fieldset Missing Legend WCAG 1.3.1

Issue ID: form-fieldset-no-legend

A <fieldset> without a <legend> provides grouping but no label. Screen readers announce the legend when entering the group. Without it, the grouping is meaningless.

โŒ Bad

<fieldset>
  <label><input type="checkbox"> Agree</label>
</fieldset>

โœ… Good

<fieldset>
  <legend>Terms and Conditions</legend>
  <label><input type="checkbox"> I agree</label>
</fieldset>

Missing Autocomplete Attribute WCAG 1.3.5

Issue ID: form-missing-autocomplete

The autocomplete attribute helps users fill forms faster and allows assistive technology to identify the purpose of each field. It's especially important for people with cognitive or motor disabilities.

โŒ Bad

<input type="text" name="fname">
<input type="email" name="email">

โœ… Good

<input type="text" name="fname"
       autocomplete="given-name">
<input type="email" name="email"
       autocomplete="email">

Required Field Not Announced WCAG 3.3.1 WCAG 3.3.2

Issue ID: form-required-no-aria

If a field is required but not marked with required or aria-required="true", screen reader users won't know it's mandatory until they submit the form and get an error.

โŒ Bad

<label>Email *</label>
<input type="email">
<!-- Visual asterisk, but not announced -->

โœ… Good

<label for="email">Email <span aria-hidden="true">*</span></label>
<input type="email" id="email" required
       aria-required="true">

Form Missing Submit Button WCAG 3.3.1

Issue ID: form-no-submit

A form without a submit button forces users to rely on pressing Enter, which not all assistive technologies support. Provide a visible submit button.

โŒ Bad

<form>
  <input type="text">
  <!-- No submit button -->
</form>

โœ… Good

<form>
  <input type="text">
  <button type="submit">Submit</button>
</form>

Headings

Empty Heading WCAG 1.3.1 WCAG 2.4.6

Issue ID: heading-empty

An empty heading tag creates a confusing "blank" entry when screen reader users navigate by headings. Either add text content or remove the empty heading.

โŒ Bad

<h2></h2>
<h3>  </h3> <!-- whitespace only -->

โœ… Good

<h2>Our Services</h2>
<!-- Or remove the empty heading entirely -->

Skipped Heading Level WCAG 1.3.1

Issue ID: heading-skipped-level

Skipping heading levels (e.g. h1 โ†’ h3) breaks the document outline that screen reader users rely on for navigation. Headings should descend logically.

โŒ Bad

<h1>Services</h1>
<h3>Web Design</h3>  <!-- skipped h2 -->

โœ… Good

<h1>Services</h1>
  <h2>Web Design</h2>
    <h3>Responsive Design</h3>

WAVE: Heading Level Skipped WCAG 1.3.1

Issue ID: heading_skipped (WAVE)

Same issue as heading-skipped-level. See the fix above.

Heading Too Long WCAG 2.4.6

Issue ID: heading-too-long

Headings should be concise labels, not full paragraphs. Long headings are hard to scan and unwieldy in screen reader heading lists. Keep headings under ~80 characters.

โŒ Bad

<h2>Here is a very long heading that describes
everything about our web design services including
responsive design, mobile-first approach...</h2>

โœ… Good

<h2>Web Design Services</h2>
<p>We offer responsive design, mobile-first...</p>

First Heading Is Not h1 WCAG 1.3.1

Issue ID: heading-first-not-h1

The first heading on the page should be an <h1> to establish the document's main topic. Starting with h2 or lower confuses the heading hierarchy.

โŒ Bad

<h2>Welcome to Our Site</h2>

โœ… Good

<h1>Welcome to Our Site</h1>

Page Missing h1 WCAG 1.3.1 WCAG 2.4.6

Issue ID: heading-no-h1

Every page should have exactly one <h1> that describes the page's main content. Screen readers use it as a landmark.

โŒ Bad: no h1 at all

<h2>About Us</h2>
<h2>Contact</h2>

โœ… Good

<h1>About Our Company</h1>
  <h2>About Us</h2>
  <h2>Contact</h2>

WAVE: Missing h1 WCAG 1.3.1

Issue ID: h1_missing (WAVE)

Same issue as heading-no-h1. See fix above.

Multiple h1 Headings WCAG 1.3.1

Issue ID: heading-multiple-h1

Multiple <h1> elements make it unclear which is the page's main topic. Use one <h1> per page and <h2> for sections.

โŒ Bad

<h1>My Site</h1>
<h1>Welcome</h1>  <!-- two h1s -->

โœ… Good

<h1>My Site | Welcome</h1>
  <h2>Recent News</h2>

WAVE: Multiple h1 WCAG 1.3.1

Issue ID: h1_multiple (WAVE)

Same issue as heading-multiple-h1. See fix above.

Page Has No Headings WCAG 1.3.1

Issue ID: heading-none

Pages without any headings force screen reader users to read the entire page linearly. Add heading structure to identify major sections.

โŒ Bad

<div class="hero">Welcome</div>
<div class="section">About us</div>

โœ… Good

<h1>Welcome</h1>
<section>
  <h2>About Us</h2>
  <p>...</p>
</section>

Keyboard Accessibility

Interactive Element Not Focusable WCAG 2.1.1

Issue ID: keyboard-not-focusable

If an interactive element can't be reached with the Tab key, keyboard-only users are locked out. Use native HTML elements (<button>, <a>) or add tabindex="0".

โŒ Bad

<div class="btn" onclick="save()">Save</div>

โœ… Good

<button onclick="save()">Save</button>

<!-- If you must use a div: -->
<div class="btn" role="button" tabindex="0"
     onclick="save()"
     onkeydown="if(event.key==='Enter')save()">Save</div>

Positive Tabindex WCAG 2.4.3

Issue ID: keyboard-positive-tabindex

Using tabindex greater than 0 forces a custom tab order that overrides the natural document flow and is nearly impossible to maintain. Use tabindex="0" or -1 only.

โŒ Bad

<input tabindex="3">
<input tabindex="1">
<input tabindex="2">

โœ… Good

<!-- Let DOM order determine tab order -->
<input>
<input>
<input>

Click Handler Without Keyboard Support WCAG 2.1.1

Issue ID: keyboard-click-only

Elements with mouse click handlers but no keyboard event handlers cannot be activated by keyboard users. Native buttons and links handle this automatically.

โŒ Bad

<span onclick="toggle()">Toggle</span>

โœ… Good

<button onclick="toggle()">Toggle</button>

No Visible Focus Indicator WCAG 2.4.7

Issue ID: keyboard-no-focus-indicator

Keyboard users need to see which element is currently focused. Removing or hiding the focus outline makes keyboard navigation impossible. Never use outline: none without a replacement.

โŒ Bad

button:focus { outline: none; }

โœ… Good

button:focus-visible {
  outline: 3px solid #4A90D9;
  outline-offset: 2px;
}

Potential Focus Trap WCAG 2.1.2

Issue ID: keyboard-potential-focus-trap

A focus trap prevents keyboard users from tabbing out of a component. This is only acceptable for modals (which should trap focus intentionally and release it when closed). Unintentional traps lock users in.

โŒ Bad: accidental trap

<div tabindex="0" onkeydown="event.preventDefault()">
  <input> <!-- User cannot Tab out -->
</div>

โœ… Good: intentional modal trap with Escape

<div role="dialog" aria-modal="true">
  <button onclick="closeModal()">Close</button>
  <input>
</div>
<script>
  // Trap focus inside, release on Escape
  dialog.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') closeModal();
  });
</script>

Scrollable Region Not Focusable WCAG 2.1.1

Issue ID: keyboard-scrollable-not-focusable

Scrollable containers that aren't focusable can't be scrolled by keyboard-only users. Add tabindex="0" and a role to make them accessible.

โŒ Bad

<div style="overflow:auto; height:200px">
  <!-- Long content... -->
</div>

โœ… Good

<div style="overflow:auto; height:200px"
     tabindex="0" role="region"
     aria-label="Scrollable code example">
  <!-- Long content... -->
</div>

Language & Structure

Missing Document Language WCAG 3.1.1

Issue ID: language_missing

The lang attribute on <html> tells screen readers which language to use for pronunciation. Without it, a screen reader may use the wrong voice.

โŒ Bad

<html>
  <head>...</head>
</html>

โœ… Good

<html lang="en">
  <head>...</head>
</html>

Missing Page Title WCAG 2.4.2

Issue ID: title_missing

The <title> element is the first thing screen readers announce. Without it, users hear the URL or nothing, making it impossible to identify the page.

โŒ Bad

<head>
  <meta charset="UTF-8">
  <!-- No title -->
</head>

โœ… Good

<head>
  <meta charset="UTF-8">
  <title>Contact Us | Acme Corp</title>
</head>

No ARIA Landmarks WCAG 1.3.1

Issue ID: no_landmarks (WAVE)

Landmarks (<nav>, <main>, <header>, <footer>) let screen reader users jump between page regions. Without them, users must read linearly.

โŒ Bad

<div class="navigation">...</div>
<div class="content">...</div>
<div class="footer">...</div>

โœ… Good

<nav aria-label="Main">...</nav>
<main>...</main>
<footer>...</footer>

WAVE: Text Too Small

Issue ID: text_small (WAVE)

Very small text (below ~10px) is difficult to read for most users and impossible for those with low vision. Use relative units like rem and ensure body text is at least 16px.

โŒ Bad

.fine-print { font-size: 8px; }

โœ… Good

.fine-print { font-size: 0.875rem; } /* 14px */

WAVE: Justified Text

Issue ID: text_justified (WAVE)

Justified text creates uneven word spacing ("rivers of white") that makes reading harder for people with dyslexia or cognitive disabilities. Use left-aligned text.

โŒ Bad

p { text-align: justify; }

โœ… Good

p { text-align: left; }

ARIA & Buttons

Empty Button WCAG 4.1.2

Issue ID: button_empty

Buttons without accessible text are announced as just "button". Always add text content, aria-label, or aria-labelledby.

โŒ Bad

<button>
  <svg>...menu icon...</svg>
</button>

โœ… Good

<button aria-label="Open menu">
  <svg aria-hidden="true">...</svg>
</button>

WAVE: Image Missing Alt WCAG 1.1.1

Issue ID: img_alt_missing (WAVE)

Same issue as media-img-no-alt. See the fix in the Media & Images section.

WAVE: Image Has Empty Alt

Issue ID: img_alt_empty (WAVE)

An image with alt="" is treated as decorative and hidden from screen readers. This is correct for decorative images but wrong for meaningful ones. Verify the image is truly decorative; if not, add descriptive alt text.

WAVE: Suspicious Alt Text WCAG 1.1.1

Issue ID: img_alt_suspicious (WAVE)

Same issue as media-img-suspicious-alt. Alt text like "image" or the filename is not useful.

ARIA-Hidden Element Is Focusable WCAG 4.1.2

Issue ID: aria_hidden_focusable

An element with aria-hidden="true" that is still focusable creates a confusing experience. The screen reader says nothing but the element receives focus. Remove aria-hidden or add tabindex="-1".

โŒ Bad

<button aria-hidden="true">Close</button>

โœ… Good

<!-- Either remove aria-hidden -->
<button>Close</button>

<!-- Or make it truly hidden -->
<button aria-hidden="true" tabindex="-1">Close</button>