diff --git a/internal/templates/watch.templ b/internal/templates/watch.templ
index 72f2b7f..268e2b3 100644
--- a/internal/templates/watch.templ
+++ b/internal/templates/watch.templ
@@ -232,7 +232,7 @@ templ VideoPlayer(data WatchPageData) {
-
+
{
const iconPause = container.querySelector('[data-icon-pause]') as SVGElement
const muteBtn = container.querySelector('[data-mute]') as HTMLButtonElement
const volumeWrap = container.querySelector('[data-volume-wrap]') as HTMLElement
+ const volumePanel = container.querySelector('.volume-panel') as HTMLElement
const volumeRange = container.querySelector('[data-volume-range]') as HTMLInputElement
const iconVolume = container.querySelector('[data-icon-volume]') as SVGElement
const iconMuted = container.querySelector('[data-icon-muted]') as SVGElement
@@ -501,25 +502,35 @@ const initPlayer = (): void => {
showControls()
})
- muteBtn?.addEventListener('mouseenter', () => {
- muteBtn.classList.add('show-volume')
- isHoveringVolume = true
- showControls()
- })
+ const setVolumePanelOpen = (isOpen: boolean): void => {
+ if (volumePanel) {
+ volumePanel.classList.toggle('is-visible', isOpen)
+ }
+ volumeWrap?.classList.toggle('is-volume-open', isOpen)
+ isHoveringVolume = isOpen
+ if (isOpen) showControls()
+ }
- muteBtn?.addEventListener('mouseleave', () => {
- isHoveringVolume = false
- showControls()
- })
+ const openVolumePanel = (): void => {
+ setVolumePanelOpen(true)
+ }
- volumeWrap?.addEventListener('focusin', () => {
- isHoveringVolume = true
- showControls()
- })
+ const closeVolumePanel = (): void => {
+ setVolumePanelOpen(false)
+ }
- volumeWrap?.addEventListener('focusout', () => {
- isHoveringVolume = false
- showControls()
+ closeVolumePanel()
+
+ muteBtn?.addEventListener('mouseenter', openVolumePanel)
+
+ volumeWrap?.addEventListener('mouseleave', closeVolumePanel)
+
+ volumeWrap?.addEventListener('focusin', openVolumePanel)
+
+ volumeWrap?.addEventListener('focusout', (event: FocusEvent) => {
+ const nextTarget = event.relatedTarget
+ if (nextTarget instanceof Node && volumeWrap.contains(nextTarget)) return
+ closeVolumePanel()
})
backwardBtn?.addEventListener('click', () => seekBy(-10))
diff --git a/static/style.css b/static/style.css
index 054c6b1..6fa6b2e 100644
--- a/static/style.css
+++ b/static/style.css
@@ -75,29 +75,21 @@
box-shadow: 0 0 10px rgba(0, 0, 0, 0.55);
}
-.volume-wrap::before {
- content: '';
- position: absolute;
- left: -10px;
- right: -10px;
- bottom: 100%;
- height: 130px;
-}
-
-.volume-wrap:has([data-mute]:hover) .volume-panel,
-.volume-wrap:has([data-volume-bridge]:hover) .volume-panel,
-.volume-wrap:has(.volume-panel:hover) .volume-panel,
-.volume-wrap:focus-within .volume-panel,
-.volume-panel:hover,
-.volume-panel:focus-within {
+.volume-panel.is-visible {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
-.volume-wrap:has([data-mute]:hover) .volume-underline,
-.volume-wrap:has([data-volume-bridge]:hover) .volume-underline,
-.volume-wrap:has(.volume-panel:hover) .volume-underline,
+.volume-wrap[data-volume-wrap] [data-volume-bridge] {
+ pointer-events: none;
+}
+
+.volume-wrap.is-volume-open[data-volume-wrap] [data-volume-bridge] {
+ pointer-events: auto;
+}
+
+.volume-wrap:has(.volume-panel.is-visible) .volume-underline,
.volume-wrap:focus-within .volume-underline {
opacity: 1;
}