From 9bb7b57ee832b2994c6e551dd8ad354d5a51fe43 Mon Sep 17 00:00:00 2001 From: stroblme Date: Sat, 22 Aug 2026 12:09:18 +0200 Subject: [PATCH] Centre the offline banner over the content column --- .../components/Common/ConnectionBanner.tsx | 45 +++++++++---------- frontend/src/routes/_layout.tsx | 5 ++- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/Common/ConnectionBanner.tsx b/frontend/src/components/Common/ConnectionBanner.tsx index 82475c9..9c26577 100644 --- a/frontend/src/components/Common/ConnectionBanner.tsx +++ b/frontend/src/components/Common/ConnectionBanner.tsx @@ -1,7 +1,6 @@ import { WifiOff } from "lucide-react" import { AnimatePresence, motion } from "motion/react" import { useSyncExternalStore } from "react" -import { createPortal } from "react-dom" import { ago } from "@/components/Health/queries" import { connectionStore } from "@/lib/connectionStore" @@ -17,10 +16,11 @@ import { isPortal } from "@/lib/portal" * stale readings with a timestamp are more use than an empty page, which is * why the banner leads with when we last heard anything. * - * Drawn on `document.body` rather than where it is mounted. `position: fixed` - * resolves against the nearest ancestor carrying a transform, a filter or a - * `backdrop-filter`, and the shell floats several of those — inside them the - * banner centres on the content column instead of the viewport. + * Centred over the content column rather than the window: it belongs to what + * is being read, not to the shell around it. That is what `sticky` inside the + * content column buys — no sidebar width to track and nothing to recompute + * when the sidebar collapses — and `h-0` keeps it out of the flow, so nothing + * below it moves when the banner appears. */ export function ConnectionBanner() { const connection = useSyncExternalStore( @@ -30,19 +30,19 @@ export function ConnectionBanner() { ) if (!isPortal()) return null - return createPortal( - - {connection.offline && ( - -
+ return ( +
+ + {connection.offline && ( + Installation offline @@ -50,10 +50,9 @@ export function ConnectionBanner() { ? `last seen ${ago(connection.lastSeen / 1000)} — reconnecting…` : "reconnecting…"} -
- - )} - , - document.body, + + )} + +
) } diff --git a/frontend/src/routes/_layout.tsx b/frontend/src/routes/_layout.tsx index 432d9ba..fba8a81 100644 --- a/frontend/src/routes/_layout.tsx +++ b/frontend/src/routes/_layout.tsx @@ -29,10 +29,11 @@ function Layout() { return ( - {/* Renders nothing unless this page is served through a portal. */} - + {/* Renders nothing unless this page is served through a portal. + Inside the inset so it centres over the content, not the window. */} + {/* The sidebar carries its own collapse control; a phone has no sidebar on screen to carry it. */}