Media and Content Accessibility

1. Alternative Text Best Practices

Image Type Alt Text Pattern Example Rationale
Informative Image Describe content/function alt="Bar chart showing 40% increase in sales" Conveys essential information
Decorative Image Empty alt attribute alt="" or role="presentation" Screen reader ignores image
Functional Image Describe action/purpose alt="Search", alt="Submit form" Explains what happens when clicked
Image Link Describe destination alt="Go to home page" Where link takes user
Logo Company/product name alt="Acme Corporation" Identifies brand
Image with Adjacent Text Complement, not duplicate Text: "CEO Jane Smith" → alt="Portrait photo" Avoid redundancy
Complex Image/Chart Short alt + long description alt="Sales data" + aria-describedby Summary in alt, details elsewhere
Image Map Alt on area elements <area alt="California"> Each clickable region labeled

Example: Alt text patterns for different scenarios

<!-- Informative image -->
<img src="chart.png" alt="Quarterly sales increased 35% from Q1 to Q2">

<!-- Decorative image (empty alt) -->
<img src="divider.png" alt="">
<img src="pattern.png" role="presentation">

<!-- Functional image (button/link) -->
<button>
  <img src="trash.png" alt="Delete item">
</button>

<a href="/">
  <img src="logo.png" alt="Return to homepage">
</a>

<!-- Logo in header -->
<header>
  <a href="/">
    <img src="logo.svg" alt="Acme Corp">
  </a>
</header>

<!-- Image with caption (don't duplicate) -->
<figure>
  <img src="sunset.jpg" alt="Golden sunset over mountain range">
  <figcaption>
    Sunset at Rocky Mountain National Park, June 2024
  </figcaption>
</figure>

<!-- Complex image with long description -->
<img 
  src="org-chart.png" 
  alt="Company organizational chart"
  aria-describedby="chart-desc">
<div id="chart-desc">
  <p>Hierarchical structure showing CEO at top, reporting to 
  three Vice Presidents for Engineering, Sales, and Marketing...</p>
</div>

<!-- Image map -->
<img src="map.png" alt="United States map" usemap="#us-map">
<map name="us-map">
  <area shape="poly" coords="..." href="/ca" alt="California">
  <area shape="poly" coords="..." href="/tx" alt="Texas">
</map>

<!-- Background image with CSS (provide text alternative) -->
<div class="hero" style="background-image: url('hero.jpg')" 
     role="img" aria-label="Team collaborating in modern office">
</div>
Alt Text Guidelines Do Don't
Length Concise (typically <150 chars) Write lengthy paragraphs in alt
Phrases to Avoid Start directly with content "Image of...", "Picture of...", "Graphic of..."
Punctuation Use proper punctuation Omit periods or use all caps
Context Match surrounding content context Provide irrelevant details
File Names Write meaningful description Use file names: "IMG_1234.jpg"
Redundancy Complement adjacent text Duplicate nearby text verbatim
Decorative Use alt="" Omit alt attribute entirely
Warning: Missing alt attributes cause screen readers to announce the file name. Always include alt="" for decorative images rather than omitting the attribute.

2. Video Captions and Transcripts

Caption Type Format Use Case WCAG Level
Closed Captions (CC) WebVTT (.vtt), SRT (.srt) User can toggle on/off - includes dialogue + sounds AA (prerecorded)
Open Captions Burned into video Always visible, cannot be disabled AA (prerecorded)
Subtitles WebVTT (.vtt) Translation for different languages Not required
Full Transcript HTML text Complete text of all audio + visual descriptions AAA (recommended)
Live Captions Real-time text stream Live broadcasts, meetings AA (live content)

Example: Video with captions and transcript

<!-- Video with multiple caption tracks -->
<video controls width="640" height="360">
  <source src="video.mp4" type="video/mp4">
  <source src="video.webm" type="video/webm">
  
  <!-- English captions (default) -->
  <track 
    kind="captions" 
    src="captions-en.vtt" 
    srclang="en" 
    label="English"
    default>
  
  <!-- Spanish subtitles -->
  <track 
    kind="subtitles" 
    src="subtitles-es.vtt" 
    srclang="es" 
    label="Español">
  
  <!-- Audio description track -->
  <track 
    kind="descriptions" 
    src="descriptions.vtt" 
    srclang="en" 
    label="Audio Descriptions">
  
  <!-- Fallback for browsers without video support -->
  <p>Your browser doesn't support HTML5 video. 
     <a href="video.mp4">Download the video</a> or 
     <a href="#transcript">read the transcript</a>.
  </p>
</video>

<!-- Transcript section -->
<details id="transcript">
  <summary>Video Transcript</summary>
  <div>
    <h3>Introduction to Web Accessibility</h3>
    
    <p><strong>[00:00]</strong></p>
    <p><strong>Narrator:</strong> Welcome to our guide on web accessibility.</p>
    
    <p><strong>[00:05] [Background music plays]</strong></p>
    <p><strong>Narrator:</strong> In this video, we'll explore why accessibility matters.</p>
    
    <p><strong>[00:10] [Visual: Statistics chart appears]</strong></p>
    <p><strong>Narrator:</strong> Over 1 billion people worldwide have disabilities.</p>
    
    <p><strong>[End of transcript]</strong></p>
  </div>
</details>

Example: WebVTT caption file format

WEBVTT

NOTE This is a caption file for accessibility demo

00:00:00.000 --> 00:00:03.000
Welcome to our guide on web accessibility.

00:00:03.500 --> 00:00:07.000
[Background music plays]

00:00:07.000 --> 00:00:11.000
In this video, we'll explore why accessibility matters.

00:00:11.500 --> 00:00:15.000
<v Narrator>Over 1 billion people worldwide have disabilities.</v>

00:00:15.500 --> 00:00:19.000
[Upbeat music]
Making websites accessible benefits everyone.

NOTE You can add speaker names with <v Speaker Name>
NOTE You can add positioning with align:start, align:middle, align:end
Caption Content Requirements Include Example
Dialogue All spoken words "Welcome to our presentation"
Speaker Identification Who is speaking (when not obvious) "<v Sarah>Let me explain...</v>"
Sound Effects Important non-speech sounds "[Door slams]", "[Phone rings]"
Music Cues Mood-setting or significant music "[Suspenseful music]", "[♪ Jazz playing ♪]"
Tone/Manner How something is said (when important) "[Sarcastically] That's just great."
Off-Screen Audio Sounds from outside frame "[Voice from hallway] Is anyone there?"
Note: Captions and subtitles are different. Captions include dialogue + sound effects for deaf/hard-of-hearing. Subtitles are translations of dialogue only.

3. Audio Descriptions Implementation

Audio Description Type Implementation Use Case WCAG Level
Standard Audio Description Narration fits in existing pauses Videos with natural dialogue breaks AA (prerecorded)
Extended Audio Description Video pauses for longer descriptions Videos without adequate pauses AAA
Descriptive Transcript Text with visual descriptions Alternative to audio description AAA
Text Track (VTT descriptions) <track kind="descriptions"> Browser-supported descriptions Modern approach
Separate Audio Track Alternate video file with descriptions When track element not supported Fallback option

Example: Video with audio descriptions

<!-- Video with audio description track -->
<video id="described-video" controls>
  <source src="presentation.mp4" type="video/mp4">
  
  <!-- Captions -->
  <track 
    kind="captions" 
    src="captions.vtt" 
    srclang="en" 
    label="English Captions"
    default>
  
  <!-- Audio descriptions -->
  <track 
    kind="descriptions" 
    src="audio-descriptions.vtt" 
    srclang="en" 
    label="Audio Descriptions">
</video>

<!-- Toggle for audio descriptions -->
<button onclick="toggleDescriptions()">
  Toggle Audio Descriptions
</button>

<script>
const video = document.getElementById('described-video');
const descTrack = video.textTracks[1]; // descriptions track

function toggleDescriptions() {
  if (descTrack.mode === 'showing') {
    descTrack.mode = 'hidden';
  } else {
    descTrack.mode = 'showing';
  }
}

// Listen for description cues
descTrack.addEventListener('cuechange', () => {
  const cue = descTrack.activeCues[0];
  if (cue) {
    // Optionally announce via screen reader
    announceToScreenReader(cue.text);
  }
});

function announceToScreenReader(text) {
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', 'polite');
  announcement.className = 'sr-only';
  announcement.textContent = text;
  document.body.appendChild(announcement);
  
  setTimeout(() => announcement.remove(), 1000);
}
</script>

Example: Audio description VTT file

WEBVTT

NOTE Audio descriptions for presentation video

00:00:05.000 --> 00:00:08.000
A woman in a blue suit stands at a podium.

00:00:15.000 --> 00:00:18.000
She gestures to a graph showing upward trending data.

00:00:28.000 --> 00:00:32.000
The camera pans to audience members taking notes.

00:00:45.000 --> 00:00:49.000
A pie chart appears on screen showing market distribution.

00:01:02.000 --> 00:01:06.000
The speaker smiles and nods to acknowledge applause.
What to Describe Priority Example Description
Actions High "Sarah picks up the phone"
Settings/Scenes High "Modern office with glass walls"
Characters/People High "A man in his 40s with gray hair"
On-Screen Text High "Title card: Five Years Later"
Facial Expressions Medium "She frowns and looks away"
Clothing/Appearance Medium "Wearing a red dress and pearl necklace"
Atmosphere/Mood Low "The dimly lit room creates tension"
Warning: Audio descriptions must fit between dialogue/narration without overlapping. If there isn't enough time, consider extended audio descriptions (which pause video) or detailed transcripts.
Media Accessibility Checklist Video Audio Only
Captions/Subtitles ✓ Required (AA) N/A
Audio Descriptions ✓ Required (AA) N/A
Transcript Recommended (AAA) ✓ Required (AA)
Keyboard Controls ✓ Required ✓ Required
Pause/Stop ✓ Required ✓ Required
Volume Control ✓ Required ✓ Required
No Auto-play ✓ Best practice ✓ Best practice

4. Complex Images and Data Visualization

Visualization Type Accessibility Strategy Implementation
Charts/Graphs Short alt + detailed description + data table aria-describedby + <table>
Infographics Structured HTML alternative Headings, lists, and semantic markup
Diagrams Long description or labeled version <details> or separate page
Maps Interactive alternative or text description List of locations with links/coordinates
Flowcharts Text-based outline of flow Ordered/nested lists showing steps
Interactive Dashboards Keyboard navigation + ARIA live regions Announce data updates, allow filtering

Example: Chart with multiple accessibility layers

<!-- Complex chart with comprehensive accessibility -->
<figure>
  <img 
    src="sales-chart.png"
    alt="Bar chart: Monthly sales for 2024"
    aria-describedby="chart-summary chart-data">
  
  <figcaption id="chart-summary">
    Sales increased steadily throughout 2024, starting at $50K in January 
    and reaching $120K by December, with notable spikes in June ($95K) 
    and November ($115K).
  </figcaption>
  
  <!-- Detailed data table (can be in details/summary) -->
  <details>
    <summary>View detailed data table</summary>
    <table id="chart-data">
      <caption>Monthly Sales Data 2024</caption>
      <thead>
        <tr>
          <th scope="col">Month</th>
          <th scope="col">Sales</th>
          <th scope="col">Change</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">January</th>
          <td>$50,000</td>
          <td>-</td>
        </tr>
        <tr>
          <th scope="row">February</th>
          <td>$58,000</td>
          <td>+16%</td>
        </tr>
        <tr>
          <th scope="row">March</th>
          <td>$65,000</td>
          <td>+12%</td>
        </tr>
        <!-- More rows... -->
      </tbody>
    </table>
  </details>
</figure>

Example: Accessible SVG chart with ARIA

<!-- Accessible SVG chart -->
<svg 
  role="img"
  aria-labelledby="chart-title chart-desc"
  width="600" 
  height="400">
  
  <title id="chart-title">Quarterly Revenue Growth</title>
  <desc id="chart-desc">
    Bar chart showing revenue growth across four quarters.
    Q1: $2.5M, Q2: $3.1M, Q3: $3.8M, Q4: $4.2M.
    Revenue increased 68% from Q1 to Q4.
  </desc>
  
  <!-- Chart elements -->
  <g role="list" aria-label="Quarterly data">
    <rect role="listitem" aria-label="Q1: $2.5 million" 
          x="50" y="200" width="100" height="150" fill="#0066cc"></rect>
    <rect role="listitem" aria-label="Q2: $3.1 million"
          x="175" y="150" width="100" height="200" fill="#0066cc"></rect>
    <rect role="listitem" aria-label="Q3: $3.8 million"
          x="300" y="100" width="100" height="250" fill="#0066cc"></rect>
    <rect role="listitem" aria-label="Q4: $4.2 million"
          x="425" y="50" width="100" height="300" fill="#0066cc"></rect>
  </g>
  
  <!-- Decorative elements hidden from screen readers -->
  <g aria-hidden="true">
    <!-- Axis lines, grid, etc. -->
  </g>
</svg>
Data Viz Best Practice Why Implementation
Provide Data Table Gives precise values screen readers can navigate Hidden by default, shown in <details>
Short + Long Description Alt gives overview, long desc gives details alt + aria-describedby
Sufficient Color Contrast Users with low vision need to see data points 4.5:1 minimum for data elements
Don't Rely on Color Alone Color blind users can't distinguish colors Use patterns, labels, shapes too
Keyboard Navigable Users need to explore data without mouse Focus indicators on data points
Announce Dynamic Changes Screen reader users need update notifications aria-live regions for data updates
Note: For complex interactive visualizations (D3.js, Chart.js), consider using accessible charting libraries like Highcharts (with accessibility module) or provide comprehensive data tables as alternatives.

5. Icon and SVG Accessibility

Icon Type Treatment Code Pattern Screen Reader Output
Decorative Icon Hide from AT aria-hidden="true" Ignored
Informative Icon Provide text alternative role="img" + aria-label Announces label
Icon in Button Label button, hide icon aria-label on button, aria-hidden on icon Announces button label
Icon with Adjacent Text Hide icon from AT aria-hidden="true" on icon Announces text only
Icon Font Add text or ARIA label <span aria-label="Save"><i>💾</i></span> Announces label
SVG Icon Use <title> or aria-label <svg><title>Save</title></svg> Announces title

Example: Icon accessibility patterns

<!-- Decorative icon with text -->
<button>
  <svg aria-hidden="true" class="icon">...</svg>
  Save Document
</button>

<!-- Icon-only button (needs label) -->
<button aria-label="Delete item">
  <svg aria-hidden="true">
    <use href="#trash-icon"></use>
  </svg>
</button>

<!-- Informative icon (standalone) -->
<svg role="img" aria-label="Warning">
  <use href="#warning-icon"></use>
</svg>

<!-- SVG with title and desc -->
<svg role="img" aria-labelledby="save-title save-desc">
  <title id="save-title">Save</title>
  <desc id="save-desc">Floppy disk icon</desc>
  <path d="M..."></path>
</svg>

<!-- Font icon (FontAwesome, Material Icons) -->
<span class="icon-wrapper">
  <i class="fas fa-home" aria-hidden="true"></i>
  <span class="sr-only">Home</span>
</span>

<!-- Emoji icons (problematic - provide text alternative) -->
<span role="img" aria-label="thumbs up">👍</span>

<!-- Status icon with text -->
<span class="status">
  <svg aria-hidden="true" class="icon-success">...</svg>
  <span>Successfully saved</span>
</span>

<!-- Icon link -->
<a href="/settings" aria-label="Settings">
  <svg aria-hidden="true">
    <use href="#gear-icon"></use>
  </svg>
</a>

<!-- Icon sprite definition (hidden) -->
<svg style="display: none;">
  <symbol id="trash-icon" viewBox="0 0 24 24">
    <path d="M..."></path>
  </symbol>
  <symbol id="warning-icon" viewBox="0 0 24 24">
    <path d="M..."></path>
  </symbol>
</svg>
SVG Accessibility Technique Code Use Case
<title> element <svg><title>Icon name</title>...</svg> Simple SVG with name only
<title> + <desc> <title>...</title><desc>...</desc> SVG needing detailed description
role="img" <svg role="img"> Ensure SVG treated as image
aria-labelledby aria-labelledby="title-id desc-id" Link to title and desc elements
aria-label <svg aria-label="Icon name"> Alternative to title element
aria-hidden="true" <svg aria-hidden="true"> Decorative SVG
focusable="false" <svg focusable="false"> Prevent focus in IE/Edge
Warning: Never use CSS content property for informative icons - screen readers may not announce them. Always use proper HTML with ARIA labels or semantic <title> elements.
Icon Library Accessibility Approach Example
Font Awesome Use aria-hidden + text or title <i class="fas fa-user" aria-hidden="true"></i><span class="sr-only">User</span>
Material Icons Add aria-label or adjacent text <span class="material-icons" aria-label="delete">delete</span>
Heroicons (SVG) Use aria-hidden on icon, label on parent <button aria-label="Menu"><svg aria-hidden="true">...</svg></button>
Emoji Wrap in role="img" with aria-label <span role="img" aria-label="celebration">🎉</span>

6. Multimedia Controls and Interfaces

Control Keyboard Shortcut ARIA Attributes Required
Play/Pause Space or K aria-label="Play" or "Pause" ✓ Yes
Volume ↑↓ or M (mute) role="slider", aria-valuenow ✓ Yes
Seek/Scrub ←→ (5s), J/L (10s) role="slider", aria-valuetext ✓ Yes
Fullscreen F aria-label="Enter fullscreen" Recommended
Captions Toggle C aria-pressed for toggle state ✓ Yes (if captions available)
Playback Speed Shift+> / Shift+< aria-label with current speed Recommended
Audio Descriptions D aria-pressed for toggle If descriptions available

Example: Custom accessible video player

<div class="video-player" role="region" aria-label="Video player">
  <video id="my-video">
    <source src="video.mp4" type="video/mp4">
    <track kind="captions" src="captions.vtt" default>
  </video>
  
  <div class="controls" role="group" aria-label="Video controls">
    <!-- Play/Pause -->
    <button 
      id="play-pause"
      aria-label="Play"
      class="control-btn">
      <svg aria-hidden="true">
        <use href="#play-icon"></use>
      </svg>
    </button>
    
    <!-- Progress bar -->
    <div class="progress-wrapper">
      <input 
        type="range"
        id="progress"
        role="slider"
        aria-label="Video progress"
        aria-valuemin="0"
        aria-valuemax="100"
        aria-valuenow="0"
        aria-valuetext="0:00 of 5:30"
        min="0"
        max="100"
        value="0">
      <div class="time-display" aria-live="off">
        <span id="current-time">0:00</span> / 
        <span id="duration">5:30</span>
      </div>
    </div>
    
    <!-- Volume control -->
    <button 
      id="mute"
      aria-label="Mute"
      aria-pressed="false"
      class="control-btn">
      <svg aria-hidden="true">
        <use href="#volume-icon"></use>
      </svg>
    </button>
    
    <input 
      type="range"
      id="volume"
      role="slider"
      aria-label="Volume"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="100"
      aria-valuetext="100%"
      min="0"
      max="100"
      value="100">
    
    <!-- Captions toggle -->
    <button 
      id="captions"
      aria-label="Captions"
      aria-pressed="true"
      class="control-btn">
      <svg aria-hidden="true">
        <use href="#cc-icon"></use>
      </svg>
    </button>
    
    <!-- Fullscreen -->
    <button 
      id="fullscreen"
      aria-label="Enter fullscreen"
      class="control-btn">
      <svg aria-hidden="true">
        <use href="#fullscreen-icon"></use>
      </svg>
    </button>
  </div>
</div>

<script>
class AccessibleVideoPlayer {
  constructor(videoElement) {
    this.video = videoElement;
    this.playPauseBtn = document.getElementById('play-pause');
    this.progressBar = document.getElementById('progress');
    this.muteBtn = document.getElementById('mute');
    this.volumeBar = document.getElementById('volume');
    this.captionsBtn = document.getElementById('captions');
    this.fullscreenBtn = document.getElementById('fullscreen');
    
    this.setupEventListeners();
    this.setupKeyboardShortcuts();
  }
  
  setupEventListeners() {
    // Play/Pause
    this.playPauseBtn.addEventListener('click', () => this.togglePlay());
    this.video.addEventListener('play', () => this.updatePlayButton(true));
    this.video.addEventListener('pause', () => this.updatePlayButton(false));
    
    // Progress
    this.video.addEventListener('timeupdate', () => this.updateProgress());
    this.progressBar.addEventListener('input', (e) => this.seek(e.target.value));
    
    // Volume
    this.muteBtn.addEventListener('click', () => this.toggleMute());
    this.volumeBar.addEventListener('input', (e) => this.setVolume(e.target.value));
    
    // Captions
    this.captionsBtn.addEventListener('click', () => this.toggleCaptions());
    
    // Fullscreen
    this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
  }
  
  setupKeyboardShortcuts() {
    document.addEventListener('keydown', (e) => {
      // Only handle if video player has focus
      if (!this.video.closest('.video-player').contains(document.activeElement)) {
        return;
      }
      
      switch(e.key) {
        case ' ':
        case 'k':
          e.preventDefault();
          this.togglePlay();
          break;
        case 'ArrowLeft':
          e.preventDefault();
          this.skip(-5);
          break;
        case 'ArrowRight':
          e.preventDefault();
          this.skip(5);
          break;
        case 'ArrowUp':
          e.preventDefault();
          this.changeVolume(0.1);
          break;
        case 'ArrowDown':
          e.preventDefault();
          this.changeVolume(-0.1);
          break;
        case 'm':
          e.preventDefault();
          this.toggleMute();
          break;
        case 'c':
          e.preventDefault();
          this.toggleCaptions();
          break;
        case 'f':
          e.preventDefault();
          this.toggleFullscreen();
          break;
      }
    });
  }
  
  togglePlay() {
    if (this.video.paused) {
      this.video.play();
    } else {
      this.video.pause();
    }
  }
  
  updatePlayButton(isPlaying) {
    this.playPauseBtn.setAttribute('aria-label', isPlaying ? 'Pause' : 'Play');
  }
  
  updateProgress() {
    const percent = (this.video.currentTime / this.video.duration) * 100;
    this.progressBar.value = percent;
    
    const currentTime = this.formatTime(this.video.currentTime);
    const duration = this.formatTime(this.video.duration);
    
    this.progressBar.setAttribute('aria-valuenow', percent);
    this.progressBar.setAttribute('aria-valuetext', `${currentTime} of ${duration}`);
    
    document.getElementById('current-time').textContent = currentTime;
    document.getElementById('duration').textContent = duration;
  }
  
  seek(percent) {
    this.video.currentTime = (percent / 100) * this.video.duration;
  }
  
  skip(seconds) {
    this.video.currentTime += seconds;
  }
  
  toggleMute() {
    this.video.muted = !this.video.muted;
    this.muteBtn.setAttribute('aria-pressed', this.video.muted);
    this.muteBtn.setAttribute('aria-label', this.video.muted ? 'Unmute' : 'Mute');
  }
  
  setVolume(value) {
    this.video.volume = value / 100;
    this.volumeBar.setAttribute('aria-valuenow', value);
    this.volumeBar.setAttribute('aria-valuetext', `${value}%`);
  }
  
  changeVolume(delta) {
    const newVolume = Math.max(0, Math.min(1, this.video.volume + delta));
    this.video.volume = newVolume;
    this.volumeBar.value = newVolume * 100;
    this.setVolume(newVolume * 100);
  }
  
  toggleCaptions() {
    const track = this.video.textTracks[0];
    if (track.mode === 'showing') {
      track.mode = 'hidden';
      this.captionsBtn.setAttribute('aria-pressed', 'false');
    } else {
      track.mode = 'showing';
      this.captionsBtn.setAttribute('aria-pressed', 'true');
    }
  }
  
  toggleFullscreen() {
    if (document.fullscreenElement) {
      document.exitFullscreen();
      this.fullscreenBtn.setAttribute('aria-label', 'Enter fullscreen');
    } else {
      this.video.closest('.video-player').requestFullscreen();
      this.fullscreenBtn.setAttribute('aria-label', 'Exit fullscreen');
    }
  }
  
  formatTime(seconds) {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
  }
}

// Initialize player
const player = new AccessibleVideoPlayer(document.getElementById('my-video'));
</script>
Player Best Practice Implementation WCAG Criteria
Keyboard Accessible All controls operable via keyboard 2.1.1 Keyboard
Visible Focus Clear focus indicators on all controls 2.4.7 Focus Visible
Labeled Controls aria-label on all buttons 4.1.2 Name, Role, Value
State Announcements Update ARIA attributes dynamically 4.1.2 Name, Role, Value
No Auto-play User initiates playback 1.4.2 Audio Control
Pause Control Always provide pause button 2.2.2 Pause, Stop, Hide
Touch Targets ≥44px Buttons large enough for mobile 2.5.5 Target Size (AAA)
Note: Use native <video controls> when possible - browsers provide accessible controls by default. Only build custom controls when design absolutely requires it.

Media and Content Accessibility Quick Reference

  • All images need alt attribute - use alt="" for decorative images
  • Describe content/function in alt text, not just "image of..." - keep under 150 characters
  • Videos require captions (AA) and audio descriptions (AA) for WCAG compliance
  • Provide transcripts for all media - helps SEO and provides searchable alternative
  • Use WebVTT format for captions: include dialogue, speaker ID, and sound effects
  • Complex charts need: short alt + detailed description + data table alternative
  • SVG icons: use aria-hidden="true" for decorative, role="img" + aria-label for informative
  • Icon-only buttons must have aria-label - hide icon with aria-hidden
  • Custom video players need keyboard shortcuts (Space, arrows, M, F, C) and ARIA labels
  • Never auto-play media with sound - provide pause/stop controls within 3 clicks/taps