diff --git a/packages/html/src/define/shared.css b/packages/html/src/define/shared.css
index dbe98d6a..456d624a 100644
--- a/packages/html/src/define/shared.css
+++ b/packages/html/src/define/shared.css
@@ -6,3 +6,8 @@ media-tooltip-group {
:host {
display: grid;
}
+
+/* Hide volume popover when volume control is unsupported (e.g., iOS Safari). */
+.media-popover--volume:has(media-volume-slider[data-availability="unsupported"]) {
+ display: none;
+}
diff --git a/packages/react/src/presets/audio/minimal-skin.tailwind.tsx b/packages/react/src/presets/audio/minimal-skin.tailwind.tsx
index e88f4fae..eaa77156 100644
--- a/packages/react/src/presets/audio/minimal-skin.tailwind.tsx
+++ b/packages/react/src/presets/audio/minimal-skin.tailwind.tsx
@@ -98,6 +98,38 @@ function PlayLabel(): ReactNode {
return paused ? <>Play> : <>Pause>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+ }>
+ }>
+ } />
+
+ } />
+
+
+
+ );
+}
+
/* ------------------------------------------ Skin ------------------------------------------- */
export function MinimalAudioSkinTailwind(props: MinimalAudioSkinProps): ReactNode {
@@ -196,33 +228,7 @@ export function MinimalAudioSkinTailwind(props: MinimalAudioSkinProps): ReactNod
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
- }
- >
- }>
- } />
-
- } />
-
-
-
+
diff --git a/packages/react/src/presets/audio/minimal-skin.tsx b/packages/react/src/presets/audio/minimal-skin.tsx
index 904a5c7c..98e9e3a6 100644
--- a/packages/react/src/presets/audio/minimal-skin.tsx
+++ b/packages/react/src/presets/audio/minimal-skin.tsx
@@ -36,6 +36,38 @@ function PlayLabel(): ReactNode {
return paused ? <>Play> : <>Pause>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
export function MinimalAudioSkin(props: MinimalAudioSkinProps): ReactNode {
const { children, className, ...rest } = props;
@@ -132,29 +164,7 @@ export function MinimalAudioSkin(props: MinimalAudioSkinProps): ReactNode {
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
-
-
-
-
-
-
-
-
+
diff --git a/packages/react/src/presets/audio/skin.tailwind.tsx b/packages/react/src/presets/audio/skin.tailwind.tsx
index e475f61d..9a23b086 100644
--- a/packages/react/src/presets/audio/skin.tailwind.tsx
+++ b/packages/react/src/presets/audio/skin.tailwind.tsx
@@ -97,6 +97,38 @@ function PlayLabel(): ReactNode {
return paused ? <>Play> : <>Pause>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+ }>
+ }>
+ } />
+
+ } />
+
+
+
+ );
+}
+
/* ------------------------------------------ Skin ------------------------------------------- */
export function AudioSkinTailwind(props: AudioSkinProps): ReactNode {
@@ -188,33 +220,7 @@ export function AudioSkinTailwind(props: AudioSkinProps): ReactNode {
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
- }
- >
- }>
- } />
-
- } />
-
-
-
+
diff --git a/packages/react/src/presets/audio/skin.tsx b/packages/react/src/presets/audio/skin.tsx
index 3ce8cb06..074bcc6a 100644
--- a/packages/react/src/presets/audio/skin.tsx
+++ b/packages/react/src/presets/audio/skin.tsx
@@ -36,6 +36,38 @@ function PlayLabel(): ReactNode {
return paused ? <>Play> : <>Pause>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
export function AudioSkin(props: AudioSkinProps): ReactNode {
const { children, className, ...rest } = props;
@@ -125,29 +157,7 @@ export function AudioSkin(props: AudioSkinProps): ReactNode {
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
-
-
-
-
-
-
-
-
+
diff --git a/packages/react/src/presets/video/minimal-skin.tailwind.tsx b/packages/react/src/presets/video/minimal-skin.tailwind.tsx
index 2b0a5db4..5adbcb29 100644
--- a/packages/react/src/presets/video/minimal-skin.tailwind.tsx
+++ b/packages/react/src/presets/video/minimal-skin.tailwind.tsx
@@ -144,6 +144,38 @@ function FullscreenLabel(): ReactNode {
return fullscreen ? <>Exit fullscreen> : <>Enter fullscreen>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+ }>
+ }>
+ } />
+
+ } />
+
+
+
+ );
+}
+
/* ------------------------------------------ Skin ------------------------------------------- */
export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNode {
@@ -270,33 +302,7 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
- }
- >
- }>
- } />
-
- } />
-
-
-
+
Exit fullscreen> : <>Enter fullscreen>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
const { children, className, poster, ...rest } = props;
@@ -196,29 +228,7 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
-
-
-
-
-
-
-
-
+
Exit fullscreen> : <>Enter fullscreen>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+ }>
+ }>
+ } />
+
+ } />
+
+
+
+ );
+}
+
/* ------------------------------------------ Skin ------------------------------------------- */
export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
@@ -263,33 +295,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
- }
- >
- }>
- } />
-
- } />
-
-
-
+
Exit fullscreen> : <>Enter fullscreen>;
}
+function VolumePopover(): ReactNode {
+ const volumeUnsupported = usePlayer((s) => s.volumeAvailability === 'unsupported');
+
+ const muteButton = (
+ (
+
+ )}
+ />
+ );
+
+ if (volumeUnsupported) return muteButton;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
export function VideoSkin(props: VideoSkinProps): ReactNode {
const { children, className, poster, ...rest } = props;
@@ -189,29 +221,7 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
Toggle playback rate
-
- (
-
- )}
- />
- }
- />
-
-
-
-
-
-
-
-
-
+