Skip to main content
Back to contributions
Pull Request
Merged
23.2K

Port useMediaCaption a11y rule to HTML

biomejs/biome

Ported the useMediaCaption accessibility rule from JSX to HTML, ensuring multimedia content is accessible to users with hearing disabilities

The Problem

Biome’s accessibility (a11y) lint rules were originally designed exclusively for JSX, leaving HTML files without critical accessibility validation. With Biome’s HTML linter now operational, there was a gap in coverage: the useMediaCaption rule, which ensures multimedia elements include proper caption tracks for users with hearing disabilities, only worked in JSX contexts.

This matters because WCAG (Web Content Accessibility Guidelines) requires that audio and video content provide text alternatives. Users who are deaf or hard of hearing rely on captions to access multimedia content. Without this rule in HTML, developers could unknowingly ship inaccessible <audio> and <video> elements in their HTML, Vue, Svelte, and Astro files.

Invalid HTML that would go undetected:

<video src="video.mp4"></video>
<audio src="audio.mp3"></audio>

The Solution

I ported the useMediaCaption accessibility rule from JSX to HTML as part of issue #8155, which tracks the effort to port all 30+ a11y rules to HTML.

The rule enforces that <audio> and <video> elements include a <track kind="captions"> child element, with proper handling of edge cases:

  • Muted videos are exempt - Videos with the muted attribute don’t require captions since there’s no audio content
  • Muted audio still requires captions - Audio elements need captions regardless of mute state (for transcripts/content)
  • Case-insensitive matching - Tag names are matched case-insensitively via HtmlFileSource
  • Malformed HTML handling - Skips malformed HTML to prevent false positives

Valid HTML examples:

<!-- Video with captions track -->
<video src="video.mp4">
  <track kind="captions" src="captions.vtt" />
</video>

<!-- Muted video is exempt from captions requirement -->
<video muted src="video.mp4"></video>

Files Changed

FileDescription
crates/biome_html_analyze/src/lint/a11y/use_media_caption.rsNew rule implementation with comprehensive rustdoc documentation
crates/biome_html_analyze/src/lint/a11y.rsRule registration in the a11y module
.changeset/add-use-media-caption-html.mdChangeset file (minor version bump)
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/Test fixtures for HTML
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/*.vueVue-specific test cases
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/*.svelteSvelte-specific test cases
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/*.astroAstro-specific test cases

Technical Details

The implementation uses filter_map for cleaner code logic when iterating through child elements to find track elements. Edge cases covered in tests include:

  • Missing kind attribute entirely
  • Empty kind attribute value
  • Wrong kind values (e.g., kind="subtitles")
  • Custom components that might look like media elements

CodSpeed benchmarking confirmed no performance regression with this change.

Timeline

DateAction
2026-01-11Initial PR submission with core implementation
2026-01-12CodeRabbit automated review; addressed muted exemption logic and edge cases
2026-01-12Maintainer @ematipico requested Vue, Svelte, and Astro test coverage
2026-01-13Added framework-specific test files
2026-01-14Fixed source-type aware tag name matching per review
2026-01-15PR approved and merged by @ematipico