fix(ux): dock filter tab flush to the nav + reliable full-viewport plex backdrop
Follow-ups on the 7-item batch: - The slim nav slot was still w-20 while its card is w-14, leaving a ~24px dead gap the filter tab/bar couldn't dock against. Slot is now w-14 (exactly the card width), so the filter's flat-left tab sits flush on the rail's right edge (measured gap 0), both collapsed and expanded. - The Plex art backdrop was invisible: a position:fixed layer at z-index -1 is painted BEHIND the body's opaque background here, so nothing showed. Paint the art on the app root (#root) instead — it's exactly the viewport and a real ancestor of the transparent nav/header/content, so the art fills the whole page edge-to-edge and the glass chrome floats over it (verified in-browser).
This commit is contained in:
@@ -201,7 +201,8 @@ export default function NavSidebar({
|
||||
// Layout slot: reserves only the slim footprint when unpinned, so the expanded rail (below)
|
||||
// overlays content instead of pushing it. Pinned reserves the full width (in-flow).
|
||||
// Slim icon rail, flush against the left viewport edge (only top/bottom margin), never grows.
|
||||
<div className="relative shrink-0 w-20">
|
||||
// The slot is exactly the card's width so the filter tab/bar docks flush against its right edge.
|
||||
<div className="relative shrink-0 w-14">
|
||||
<nav
|
||||
className="glass absolute top-0 bottom-0 left-0 z-rail rounded-2xl flex flex-col py-3 my-3 w-14 px-2"
|
||||
aria-label={t("nav.primary")}
|
||||
|
||||
@@ -78,24 +78,37 @@ export function Filterable({
|
||||
}
|
||||
|
||||
// Paint the item's art as a faint FULL-VIEWPORT backdrop (HTPC-style) so the glass chrome floats over
|
||||
// it. A dedicated `position:fixed inset-0` layer at z-index -1 sits BEHIND the (transparent / glassy)
|
||||
// app content — so the art fills the whole page edge-to-edge, under the nav rail and floating header
|
||||
// too, instead of only the <main> box (which is inset by the rail, the header and the scrollbar
|
||||
// gutter). Removed on unmount / when disabled / when there's no art.
|
||||
// it. Painted on the app root (#root) — which is exactly the viewport and sits BEHIND the transparent
|
||||
// nav / header / content — so the art fills the whole page edge-to-edge (under the rail + floating
|
||||
// header too), not just the inset <main> box. (A `position:fixed` layer at negative z-index does NOT
|
||||
// work here: the body's opaque background is painted over it, so it's invisible; #root's background,
|
||||
// being a real ancestor of the content, is the reliable full-viewport layer.) `attachment:fixed`
|
||||
// keeps it viewport-anchored while the content scrolls. Cleared on unmount / disable / no art.
|
||||
export function useArtBackdrop(art: string | null | undefined, enabled: boolean) {
|
||||
useLayoutEffect(() => {
|
||||
if (!enabled || !art) return;
|
||||
const el = document.createElement("div");
|
||||
el.setAttribute("data-plex-art-backdrop", "");
|
||||
el.style.cssText =
|
||||
"position:fixed;inset:0;z-index:-1;pointer-events:none;" +
|
||||
"background-size:cover;background-position:center 20%;background-repeat:no-repeat;";
|
||||
// The faint --bg overlay keeps it subtle and text-safe (same tuning the old <main> paint used).
|
||||
el.style.backgroundImage =
|
||||
`linear-gradient(color-mix(in srgb, var(--bg) 60%, transparent), ` +
|
||||
`color-mix(in srgb, var(--bg) 64%, transparent)), url("${art}")`;
|
||||
document.body.appendChild(el);
|
||||
return () => el.remove();
|
||||
const el = document.getElementById("root");
|
||||
if (!el) return;
|
||||
const s = el.style;
|
||||
const clear = () => {
|
||||
s.backgroundImage = "";
|
||||
s.backgroundSize = "";
|
||||
s.backgroundPosition = "";
|
||||
s.backgroundRepeat = "";
|
||||
s.backgroundAttachment = "";
|
||||
};
|
||||
if (enabled && art) {
|
||||
// The faint --bg overlay keeps it subtle and text-safe (same tuning the old paint used).
|
||||
s.backgroundImage =
|
||||
`linear-gradient(color-mix(in srgb, var(--bg) 60%, transparent), ` +
|
||||
`color-mix(in srgb, var(--bg) 64%, transparent)), url("${art}")`;
|
||||
s.backgroundSize = "cover";
|
||||
s.backgroundPosition = "center 20%";
|
||||
s.backgroundRepeat = "no-repeat";
|
||||
s.backgroundAttachment = "fixed";
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
return clear;
|
||||
}, [art, enabled]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user