diff --git a/components/sidebar.tsx b/components/sidebar.tsx index 2425cd3..e1b387c 100644 --- a/components/sidebar.tsx +++ b/components/sidebar.tsx @@ -52,22 +52,47 @@ export function SidebarProvider({ children }: { children: React.ReactNode }) { return ( {/* transitions gated on mount: no slide-in flash on page load */} -
{children}
+
{children}
); } -/** Hamburger for page headers — only visible when the sidebar is closed. */ +/** Hamburger for page headers — only visible when the sidebar is closed. + * + * Owner, round 6c: the toggle is ALWAYS MOUNTED, owning a slot whose + * width+margin animate in LOCKSTEP with the sidebar's own width + * transition (same duration-200, same easing, both gated on mount) — + * before, it mounted/unmounted in one frame, so its 48px footprint + * (button + gap) popped and dragged the title and chips sideways while + * the sidebar was still mid-slide. When open the slot is exactly zero + * (w-0 -mr-3: width 0, the negative margin cancels the header's gap-3), + * so resting layouts are pixel-identical to the old conditional render. + * The icon fades with the slot and is clipped by it as it collapses — + * no popping in either direction. */ export function SidebarToggle() { const { open, toggle } = useSidebar(); - if (open) return null; return ( - + + + ); }