Skip to content

feat(core): add status announcer state updates#1659

Open
sampotts wants to merge 11 commits into
mainfrom
feat/status-announcer
Open

feat(core): add status announcer state updates#1659
sampotts wants to merge 11 commits into
mainfrom
feat/status-announcer

Conversation

@sampotts

@sampotts sampotts commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add snapshot-driven status announcer updates for confirmed player state changes.
  • Announce playback, captions, fullscreen, PiP, playback rate, debounced volume, and completed seeks while avoiding routine time/progress noise.
  • Wire React and HTML announcers to store snapshots and use hidden live-region text.
  • Add default accessible container attrs and suppress duplicate slider-owned announcements while slider focus is active.

The slider focus stuff is because if we don't suppress announcements when the sliders have focus, we get double upped announcements.

Closes #1436

Validation

  • pnpm -F @videojs/core test src/core/ui/input-feedback src/dom/ui/tests/input-action.test.ts
  • pnpm -F @videojs/react test src/ui/input-indicators/tests/input-indicators.test.tsx src/player/tests/context.test.tsx
  • pnpm -F @videojs/html test src/ui/input-indicators/tests/input-indicators.test.ts src/media/tests/container-element.test.ts
  • pnpm -F @videojs/core build
  • pnpm -F @videojs/react build
  • pnpm -F @videojs/html build
  • pnpm exec biome check
  • git diff --check

Notes

  • pnpm typecheck still fails in unrelated packages/spf/src/playback files with existing missing export/type errors.

Note

Medium Risk
Touches live-region accessibility and focus behavior across React/HTML/custom elements; logic is well-tested but incorrect suppression or timing could affect screen-reader UX.

Overview
Adds snapshot-driven screen-reader announcements in StatusAnnouncerCore: confirmed changes to play/pause, captions (when available), fullscreen, PiP, and playback rate are announced immediately; volume and completed seeks are debounced (200ms) and ignore normal currentTime drift. React and HTML StatusAnnouncer components now subscribe to the player store (with baseline/reset on reconnect) and render visually hidden live-region text instead of driving aria-label from input actions alone.

Seek/volume suppression when a role="slider" inside the player container has focus avoids doubling slider-owned feedback; getMediaSnapshot now includes playbackRate and seeking. Player containers get shared defaults (role="group", aria-label, tabindex) via applyContainerAttrs / React Container, and pointer-up focus uses composed-tree active-element helpers for shadow DOM.

Reviewed by Cursor Bugbot for commit a502337. Bugbot is set up for automated code reviews on this repo. Configure here.

@netlify

netlify Bot commented Jun 5, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit a502337
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a224947e7705c00085e5c8a
😎 Deploy Preview https://deploy-preview-1659--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Jun 5, 2026 3:58am

Request Review

Comment thread packages/html/src/ui/status-announcer/status-announcer-element.ts
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html

Path Base PR Diff %
/video/minimal-skin 39.21 kB 40.08 kB +887 B +2.2% 🔺
/video/minimal-skin.tailwind 39.64 kB 40.51 kB +890 B +2.2% 🔺
/video/skin 39.74 kB 40.61 kB +891 B +2.2% 🔺
/video/skin.tailwind 40.14 kB 41.01 kB +896 B +2.2% 🔺
/live-video/minimal-skin 34.14 kB 35.00 kB +879 B +2.5% 🔺
/live-video/minimal-skin.tailwind 34.21 kB 35.14 kB +950 B +2.7% 🔺
/live-video/skin 34.17 kB 35.05 kB +902 B +2.6% 🔺
/live-video/skin.tailwind 34.25 kB 35.16 kB +936 B +2.7% 🔺
/ui/compounds 5.66 kB 5.22 kB -453 B -7.8% 🔽
/ui/status-announcer 2.47 kB 3.17 kB +723 B +28.6% 🔴
/video (default) 39.71 kB 40.56 kB +863 B +2.1% 🔺
/video (default + hls) 173.25 kB 174.10 kB +871 B +0.5% 🔺
/video (minimal) 39.27 kB 40.13 kB +881 B +2.2% 🔺
/video (minimal + hls) 172.88 kB 173.71 kB +853 B +0.5% 🔺
Presets (7)
Entry Size
/video (default) 40.56 kB
/video (default + hls) 174.10 kB
/video (minimal) 40.13 kB
/video (minimal + hls) 173.71 kB
/audio (default) 34.16 kB
/audio (minimal) 31.14 kB
/background 4.40 kB
Media (9)
Entry Size
/media/background-video 1.04 kB
/media/container 1.90 kB
/media/dash-video 236.69 kB
/media/hls-video 134.98 kB
/media/mux-audio 160.96 kB
/media/mux-video 160.95 kB
/media/native-hls-video 4.63 kB
/media/simple-hls-audio-only 15.32 kB
/media/simple-hls-video 16.96 kB
Players (5)
Entry Size
/video/player 7.36 kB
/audio/player 5.35 kB
/background/player 4.10 kB
/live-video/player 7.37 kB
/live-audio/player 5.36 kB
Skins (30)
Entry Type Size
/video/minimal-skin.css css 4.84 kB
/video/skin.css css 4.90 kB
/video/minimal-skin js 40.08 kB
/video/minimal-skin.tailwind js 40.51 kB
/video/skin js 40.61 kB
/video/skin.tailwind js 41.01 kB
/audio/minimal-skin.css css 3.03 kB
/audio/skin.css css 3.05 kB
/audio/minimal-skin js 31.16 kB
/audio/minimal-skin.tailwind js 31.48 kB
/audio/skin js 34.20 kB
/audio/skin.tailwind js 34.49 kB
/background/skin.css css 133 B
/background/skin js 1.16 kB
/live-video/minimal-skin.css css 4.84 kB
/live-video/skin.css css 4.90 kB
/live-video/minimal-skin js 35.00 kB
/live-video/minimal-skin.tailwind js 35.14 kB
/live-video/skin js 35.05 kB
/live-video/skin.tailwind js 35.16 kB
/live-audio/minimal-skin.css css 3.03 kB
/live-audio/skin.css css 3.05 kB
/live-audio/minimal-skin js 26.12 kB
/live-audio/minimal-skin.tailwind js 26.04 kB
/live-audio/skin js 28.73 kB
/live-audio/skin.tailwind js 28.60 kB
/global.css css 176 B
/shared.css css 88 B
/tailwind.css css 228 B
/skin-element js 1.37 kB
UI Components (36)
Entry Size
/ui/airplay-button 2.21 kB
/ui/alert-dialog 864 B
/ui/alert-dialog-close 399 B
/ui/alert-dialog-description 390 B
/ui/alert-dialog-title 358 B
/ui/buffering-indicator 2.26 kB
/ui/captions-button 2.22 kB
/ui/cast-button 2.21 kB
/ui/compounds 5.22 kB
/ui/controls 2.01 kB
/ui/error-dialog 2.47 kB
/ui/fullscreen-button 2.21 kB
/ui/hotkey 2.73 kB
/ui/menu 2.60 kB
/ui/mute-button 2.22 kB
/ui/pip-button 2.25 kB
/ui/play-button 2.21 kB
/ui/playback-rate-button 2.09 kB
/ui/playback-rate-menu 3.03 kB
/ui/popover 1.48 kB
/ui/poster 2.03 kB
/ui/seek-button 2.31 kB
/ui/seek-indicator 2.57 kB
/ui/seek-indicator-value 315 B
/ui/slider 1.13 kB
/ui/status-announcer 3.17 kB
/ui/status-indicator 2.35 kB
/ui/status-indicator-value 183 B
/ui/thumbnail 2.42 kB
/ui/time 2.11 kB
/ui/time-slider 2.85 kB
/ui/tooltip 1.80 kB
/ui/volume-indicator 2.66 kB
/ui/volume-indicator-fill 332 B
/ui/volume-indicator-value 332 B
/ui/volume-slider 3.47 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react

Path Base PR Diff %
/video/minimal-skin 33.37 kB 34.15 kB +796 B +2.3% 🔺
/video/minimal-skin.tailwind 38.31 kB 39.19 kB +910 B +2.3% 🔺
/video/skin 33.24 kB 34.05 kB +828 B +2.4% 🔺
/video/skin.tailwind 38.31 kB 39.15 kB +861 B +2.2% 🔺
/live-video/minimal-skin 24.54 kB 25.51 kB +998 B +4.0% 🔺
/live-video/minimal-skin.tailwind 29.11 kB 30.14 kB +1.03 kB +3.5% 🔺
/live-video/skin 24.50 kB 25.49 kB +1014 B +4.0% 🔺
/live-video/skin.tailwind 29.21 kB 30.18 kB +999 B +3.3% 🔺
/live-audio/minimal-skin 20.35 kB 20.73 kB +387 B +1.9% 🔺
/live-audio/minimal-skin.tailwind 23.06 kB 23.42 kB +363 B +1.5% 🔺
/live-audio/skin 20.39 kB 20.75 kB +372 B +1.8% 🔺
/live-audio/skin.tailwind 23.18 kB 23.54 kB +372 B +1.6% 🔺
/ui/seek-indicator 1.83 kB 1.96 kB +136 B +7.3% 🔺
/ui/status-announcer 1.67 kB 2.32 kB +669 B +39.2% 🔴
/video (default) 33.31 kB 34.17 kB +877 B +2.6% 🔺
/video (default + hls) 165.80 kB 166.51 kB +720 B +0.4% 🔺
/video (minimal) 33.41 kB 34.28 kB +890 B +2.6% 🔺
/video (minimal + hls) 165.79 kB 166.62 kB +851 B +0.5% 🔺
Presets (7)
Entry Size
/video (default) 34.17 kB
/video (default + hls) 166.51 kB
/video (minimal) 34.28 kB
/video (minimal + hls) 166.62 kB
/audio (default) 27.58 kB
/audio (minimal) 27.65 kB
/background 939 B
Media (8)
Entry Size
/media/background-video 773 B
/media/dash-video 235.32 kB
/media/hls-video 133.70 kB
/media/mux-audio 159.77 kB
/media/mux-video 159.78 kB
/media/native-hls-video 3.33 kB
/media/simple-hls-audio-only 14.08 kB
/media/simple-hls-video 15.73 kB
Skins (27)
Entry Type Size
/tailwind.css css 228 B
/video/minimal-skin.css css 4.75 kB
/video/skin.css css 4.81 kB
/video/minimal-skin js 34.15 kB
/video/minimal-skin.tailwind js 39.19 kB
/video/skin js 34.05 kB
/video/skin.tailwind js 39.15 kB
/audio/minimal-skin.css css 2.91 kB
/audio/skin.css css 2.91 kB
/audio/minimal-skin js 27.55 kB
/audio/minimal-skin.tailwind js 27.44 kB
/audio/skin js 27.48 kB
/audio/skin.tailwind js 30.75 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 4.75 kB
/live-video/skin.css css 4.81 kB
/live-video/minimal-skin js 25.51 kB
/live-video/minimal-skin.tailwind js 30.14 kB
/live-video/skin js 25.49 kB
/live-video/skin.tailwind js 30.18 kB
/live-audio/minimal-skin.css css 2.91 kB
/live-audio/skin.css css 2.91 kB
/live-audio/minimal-skin js 20.73 kB
/live-audio/minimal-skin.tailwind js 23.42 kB
/live-audio/skin js 20.75 kB
/live-audio/skin.tailwind js 23.54 kB
UI Components (30)
Entry Size
/ui/airplay-button 2.17 kB
/ui/alert-dialog 1.18 kB
/ui/buffering-indicator 1.90 kB
/ui/captions-button 2.16 kB
/ui/cast-button 2.11 kB
/ui/controls 1.97 kB
/ui/error-dialog 2.45 kB
/ui/fullscreen-button 2.12 kB
/ui/gesture 1.39 kB
/ui/hotkey 1.54 kB
/ui/live-button 2.10 kB
/ui/menu 4.66 kB
/ui/mute-button 2.12 kB
/ui/pip-button 2.15 kB
/ui/play-button 2.13 kB
/ui/playback-rate-button 2.14 kB
/ui/playback-rate-menu 4.58 kB
/ui/popover 2.79 kB
/ui/poster 1.79 kB
/ui/seek-button 2.21 kB
/ui/seek-indicator 1.96 kB
/ui/slider 2.70 kB
/ui/status-announcer 2.32 kB
/ui/status-indicator 1.86 kB
/ui/thumbnail 2.13 kB
/ui/time 1.56 kB
/ui/time-slider 3.03 kB
/ui/tooltip 2.94 kB
/ui/volume-indicator 1.97 kB
/ui/volume-slider 2.38 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core

Path Base PR Diff %
. 7.59 kB 8.13 kB +550 B +7.1% 🔺
/dom 15.70 kB 16.15 kB +452 B +2.8% 🔺
Entries (10)
Entry Size
. 8.13 kB
/dom 16.15 kB
/dom/media/custom-media-element 1.90 kB
/dom/media/dash 234.36 kB
/dom/media/google-cast 4.07 kB
/dom/media/hls 132.99 kB
/dom/media/mux 158.95 kB
/dom/media/native-hls 2.52 kB
/dom/media/simple-hls 14.91 kB
/dom/media/simple-hls-audio-only 13.29 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 2.21 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 192 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (4)
Entry Size
. 4.45 kB
/dom 6.33 kB
/hls 14.40 kB
/background-looping-video 12.28 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

Comment thread packages/core/src/core/ui/input-feedback/status-announcer-core.ts
Comment thread packages/react/src/ui/status-announcer/status-announcer.tsx Outdated
Comment thread packages/core/src/core/ui/input-feedback/status-announcer-core.ts
Comment thread packages/core/src/core/ui/input-feedback/status-announcer-core.ts
Comment thread packages/core/src/dom/ui/slider-focus.ts
Comment thread packages/core/src/dom/ui/slider-focus.ts
Comment thread packages/react/src/ui/status-announcer/status-announcer.tsx Outdated
Comment thread packages/react/src/ui/status-announcer/status-announcer.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3bbb041. Configure here.

Comment thread packages/core/src/core/ui/input-feedback/status-announcer-core.ts
@sampotts sampotts requested review from luwes and mihar-22 June 5, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Feature: Accessible Player State Announcer

1 participant