Skip to content
Accessible Images & Media: Alt Text, Captions & More

Accessible Images & Media: Alt Text, Captions & More

DodaTech Updated Jun 20, 2026 11 min read

Images convey information visually — but for blind and low-vision users, that information must be provided as text alternatives. Accessible media extends to video captions, audio transcripts, and animations.

What You’ll Learn

By the end of this tutorial, you’ll understand alt text for different image types (decorative, informative, functional, complex), using <figure> and <figcaption>, longdesc for complex images, WebVTT captions for video, audio transcripts, accessible animated GIFs, and lazy loading with accessibility in mind.

Why Accessible Images Matter

Every image on your site is either informative (needs alt text), decorative (needs empty alt), or functional (needs action-oriented alt). Getting this wrong means screen reader users either miss critical information or are bombarded with irrelevant noise. At DodaTech, Doda Browser’s image inspector shows exactly how each image is exposed to assistive technologies, and built-in tools help verify alt text quality.

Accessible Images Learning Path

    flowchart LR
  A[Accessibility Overview] --> B[WCAG Compliance]
  B --> C[Color Contrast]
  C --> D[Accessible Forms]
  D --> E[Accessible Images]
  E --> F[Accessible Navigation]
  E:::current

  classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px
  
Prerequisites: Basic HTML and HTML5 knowledge. Understanding of WCAG and screen readers from previous tutorials.

Alt Text for Different Image Types

Decorative Images

Decorative images add visual flair but don’t convey information. They should have alt="" (empty) so screen readers skip them:

<!-- Decorative border image -->
<img src="decorative-border.svg" alt="" role="presentation">

<!-- Background-like spacer image -->
<img src="spacer.png" alt="" aria-hidden="true">

<!-- Icon that repeats adjacent visible text -->
<span>
  <img src="phone-icon.svg" alt="" aria-hidden="true">
  Call us: 1-800-555-0199
</span>

<!-- ❌ Bad — redundant alt text -->
<img src="star.png" alt="star">
<!-- Screen reader: "star, image" — wastes user's time -->

When to use alt="" vs aria-hidden="true"? Both work for hiding images. alt="" is simpler for <img> elements. aria-hidden="true" is useful for icon fonts or inline SVGs.

Informative Images

Informative images convey information. Their alt text should describe the content, not just label it:

<!-- ✅ Good — describes the content -->
<img src="team-photo.jpg"
     alt="DodaTech engineering team of 8 people gathered around a conference table">

<!-- ❌ Bad — too vague -->
<img src="team-photo.jpg" alt="Team photo">

<!-- ✅ Good — chart with data -->
<img src="revenue-chart.png"
     alt="Bar chart: Q1 2026 revenue $100K, Q2 $145K, Q3 projected $180K">

<!-- ✅ Good — screenshot showing a specific feature -->
<img src="dashboard-screenshot.png"
     alt="Doda Browser DevTools accessibility panel showing contrast ratio checker with AA and AAA pass/fail indicators">

Functional Images

When an image is a link or button, the alt text must describe the action:

<!-- Image link — alt describes the destination -->
<a href="/download-report">
  <img src="download-icon.svg"
       alt="Download Q2 2026 revenue report (PDF, 2.4 MB)">
</a>

<!-- Image button — alt describes the action -->
<button onclick="print()">
  <img src="print-icon.svg" alt="Print this page">
</button>

<!-- ❌ Bad — describes appearance, not function -->
<a href="/download-report">
  <img src="download-icon.svg" alt="Download icon">
  <!-- Screen reader: "Download icon, link" — users don't know what happens -->
</a>

Complex Images

Complex images (charts, maps, diagrams) need more than a short alt text. Provide a detailed description:

<!-- Method 1: Adjacent text description -->
<figure>
  <img src="org-chart.png" alt="DodaTech organizational chart">
  <figcaption>
    <h3>Organizational Structure</h3>
    <ul>
      <li>CEO: Alex Chen</li>
      <li>Reports to CEO:
        <ul>
          <li>CTO: Sarah Johnson (Engineering, QA, DevOps)</li>
          <li>CPO: Michael Park (Product, Design, Research)</li>
          <li>CFO: Lisa Wong (Finance, Legal, HR)</li>
        </ul>
      </li>
    </ul>
  </figcaption>
</figure>

<!-- Method 2: longdesc attribute (links to full description) -->
<img src="state-population-map.png"
     alt="Population density map of California"
     longdesc="https://example.com/map-description.html">

<!-- Method 3: ARIA-describedby for on-page description -->
<img src="sales-chart.png"
     alt="Line chart showing 40% growth in Q2 2026"
     aria-describedby="chart-desc">
<div id="chart-desc" hidden>
  <p>Monthly sales figures for 2026: January $80K, February $85K,
     March $95K, April $100K, May $120K, June $145K.</p>
</div>

Figure and Figcaption

The <figure> and <figcaption> elements group an image with its caption, which is useful for both sighted and screen reader users:

<figure>
  <img src="architecture-diagram.png"
       alt="DodaTech platform architecture showing frontend, API gateway, microservices, and database layers">
  <figcaption>
    Figure 1: DodaTech platform architecture. The frontend communicates
    through an API gateway to backend microservices and databases.
  </figcaption>
</figure>

Screen reader behavior: Some screen readers announce “Figure, [alt text], [figcaption text], end of figure.” Others announce “Group, [alt text], [figcaption text].”

Video Captions (WebVTT)

All video content must have captions for deaf and hard-of-hearing users:

# captions.vtt
WEBVTT

00:00:01.000 --> 00:00:05.000
Welcome to the DodaTech accessibility tutorial series.

00:00:05.500 --> 00:00:10.000
Today we're learning about accessible images and media.

00:00:10.500 --> 00:00:15.000
Over 285 million people worldwide have visual impairments.

00:00:15.500 --> 00:00:20.000
Alt text is the primary way they understand images.
<!-- HTML5 video with WebVTT captions -->
<video controls preload="metadata"
       poster="tutorial-thumbnail.jpg">
  <source src="tutorial-accessible-images.mp4" type="video/mp4">
  <source src="tutorial-accessible-images.webm" type="video/webm">
  <track kind="captions" src="captions.vtt"
         srclang="en" label="English captions" default>
  <track kind="subtitles" src="subtitles-es.vtt"
         srclang="es" label="Spanish subtitles">
  <p>
    Your browser doesn't support HTML5 video.
    <a href="tutorial-accessible-images.mp4">Download the video</a>
  </p>
</video>

<!-- Audio description track for blind users -->
<track kind="descriptions" src="descriptions.vtt"
       srclang="en" label="English audio descriptions">

Audio descriptions are separate tracks that describe visual content during natural pauses in the video. For example: “[The instructor points to a chart showing 40% revenue growth].”

Audio Transcripts

Podcasts, audio recordings, and voice messages need text transcripts:

<audio controls>
  <source src="podcast-episode-42.mp3" type="audio/mp3">
  <p>
    Your browser doesn't support audio.
    <a href="podcast-episode-42.mp3">Download the episode (32 MB)</a>
  </p>
</audio>

<section aria-label="Episode transcript">
  <h2>Transcript</h2>
  <p>
    <strong>Host:</strong> Welcome to the DodaTech Accessibility
    Podcast. Today we're talking about WCAG 2.2 compliance...
  </p>
  <p>
    <strong>Guest:</strong> Thanks for having me. Let's start with
    the new Focus Appearance criterion...
  </p>
  <p>
    <em>[Episode continues — full transcript follows]</em>
  </p>
</section>

Animated GIFs and Motion

Animations can cause accessibility issues — both for users with vestibular disorders (motion sensitivity) and cognitive disabilities (distraction):

<!-- ❌ Bad — auto-playing GIF with no controls -->
<img src="spinning-loader.gif" alt="Loading...">

<!-- ✅ Good — static image with animation as enhancement -->
<img src="diagram.png"
     alt="Flowchart showing user registration process"
     class="animated-diagram"
     data-animated-src="diagram-animated.gif">

<script>
// Provide a play/pause button for animations
const img = document.querySelector('.animated-diagram');
document.getElementById('toggle-animation').addEventListener('click', () => {
  const isAnimated = img.src.includes('-animated');
  img.src = isAnimated
    ? img.dataset.animatedSrc.replace('-animated', '')
    : img.dataset.animatedSrc;
});
</script>

Respecting User’s Motion Preferences

/* Respect user's "reduce motion" setting */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Apply to GIFs as well */
@media (prefers-reduced-motion: reduce) {
  .animated-gif {
    display: none;
  }
  .static-fallback {
    display: block;
  }
}

Lazy Loading Accessibility

Lazy loading images (loading images only when they’re near the viewport) must not interfere with screen readers:

<!-- Native lazy loading (supported in modern browsers) -->
<img src="hero.jpg" alt="DodaTech product showcase" loading="lazy">

<!-- JavaScript lazy loading with proper accessibility -->
<img data-src="large-chart.png" alt="Revenue chart Q2 2026"
     class="lazy" width="800" height="600">

<script>
// Lazy loading that respects screen readers
document.addEventListener('DOMContentLoaded', () => {
  const lazyImages = document.querySelectorAll('img.lazy');

  if ('IntersectionObserver' in window) {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;
          img.removeAttribute('data-src');
          img.classList.remove('lazy');
          observer.unobserve(img);
        }
      });
    });

    lazyImages.forEach(img => observer.observe(img));
  } else {
    // Fallback: load all images immediately
    lazyImages.forEach(img => {
      img.src = img.dataset.src;
    });
  }
});
</script>

Important: Never lazy-load the image used as aria-describedby target or any image referenced as critical content. Screen readers may not trigger IntersectionObserver.

Image Accessibility Testing

// alt-text-audit.js — audit all images on a page
function auditAllImages() {
  const issues = [];
  const images = document.querySelectorAll('img');

  images.forEach((img, index) => {
    const alt = img.getAttribute('alt');
    const src = img.getAttribute('src');
    const role = img.getAttribute('role');

    // Check 1: Missing alt attribute entirely
    if (!img.hasAttribute('alt')) {
      issues.push({
        severity: 'critical',
        img: src || `#${index}`,
        issue: 'Missing alt attribute — screen readers may read file name',
        fix: 'Add alt="description" for informative images or alt="" for decorative',
        wcag: '1.1.1 Non-text Content (A)'
      });
      return;
    }

    // Check 2: Alt text is just the file name
    if (alt && src && alt === src) {
      issues.push({
        severity: 'major',
        img: src,
        issue: 'Alt text matches file name',
        fix: 'Replace with a meaningful description or empty alt',
        wcag: '1.1.1 Non-text Content (A)'
      });
    }

    // Check 3: Alt text contains "image of" or "picture of"
    if (alt && /^(image|picture|photo|graphic)\s+(of|showing)/i.test(alt)) {
      issues.push({
        severity: 'minor',
        img: src,
        issue: 'Redundant "image of" in alt text',
        fix: `Remove "image of" — screen readers already announce this as an image`,
        wcag: '1.1.1 Non-text Content (A)'
      });
    }

    // Check 4: Image in a link with generic alt
    if (img.closest('a') && alt && /^(click here|read more|learn more|go|link)$/i.test(alt)) {
      issues.push({
        severity: 'critical',
        img: src,
        issue: 'Generic link alt text',
        fix: 'Describe the destination/action, not the image',
        wcag: '2.4.4 Link Purpose (A)'
      });
    }
  });

  console.table(issues);
  return issues;
}

auditAllImages();

Expected output: Console table showing all images on the page with any critical, major, or minor alt text issues found.

Common Images & Media Mistakes

1. Missing Alt Attribute on Informative Images

Without alt, screen readers announce the file name: “chart-q2-dot-png”. Users have no idea what the image shows.

2. Empty Alt on Informative Images

alt="" on a chart or diagram tells screen readers to skip it entirely, hiding critical information.

3. Alt Text on Decorative Images

alt="border" or alt="spacer" forces screen readers to announce meaningless content. Use alt="".

4. Text Inside Images

Images of text fail when text needs to be translated, resized, or read by screen readers. Use real text with CSS styling instead.

5. Videos Without Captions

A video without captions is completely inaccessible to deaf users. Always provide synchronized captions via <track kind="captions">.

6. Audio Without Transcript

A podcast without a transcript excludes deaf users and users who can’t play audio. Always provide a text transcript.

7. Auto-Playing Animations

Auto-playing GIFs or videos can’t be paused by screen reader users and may trigger vestibular disorders. Add play/pause controls or use static fallbacks with prefers-reduced-motion.

Practice Questions

1. When should you use empty alt text (alt="")?

For decorative images that don’t convey information — borders, spacers, icons that repeat adjacent text. Screen readers skip these.

2. What’s the difference between alt text for a decorative image vs an informative image?

Decorative: alt="" (hide entirely). Informative: describe the content or data the image shows.

3. What is WebVTT and how is it used?

WebVTT (Web Video Text Tracks) is a format for captions and subtitles. It’s used with the <track> element inside <video> to provide timed text tracks.

4. What is the prefers-reduced-motion media query for?

It detects if the user has set their system to prefer reduced motion. You can then disable or simplify animations, auto-playing GIFs, and parallax effects.

5. Challenge: Take a complex infographic (a multi-panel chart or diagram) and write both a concise alt text (under 15 words) and a detailed long description (using either longdesc, aria-describedby, or <figcaption>). Test your description with a screen reader.

Real-World Task

Audit your entire website for image accessibility. For every <img>, check: is the alt attribute present, is the alt text appropriate (informative vs decorative), and do complex images have detailed descriptions? Fix all issues and test with a screen reader.

FAQ

Should I include “image of” in alt text?
No. Screen readers announce “image” or “graphic” automatically. Writing “Image of a chart” becomes “Image: Image of a chart” — redundant.
How long should alt text be?
Generally 5-15 words for standard images. Complex images should have short alt text plus a separate detailed description. WCAG doesn’t set a strict limit, but extremely long alt text is hard to listen to.
Do I need captions for all video content?
Yes, if the video has speech or important audio information. WCAG SC 1.2.2 Captions (Prerecorded) requires captions for all prerecorded video with audio.
What about SVG images?
Inline SVGs (in the HTML) should have <title> and <desc> elements. For SVGs used as <img src="...">, use standard alt text.
Do icons need alt text?
If the icon is alongside visible text that explains its meaning, use alt="" (decorative). If the icon is the only indicator of the action (like a search magnifying glass), it needs alt text describing the action.

Try It Yourself

Create an image accessibility checker tool:

// image-a11y-checker.js
(function() {
  const results = document.getElementById('a11y-results') || createResultsPanel();

  document.querySelectorAll('img').forEach((img, i) => {
    const status = document.createElement('div');
    status.className = 'img-status';
    status.style.cssText = `
      padding: 4px 8px; margin: 2px 0;
      border-radius: 4px; font-size: 12px;
      font-family: monospace;
    `;

    const hasAlt = img.hasAttribute('alt');
    const altText = img.getAttribute('alt');
    const src = img.getAttribute('src')?.substring(0, 40) || '(no src)';

    if (!hasAlt) {
      status.textContent = `❌ [${i}] ${src} — MISSING ALT`;
      status.style.background = '#fdd';
    } else if (altText === '') {
      status.textContent = `➖ [${i}] ${src} — Decorative (empty alt)`;
      status.style.background = '#ffe';
    } else if (altText.length < 5) {
      status.textContent = `⚠️ [${i}] ${src} — Short alt: "${altText}"`;
      status.style.background = '#ffd';
    } else if (/image|picture|photo/i.test(altText) && altText.length < 20) {
      status.textContent = `ℹ️ [${i}] ${src} — Alt contains "image": "${altText}"`;
      status.style.background = '#eef';
    } else {
      status.textContent = `✅ [${i}] ${src} — Alt: "${altText}"`;
      status.style.background = '#dfd';
    }

    results.appendChild(status);
  });

  function createResultsPanel() {
    const panel = document.createElement('div');
    panel.id = 'a11y-results';
    panel.style.cssText = `
      position: fixed; top: 10px; right: 10px;
      max-width: 500px; max-height: 80vh; overflow-y: auto;
      background: white; border: 2px solid #333;
      border-radius: 8px; padding: 12px;
      z-index: 99999; font-family: sans-serif;
      box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    `;
    panel.innerHTML = '<h3 style="margin:0 0 8px">Image Accessibility Audit</h3>';
    document.body.appendChild(panel);
    return panel;
  }
})();

Expected behavior: A floating panel appears on the page showing every image, its alt status (missing, empty, short, or good), and recommendations.

What’s Next

Congratulations on completing this Accessible Images tutorial! Here’s where to go from here:

  • Practice daily — Write alt text for every image you add
  • Build a project — Create an automated alt-text review system for your team
  • Explore related topics — Learn accessible navigation next
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro