From d7b23b3c08332cef8b9e350585d141ff44c97415 Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Mon, 18 May 2026 23:35:08 +0300 Subject: [PATCH] fix(ui): stable play/enqueue hover on album rails and mainstage cards (#787) * fix(ui): stable play/enqueue hover on album rails and mainstage cards Horizontal Home rails (Discover): drop content-visibility:auto and scroll-snap, pin controls to cover hover, dim overlay via background. Grids and song cards: pointer-events on overlays so WebKitGTK/Wayland GPU does not lose :hover. * chore: CHANGELOG and credits for PR #787 --- CHANGELOG.md | 9 +++ src/config/settingsCredits.ts | 1 + src/styles/components/album-row-container.css | 73 ++++++++++++++++--- src/styles/themes/reset.css | 6 ++ src/styles/tracks/song-card.css | 14 +++- 5 files changed, 91 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65b18836..05c78fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +### Mainstage — album rail hover controls + +**By [@cucadmuh](https://github.com/cucadmuh), PR [#787](https://github.com/Psychotoxical/psysonic/pull/787)** + +* **Home horizontal album rails (Discover, etc.):** play/enqueue overlay no longer flickers on WebKitGTK + Wayland GPU; cover zoom stays smooth like the All Albums grid. +* **Album grids and song rails:** overlay `pointer-events` so the dim layer does not steal hover from the card. + + + ### Radio — paused streams stay paused **By [@Psychotoxical](https://github.com/Psychotoxical), reported by drelabre on GitHub, PR [#786](https://github.com/Psychotoxical/psysonic/pull/786)** diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index edc31709..5815061e 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -120,6 +120,7 @@ const CONTRIBUTOR_ENTRIES = [ 'Multi-server: Lucky Mix and Now Playing keep browsed server; queue metadata via apiForServer (PR #768)', 'Linux: session GDK/WebKit mitigations, in-page library scroll, Wayland text presets (PR #731)', 'In-page browse: virtual list scrollMargin + CachedImage load priority; Artists infinite-scroll batching (PR #783)', + 'Home album rails: stable play/enqueue hover on WebKitGTK/Wayland (PR #787)', ], }, { diff --git a/src/styles/components/album-row-container.css b/src/styles/components/album-row-container.css index 7d5b8716..bd993072 100644 --- a/src/styles/components/album-row-container.css +++ b/src/styles/components/album-row-container.css @@ -52,7 +52,7 @@ gap: var(--space-4); overflow-x: auto; padding-bottom: var(--space-4); - scroll-snap-type: x mandatory; + scroll-snap-type: none; scroll-behavior: smooth; /* Hide scrollbar for Webkit */ -webkit-overflow-scrolling: touch; @@ -95,13 +95,58 @@ .album-grid .album-card, .album-grid .artist-card { flex: 0 0 clamp(140px, 15vw, 180px); - scroll-snap-align: start; } -/* Horizontal rails can hold many off-screen cards; skip their paint/layout until visible. */ +/* Horizontal rails (Home Discover, etc.): WebKitGTK + scroll — no content-visibility:auto, + no .card transition, hover pinned to cover, dim via overlay background not opacity. */ .album-grid .album-card { - content-visibility: auto; - contain-intrinsic-size: 0 240px; + contain: none; + transform: none; + transition: none; +} + +.album-grid .album-card:hover { + box-shadow: inset 0 0 0 1px var(--border-subtle); +} + +.album-grid .album-card-cover { + contain: layout style; + transform: none; +} + +.album-grid .album-card-cover img { + transform-origin: center center; + transform: scale(1); + transition: opacity 0.15s ease, transform 350ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.album-grid .album-card-cover:hover img { + transform: scale(1.02); +} + +.album-grid .album-card .album-card-play-overlay { + opacity: 1; + background-color: transparent; + transition: none; +} + +.album-grid .album-card .album-card-details-btn { + visibility: hidden; + opacity: 0; + pointer-events: none; + transition: none; +} + +.album-grid .album-card-cover:hover .album-card-play-overlay, +.album-grid .album-card-cover:focus-within .album-card-play-overlay { + background-color: rgba(0, 0, 0, 0.4); +} + +.album-grid .album-card-cover:hover .album-card-details-btn, +.album-grid .album-card-cover:focus-within .album-card-details-btn { + visibility: visible; + opacity: 1; + pointer-events: auto; } .album-grid .artist-card { @@ -121,7 +166,6 @@ border: 1px dashed var(--border); color: var(--text-secondary); cursor: pointer; - scroll-snap-align: start; transition: all var(--transition-base); } @@ -177,10 +221,13 @@ justify-content: center; gap: 8px; opacity: 0; + pointer-events: none; transition: opacity var(--transition-base); } -.album-card:hover .album-card-play-overlay { +.album-card:hover .album-card-play-overlay, +.album-card:focus-within .album-card-play-overlay, +.album-card:has(.album-card-details-btn:hover) .album-card-play-overlay { opacity: 1; } @@ -196,12 +243,20 @@ font-weight: 600; font-size: 13px; cursor: pointer; - transition: transform var(--transition-fast), background var(--transition-fast); + pointer-events: none; + transition: filter var(--transition-fast), background var(--transition-fast); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6); } +.album-card:hover .album-card-details-btn, +.album-card:focus-within .album-card-details-btn, +.because-card:hover .album-card-details-btn, +.because-card:focus-within .album-card-details-btn { + pointer-events: auto; +} + .album-card-details-btn:hover { - transform: scale(1.05); + filter: brightness(1.08); background: var(--accent); } diff --git a/src/styles/themes/reset.css b/src/styles/themes/reset.css index 021fba76..b785f540 100644 --- a/src/styles/themes/reset.css +++ b/src/styles/themes/reset.css @@ -89,3 +89,9 @@ html[data-platform="linux"][data-linux-session="wayland"]:not(.no-compositing)[d text-rendering: geometricPrecision; } +/* Card play/enqueue overlays: opacity fade + GL layers can flutter on hover (mainstage). */ +html[data-platform="linux"][data-linux-session="wayland"]:not(.no-compositing) + :is(.album-card-play-overlay, .song-card-play-overlay) { + transition: none; +} + diff --git a/src/styles/tracks/song-card.css b/src/styles/tracks/song-card.css index 7fa54714..b879a4ce 100644 --- a/src/styles/tracks/song-card.css +++ b/src/styles/tracks/song-card.css @@ -46,10 +46,12 @@ justify-content: center; gap: 6px; opacity: 0; + pointer-events: none; transition: opacity var(--transition-base); } -.song-card:hover .song-card-play-overlay { +.song-card:hover .song-card-play-overlay, +.song-card:focus-within .song-card-play-overlay { opacity: 1; } @@ -64,12 +66,18 @@ align-items: center; justify-content: center; cursor: pointer; - transition: transform var(--transition-fast); + pointer-events: none; + transition: filter var(--transition-fast); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); } +.song-card:hover .song-card-action-btn, +.song-card:focus-within .song-card-action-btn { + pointer-events: auto; +} + .song-card-action-btn:hover { - transform: scale(1.08); + filter: brightness(1.08); } .song-card-info {