fix(ui): toasts raised while a full-screen player is open show inside it, not under its scrim

The app's Toaster lives in the content column, which is a stacking context (its `@container` implies
layout containment), so both full-screen players — portalled to <body> at the same z-index — painted
over every toast. Round 1's "a failed Remove is reported" fix was therefore still silent inside the
player: its toast sat under an 80% black, blurred scrim (elementFromPoint returned the scrim), and a
click on it closed the player.

An overlay now mounts its own `<Toaster overlay />`, and exactly one host shows the toasts: the most
recently mounted overlay, else the app's. Same component, same look — and inside the aria-modal
dialog, where a screen reader can still reach it. A click on a toast inside an overlay stays there.
Both players get one (the Plex player's also works in native fullscreen, where nothing outside the
fullscreen element is drawn). Verified on localdev: the removal failure reads above the scrim,
bottom-left, and clicking it leaves the player open.
This commit is contained in:
2026-09-11 03:18:55 +02:00
parent 645d14f45d
commit 6de2cd950d
5 changed files with 118 additions and 3 deletions
+4
View File
@@ -48,6 +48,7 @@ import { fullscreenElement } from "../lib/fullscreen";
import { dispatch, isActivationTarget, useKeyScope } from "../lib/keymap";
import { closeThenNavigate, useBackToClose } from "../lib/history";
import ChannelNameLink from "./ChannelNameLink";
import Toaster from "./ui/Toaster";
import { afterRemoval } from "../lib/playerQueue";
import {
useNativeControlsYield,
@@ -960,6 +961,9 @@ export default function PlayerModal({
</button>
)}
</div>
{/* Toasts raised while the player is open (a failed Remove, a lost connection) — the app's own
Toaster is underneath this scrim. See ui/Toaster. */}
<Toaster overlay />
</div>,
document.body,
);