diff --git a/src/Web/autopilot-monitor-web/app/globals.css b/src/Web/autopilot-monitor-web/app/globals.css index 9369131e8..aaaedf795 100644 --- a/src/Web/autopilot-monitor-web/app/globals.css +++ b/src/Web/autopilot-monitor-web/app/globals.css @@ -22,6 +22,22 @@ html { drawer + overlay) keep sizing against the viewport. */ overflow-x: clip; scrollbar-gutter: stable; + /* Anchor jumps and scrollIntoView stop below the view-as bar as well (see the next block). */ + scroll-padding-top: var(--view-as-offset); +} + +/* ===== View-as bar (components/ViewAsBar.tsx) ===== + While a tab views a tenant as its member, or says once why that was refused, the bar sticks above the + navbar at a fixed height. Everything positioned against the top of the viewport adds --view-as-offset — + the navbar's sticky top, the desktop sidebar, sticky content, anchor scrolling — so nothing slides under + the bar: the offset is the bar's height while it is shown and 0 otherwise. Offsets computed in JS read + the bar's height through viewAsBarHeight() (lib/viewAs.ts). */ +:root { + --view-as-bar-height: 2.25rem; + --view-as-offset: 0px; +} +:root:has([data-view-as-bar]) { + --view-as-offset: var(--view-as-bar-height); } :root { diff --git a/src/Web/autopilot-monitor-web/app/push/PushNav.tsx b/src/Web/autopilot-monitor-web/app/push/PushNav.tsx index 430156eac..cf93802de 100644 --- a/src/Web/autopilot-monitor-web/app/push/PushNav.tsx +++ b/src/Web/autopilot-monitor-web/app/push/PushNav.tsx @@ -12,7 +12,7 @@ const ITEMS = [ export function PushNav() { const pathname = (usePathname() ?? "/push").replace(/\/$/, "") || "/push"; return ( -
+
diff --git a/src/Web/autopilot-monitor-web/components/GlobalSidebar.tsx b/src/Web/autopilot-monitor-web/components/GlobalSidebar.tsx index 7e55b3d4e..9bf336b9d 100644 --- a/src/Web/autopilot-monitor-web/components/GlobalSidebar.tsx +++ b/src/Web/autopilot-monitor-web/components/GlobalSidebar.tsx @@ -13,6 +13,7 @@ import { useAdminMode } from "../hooks/useAdminMode"; import { useMediaQuery } from "../hooks/useMediaQuery"; import { DOCS_URL } from "@/lib/config"; import { isPushAppPath } from "@/lib/push/pushAppPath"; +import { viewAsBarHeight } from "@/lib/viewAs"; // Sidebar pixel widths export const SIDEBAR_PX: Record = { @@ -75,7 +76,8 @@ export function GlobalSidebar({ children }: { children: ReactNode }) { } } }, - { rootMargin: "-80px 0px -60% 0px", threshold: 0 }, + // The top 80 px are the navbar plus a margin; a view-as bar above the navbar adds its own height. + { rootMargin: `-${80 + viewAsBarHeight()}px 0px -60% 0px`, threshold: 0 }, ); observerRef.current = observer; @@ -104,7 +106,8 @@ export function GlobalSidebar({ children }: { children: ReactNode }) { const scrollTo = useCallback((id: string) => { const el = document.getElementById(id); if (el) { - const y = el.getBoundingClientRect().top + window.scrollY - 90; + // 90 px keep the section clear of the navbar; a view-as bar above the navbar adds its own height. + const y = el.getBoundingClientRect().top + window.scrollY - 90 - viewAsBarHeight(); window.scrollTo({ top: y, behavior: "smooth" }); } setActiveSectionId(id); @@ -726,7 +729,7 @@ export function GlobalSidebar({ children }: { children: ReactNode }) { {/* ===== Desktop: fixed sidebar ===== */}