Skip to content

fix: add volume availability to skins#984

Open
sampotts wants to merge 3 commits intomainfrom
fix/volume-availability
Open

fix: add volume availability to skins#984
sampotts wants to merge 3 commits intomainfrom
fix/volume-availability

Conversation

@sampotts
Copy link
Collaborator

Summary

  • Add volume availability state/data attrs to the volume slider and mute button
  • Detect volume support with a runtime probe and use it to gate React and HTML volume popovers
  • Add coverage for the feature detection and HTML popover gating, and update the volume docs

Testing

  • pnpm -F @videojs/core test src/core/ui/mute-button/tests/mute-button-core.test.ts
  • pnpm -F @videojs/html test src/ui/popover/tests/popover-element.test.ts
  • pnpm -F @videojs/core build
  • pnpm -F @videojs/react build
  • pnpm -F @videojs/html build

Closes #961

@vercel
Copy link

vercel bot commented Mar 17, 2026

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Mar 17, 2026 4:43am

Request Review

@netlify
Copy link

netlify bot commented Mar 17, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 2f2cdc2
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69b8dbeab7e9100008eafa7f
😎 Deploy Preview https://deploy-preview-984--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

📦 Bundle Size Report

🎨 @videojs/html

Path Base PR Diff %
/video/minimal-skin 22.15 kB 22.51 kB +374 B +1.6% 🔺
/video/minimal-skin.tailwind 22.44 kB 22.80 kB +365 B +1.6% 🔺
/video/skin 22.32 kB 22.65 kB +338 B +1.5% 🔺
/video/skin.tailwind 22.63 kB 22.97 kB +348 B +1.5% 🔺
/audio/minimal-skin 20.74 kB 21.10 kB +361 B +1.7% 🔺
/audio/minimal-skin.tailwind 20.78 kB 21.15 kB +380 B +1.8% 🔺
/audio/skin 20.79 kB 21.17 kB +389 B +1.8% 🔺
/audio/skin.tailwind 21.01 kB 21.40 kB +397 B +1.8% 🔺
/ui/popover 3.28 kB 2.32 kB -982 B -29.2% 🔽
/video (default) 22.86 kB 23.21 kB +354 B +1.5% 🔺
/video (default + hls) 153.21 kB 153.73 kB +535 B +0.3% 🔺
/video (minimal) 22.73 kB 23.14 kB +418 B +1.8% 🔺
/video (minimal + hls) 153.22 kB 153.60 kB +385 B +0.2% 🔺
/audio (default) 21.33 kB 21.72 kB +398 B +1.8% 🔺
/audio (minimal) 21.32 kB 21.66 kB +357 B +1.6% 🔺
Presets (7)
Entry Size
/video (default) 23.21 kB
/video (default + hls) 153.73 kB
/video (minimal) 23.14 kB
/video (minimal + hls) 153.60 kB
/audio (default) 21.72 kB
/audio (minimal) 21.66 kB
/background 6.60 kB
Media (5)
Entry Size
/media/background-video 617 B
/media/container 1.91 kB
/media/dash-video 236.04 kB
/media/hls-video 131.29 kB
/media/simple-hls-video 11.95 kB
Players (3)
Entry Size
/video/player 6.46 kB
/audio/player 6.45 kB
/background/player 6.44 kB
Skins (16)
Entry Type Size
/video/minimal-skin.css css 2.99 kB
/video/skin.css css 3.03 kB
/video/minimal-skin js 22.51 kB
/video/minimal-skin.tailwind js 22.80 kB
/video/skin js 22.65 kB
/video/skin.tailwind js 22.97 kB
/audio/minimal-skin.css css 2.26 kB
/audio/skin.css css 2.29 kB
/audio/minimal-skin js 21.10 kB
/audio/minimal-skin.tailwind js 21.15 kB
/audio/skin js 21.17 kB
/audio/skin.tailwind js 21.40 kB
/background/skin.css css 124 B
/background/skin js 999 B
/base.css css 177 B
/shared.css css 43 B
UI Components (21)
Entry Size
/ui/alert-dialog 2.11 kB
/ui/alert-dialog-close 1.25 kB
/ui/alert-dialog-description 1.20 kB
/ui/alert-dialog-title 1.19 kB
/ui/buffering-indicator 1.77 kB
/ui/captions-button 1.81 kB
/ui/controls 1.57 kB
/ui/fullscreen-button 1.80 kB
/ui/mute-button 1.79 kB
/ui/pip-button 1.82 kB
/ui/play-button 1.79 kB
/ui/playback-rate-button 1.83 kB
/ui/popover 2.32 kB
/ui/poster 1.71 kB
/ui/seek-button 1.84 kB
/ui/slider 2.03 kB
/ui/thumbnail 2.15 kB
/ui/time 1.60 kB
/ui/time-slider 2.95 kB
/ui/tooltip 3.33 kB
/ui/volume-slider 2.19 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react

(no changes)

Presets (7)
Entry Size
/video (default) 18.91 kB
/video (default + hls) 149.68 kB
/video (minimal) 18.91 kB
/video (minimal + hls) 149.66 kB
/audio (default) 15.09 kB
/audio (minimal) 15.16 kB
/background 3.34 kB
Media (4)
Entry Size
/media/background-video 539 B
/media/dash-video 236.19 kB
/media/hls-video 131.64 kB
/media/simple-hls-video 12.38 kB
Skins (14)
Entry Type Size
/video/minimal-skin.css css 2.99 kB
/video/skin.css css 3.03 kB
/video/minimal-skin js 18.81 kB
/video/minimal-skin.tailwind js 21.86 kB
/video/skin js 18.81 kB
/video/skin.tailwind js 21.91 kB
/audio/minimal-skin.css css 2.26 kB
/audio/skin.css css 2.29 kB
/audio/minimal-skin js 15.05 kB
/audio/minimal-skin.tailwind js 17.12 kB
/audio/skin js 15.03 kB
/audio/skin.tailwind js 17.35 kB
/background/skin.css css 90 B
/background/skin js 272 B
UI Components (18)
Entry Size
/ui/alert-dialog 2.32 kB
/ui/buffering-indicator 2.23 kB
/ui/captions-button 2.28 kB
/ui/controls 2.24 kB
/ui/fullscreen-button 2.27 kB
/ui/mute-button 2.28 kB
/ui/pip-button 2.26 kB
/ui/play-button 2.27 kB
/ui/playback-rate-button 2.31 kB
/ui/popover 2.81 kB
/ui/poster 2.13 kB
/ui/seek-button 2.25 kB
/ui/slider 2.98 kB
/ui/thumbnail 2.17 kB
/ui/time 2.00 kB
/ui/time-slider 2.76 kB
/ui/tooltip 2.63 kB
/ui/volume-slider 2.63 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core

(no changes)

Entries (6)
Entry Size
. 4.81 kB
/dom 8.53 kB
/dom/media/custom-media-element 1.81 kB
/dom/media/dash 235.63 kB
/dom/media/hls 131.19 kB
/dom/media/simple-hls 11.84 kB

🏷️ @videojs/element

(no changes)

Entries (2)
Entry Size
. 999 B
/context 936 B

📦 @videojs/store

(no changes)

Entries (3)
Entry Size
. 1.32 kB
/html 700 B
/react 360 B

🔧 @videojs/utils

(no changes)

Entries (10)
Entry Size
/array 104 B
/dom 1.25 kB
/events 227 B
/function 261 B
/object 119 B
/predicate 265 B
/string 148 B
/style 190 B
/time 478 B
/number 158 B

📦 @videojs/spf

(no changes)

Entries (3)
Entry Size
. 40 B
/dom 10.04 kB
/playback-engine 9.94 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.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

CI Failure Diagnosis

File Type What failed
packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx typecheck Type "unsupported" is not assignable to type "available" at lines 138 and 154 (TS2322).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an explicit “volume availability” signal throughout the UI so skins can hide/disable volume controls (notably the volume popover on iOS/Mobile Safari where programmatic volume changes are unsupported), backed by a runtime capability probe and new tests/docs.

Changes:

  • Introduces async runtime probing for volumeAvailability in the core volume feature and updates tests accordingly.
  • Threads availability into MuteButton and VolumeSlider state + data-availability attrs across core/react/html implementations.
  • Gates React skins’ volume popover rendering and HTML popover trigger binding/visibility based on availability; updates docs with styling guidance.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
site/src/content/docs/reference/volume-slider.mdx Documents data-availability and CSS patterns to hide unsupported volume sliders.
site/src/content/docs/reference/feature-volume.mdx Updates examples to check volumeAvailability before rendering volume UI; adds HTML getter example.
packages/react/src/ui/volume-slider/volume-slider-root.tsx Switches to VolumeSliderDataAttrs so data-availability is applied on the root.
packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx Adds coverage for data-availability propagation and render-state exposure.
packages/react/src/presets/video/skin.tsx Gates volume popover rendering based on volumeAvailability; factors mute render into a shared function.
packages/react/src/presets/video/skin.tailwind.tsx Same gating/refactor for the Tailwind video skin.
packages/react/src/presets/video/minimal-skin.tsx Same gating/refactor for the minimal video skin.
packages/react/src/presets/video/minimal-skin.tailwind.tsx Same gating/refactor for the Tailwind minimal video skin.
packages/react/src/presets/audio/skin.tsx Same gating/refactor for the audio skin.
packages/react/src/presets/audio/skin.tailwind.tsx Same gating/refactor for the Tailwind audio skin.
packages/react/src/presets/audio/minimal-skin.tsx Same gating/refactor for the minimal audio skin.
packages/react/src/presets/audio/minimal-skin.tailwind.tsx Same gating/refactor for the Tailwind minimal audio skin.
packages/html/src/ui/volume-slider/volume-slider-element.ts Switches to VolumeSliderDataAttrs so data-availability is applied in HTML implementation.
packages/html/src/ui/popover/popover-element.ts Adds trigger availability gating using trigger’s data-availability to hide/unbind popovers when unsupported.
packages/html/src/ui/popover/tests/popover-element.test.ts Adds tests covering binding/visibility behavior for available vs unsupported triggers.
packages/core/src/dom/store/features/volume.ts Replaces sync canSetVolume with async DOM probe to resolve volumeAvailability.
packages/core/src/dom/store/features/tests/volume.test.ts Adds coverage for async probe resolution and unsupported detection behavior.
packages/core/src/core/ui/volume-slider/volume-slider-data-attrs.ts Adds availability -> data-availability mapping for volume slider state attrs.
packages/core/src/core/ui/volume-slider/volume-slider-core.ts Adds availability to slider state derived from media volumeAvailability.
packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts Verifies availability is projected into VolumeSliderCore state.
packages/core/src/core/ui/mute-button/mute-button-core.ts Adds availability to mute button state.
packages/core/src/core/ui/mute-button/mute-button-data-attrs.ts Adds availability -> data-availability mapping for mute button state attrs.
packages/core/src/core/ui/mute-button/tests/mute-button-core.test.ts Updates tests for new availability state on mute button.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +164 to 167
return;
}

if (state.open) {
Comment on lines +142 to +144
const availableTriggerEl = this.#isTriggerAvailable(triggerEl) ? triggerEl : null;
this.toggleAttribute('hidden', !availableTriggerEl);
this.#syncTrigger(availableTriggerEl);
Comment on lines +48 to +51
detectVolumeAvailability().then((volumeAvailability) => {
if (signal.aborted) return;
set({ volumeAvailability });
});
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a first-class “volume availability” signal across core state, UI components, and skins to prevent showing non-functional volume controls/popovers on platforms (notably iOS Safari) where programmatic volume changes are unsupported.

Changes:

  • Introduces async runtime probing of volume support and exposes it via volumeAvailability (available / unavailable / unsupported).
  • Propagates volume availability to UI state/data attributes for volume slider and mute button, and gates volume popovers in React/HTML skins.
  • Adds/updates tests and documentation for availability detection and popover gating.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
site/src/content/docs/reference/volume-slider.mdx Documents data-availability and styling patterns for unsupported volume control.
site/src/content/docs/reference/feature-volume.mdx Updates examples to gate UI rendering based on volumeAvailability.
packages/utils/src/dom/tests/slotted.test.ts Tightens typing for getSlottedElement usage.
packages/react/src/ui/volume-slider/volume-slider-root.tsx Switches to VolumeSliderDataAttrs so availability is reflected on the root.
packages/react/src/ui/volume-slider/tests/volume-slider.test.tsx Adds tests asserting data-availability and render-state exposure.
packages/react/src/presets/video/skin.tsx Gates the volume popover based on volumeAvailability.
packages/react/src/presets/video/skin.tailwind.tsx Same gating for tailwind video skin.
packages/react/src/presets/video/minimal-skin.tsx Same gating for minimal video skin.
packages/react/src/presets/video/minimal-skin.tailwind.tsx Same gating for tailwind minimal video skin.
packages/react/src/presets/audio/skin.tsx Same gating for audio skin.
packages/react/src/presets/audio/skin.tailwind.tsx Same gating for tailwind audio skin.
packages/react/src/presets/audio/minimal-skin.tsx Same gating for minimal audio skin.
packages/react/src/presets/audio/minimal-skin.tailwind.tsx Same gating for tailwind minimal audio skin.
packages/html/src/ui/volume-slider/volume-slider-element.ts Uses VolumeSliderDataAttrs to apply data-availability.
packages/html/src/ui/popover/popover-element.ts Adds trigger availability gating and mutation observation to rebind/hide popovers.
packages/html/src/ui/popover/tests/popover-element.test.ts Adds coverage for popover binding/hiding based on trigger availability.
packages/core/src/dom/store/features/volume.ts Replaces sync detection with async probe; initializes volumeAvailability to unavailable.
packages/core/src/dom/store/features/tests/volume.test.ts Adds coverage for initial unavailable and probe outcomes.
packages/core/src/core/ui/volume-slider/volume-slider-data-attrs.ts Adds availability -> data-availability mapping.
packages/core/src/core/ui/volume-slider/volume-slider-core.ts Projects volumeAvailability into slider state as availability.
packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts Adds assertions for availability projection.
packages/core/src/core/ui/mute-button/mute-button-data-attrs.ts Adds availability -> data-availability mapping.
packages/core/src/core/ui/mute-button/mute-button-core.ts Adds availability to mute button state.
packages/core/src/core/ui/mute-button/tests/mute-button-core.test.ts Updates state factory/assertions for new availability field.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +264 to +273
if (node instanceof HTMLElement && this.#isLinkedTrigger(node)) return true;
}

for (const node of record.removedNodes) {
if (node instanceof HTMLElement && this.#isLinkedTrigger(node)) return true;
}

return false;
}

Comment on lines +140 to +143
it('sets data-availability from the volume feature', () => {
const { Wrapper } = createPlayerWrapper();
mockVolumeState.volumeAvailability = 'unsupported';

Comment on lines +156 to +159
it('exposes availability to render state', () => {
const { Wrapper } = createPlayerWrapper();
mockVolumeState.volumeAvailability = 'unsupported';

Comment on lines +264 to +273
if (node instanceof HTMLElement && this.#isLinkedTrigger(node)) return true;
}

for (const node of record.removedNodes) {
if (node instanceof HTMLElement && this.#isLinkedTrigger(node)) return true;
}

return false;
}

Comment on lines +48 to 52
detectVolumeAvailability().then((volumeAvailability) => {
if (signal.aborted) return;
set({ volumeAvailability });
});


Controls the media volume level. The slider maps its 0–100 internal range to the media's 0–1 volume scale. When the media is muted, the fill level drops to 0 regardless of the stored volume value.

The root also reflects volume control availability with `data-availability`. On platforms where volume cannot be changed programmatically, such as iOS, the slider exposes `data-availability="unsupported"`.
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.

Bug: Volume popover is shown on Mobile Safari

2 participants