fix(player): the Plex bars hold while hovered, volume is the native slider, tooltips no longer re-layer the row (E28)

- F22: the control bar faded out from under a mouse parked on it; the hide timer
  now waits while the pointer is on either band and re-arms on leave (test seen
  RED by disabling the hold).
- The hand-drawn volume bar broke DESIGN.md A6 (native sliders): it had no hover,
  focus, keyboard or screen-reader state. It is the Slider primitive now; a
  pointer drag blurs it so Space and the arrows keep driving the player.
- The control tooltips are composited from mount (will-change: opacity): a fade
  that promoted one on hover re-layered its neighbours under the player's
  scale(1.25), and the whole row shimmered.
This commit is contained in:
2026-09-25 14:48:45 +02:00
parent e9ccd9bc98
commit 62dbf35ea3
8 changed files with 106 additions and 51 deletions
@@ -133,6 +133,10 @@ export function Ctrl({
}) {
const pos =
align === "start" ? "left-0" : align === "end" ? "right-0" : "left-1/2 -translate-x-1/2";
// `will-change-[opacity]` keeps each tooltip on its own compositor layer from the start. Without it
// the browser promotes a tooltip only while its fade runs, and inside the player's scale(1.25)
// the neighbouring glyphs got re-layered with it: every button in the row shimmered as the
// pointer crossed one (owner, E28 audit).
return (
<div className="relative flex group/ctrl">
<IconButton
@@ -147,7 +151,7 @@ export function Ctrl({
{children}
</IconButton>
<span
className={`pointer-events-none absolute bottom-full ${pos} mb-2 whitespace-nowrap rounded-sm bg-black/90 px-2 py-1 text-2xs text-white opacity-0 transition group-hover/ctrl:opacity-100`}
className={`pointer-events-none absolute bottom-full ${pos} mb-2 whitespace-nowrap rounded-sm bg-black/90 px-2 py-1 text-2xs text-white opacity-0 will-change-[opacity] transition group-hover/ctrl:opacity-100`}
>
{label}
</span>