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
mutedattribute 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
| File | Description |
|---|---|
crates/biome_html_analyze/src/lint/a11y/use_media_caption.rs | New rule implementation with comprehensive rustdoc documentation |
crates/biome_html_analyze/src/lint/a11y.rs | Rule registration in the a11y module |
.changeset/add-use-media-caption-html.md | Changeset file (minor version bump) |
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/ | Test fixtures for HTML |
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/*.vue | Vue-specific test cases |
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/*.svelte | Svelte-specific test cases |
crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/*.astro | Astro-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
kindattribute entirely - Empty
kindattribute value - Wrong
kindvalues (e.g.,kind="subtitles") - Custom components that might look like media elements
CodSpeed benchmarking confirmed no performance regression with this change.
Timeline
| Date | Action |
|---|---|
| 2026-01-11 | Initial PR submission with core implementation |
| 2026-01-12 | CodeRabbit automated review; addressed muted exemption logic and edge cases |
| 2026-01-12 | Maintainer @ematipico requested Vue, Svelte, and Astro test coverage |
| 2026-01-13 | Added framework-specific test files |
| 2026-01-14 | Fixed source-type aware tag name matching per review |
| 2026-01-15 | PR approved and merged by @ematipico |