diff --git a/services/frontend/src/components/shell/nav-rail.tsx b/services/frontend/src/components/shell/nav-rail.tsx index 4faa704b..040506d9 100644 --- a/services/frontend/src/components/shell/nav-rail.tsx +++ b/services/frontend/src/components/shell/nav-rail.tsx @@ -1,7 +1,8 @@ "use client"; import { usePathname } from "next/navigation"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; import { isActivePath, navItems } from "@/lib/navigation"; import { cn } from "@/lib/utils"; @@ -16,11 +17,35 @@ function NavItem({ active: boolean; Icon: React.ComponentType>; }) { + const itemRef = useRef(null); + const [tipPos, setTipPos] = useState<{ top: number; left: number } | null>(null); + const [hovering, setHovering] = useState(false); + + // Measure the item and position the tooltip via a Portal on document.body + // so it always paints above the main content (which has its own stacking + // contexts from backdrop-blur panels). + const showTooltip = () => { + const el = itemRef.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + setTipPos({ top: rect.top + rect.height / 2, left: rect.right + 10 }); + setHovering(true); + }; + const hideTooltip = () => { + setHovering(false); + setTipPos(null); + }; + return ( )} - - {label} - + + {/* Tooltip — portalled to body so it never hides behind content */} + {hovering && + tipPos && + typeof document !== "undefined" && + createPortal( + + {label} + , + document.body, + )} ); }