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
This commit is contained in:
cucadmuh
2026-05-18 23:35:08 +03:00
committed by GitHub
parent d7abf9be3b
commit d7b23b3c08
5 changed files with 91 additions and 12 deletions
+9
View File
@@ -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 ### 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)** **By [@Psychotoxical](https://github.com/Psychotoxical), reported by drelabre on GitHub, PR [#786](https://github.com/Psychotoxical/psysonic/pull/786)**
+1
View File
@@ -120,6 +120,7 @@ const CONTRIBUTOR_ENTRIES = [
'Multi-server: Lucky Mix and Now Playing keep browsed server; queue metadata via apiForServer (PR #768)', '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)', '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)', '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)',
], ],
}, },
{ {
+64 -9
View File
@@ -52,7 +52,7 @@
gap: var(--space-4); gap: var(--space-4);
overflow-x: auto; overflow-x: auto;
padding-bottom: var(--space-4); padding-bottom: var(--space-4);
scroll-snap-type: x mandatory; scroll-snap-type: none;
scroll-behavior: smooth; scroll-behavior: smooth;
/* Hide scrollbar for Webkit */ /* Hide scrollbar for Webkit */
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
@@ -95,13 +95,58 @@
.album-grid .album-card, .album-grid .album-card,
.album-grid .artist-card { .album-grid .artist-card {
flex: 0 0 clamp(140px, 15vw, 180px); 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 { .album-grid .album-card {
content-visibility: auto; contain: none;
contain-intrinsic-size: 0 240px; 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 { .album-grid .artist-card {
@@ -121,7 +166,6 @@
border: 1px dashed var(--border); border: 1px dashed var(--border);
color: var(--text-secondary); color: var(--text-secondary);
cursor: pointer; cursor: pointer;
scroll-snap-align: start;
transition: all var(--transition-base); transition: all var(--transition-base);
} }
@@ -177,10 +221,13 @@
justify-content: center; justify-content: center;
gap: 8px; gap: 8px;
opacity: 0; opacity: 0;
pointer-events: none;
transition: opacity var(--transition-base); 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; opacity: 1;
} }
@@ -196,12 +243,20 @@
font-weight: 600; font-weight: 600;
font-size: 13px; font-size: 13px;
cursor: pointer; 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); 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 { .album-card-details-btn:hover {
transform: scale(1.05); filter: brightness(1.08);
background: var(--accent); background: var(--accent);
} }
+6
View File
@@ -89,3 +89,9 @@ html[data-platform="linux"][data-linux-session="wayland"]:not(.no-compositing)[d
text-rendering: geometricPrecision; 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;
}
+11 -3
View File
@@ -46,10 +46,12 @@
justify-content: center; justify-content: center;
gap: 6px; gap: 6px;
opacity: 0; opacity: 0;
pointer-events: none;
transition: opacity var(--transition-base); 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; opacity: 1;
} }
@@ -64,12 +66,18 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; 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); 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 { .song-card-action-btn:hover {
transform: scale(1.08); filter: brightness(1.08);
} }
.song-card-info { .song-card-info {