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

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Accessible Player State Announcer

2 participants