From affa32cbaabc26ccb3add3d9115aa6e2244e0a6e Mon Sep 17 00:00:00 2001 From: claude-stand Date: Thu, 2 Jul 2026 16:28:03 +0300 Subject: [PATCH 1/3] fix(ui): collapse global sidebar to a drawer below 992px (tablet layout overflow) At tablet widths (~768px) the fixed ~300px global sidebar stayed pinned, leaving too little room for content: the settings tables (Members etc.) overflowed the offset content area and pushed the Role/actions columns off-screen with no horizontal scroll (unreachable). Raise the AppShell navbar (and page aside) breakpoint from `sm` (768px) to `md` (992px) so the whole tablet band uses the toggle drawer (closed by default) and content gets the full width. Verified with Playwright screenshots: 768px settings/members now fits all columns (table right 736<768, no overflow); desktop (>=992px) unchanged (sidebar pinned, content offset); mobile unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/layouts/global/global-app-shell.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/client/src/components/layouts/global/global-app-shell.tsx b/apps/client/src/components/layouts/global/global-app-shell.tsx index 41d3886f..5bff0bab 100644 --- a/apps/client/src/components/layouts/global/global-app-shell.tsx +++ b/apps/client/src/components/layouts/global/global-app-shell.tsx @@ -88,7 +88,13 @@ export default function GlobalAppShell({ header={{ height: 45 }} navbar={{ width: isSpaceRoute ? sidebarWidth : 300, - breakpoint: "sm", + // `md` (not `sm`): below 992px the fixed ~300px sidebar leaves too little + // room for content — the settings tables (Members/…) overflow the offset + // content area on tablet (~768px) and clip the Role/actions columns + // off-screen with no horizontal scroll. Collapsing the navbar to a toggle + // drawer across the whole tablet band frees the full width for content + // (the mobile drawer is closed by default, so nothing overlaps on load). + breakpoint: "md", collapsed: { mobile: !mobileOpened, desktop: !desktopOpened, @@ -97,7 +103,7 @@ export default function GlobalAppShell({ aside={ isPageRoute && { width: 420, - breakpoint: "sm", + breakpoint: "md", collapsed: { mobile: !isAsideOpen, desktop: !isAsideOpen }, } } -- 2.52.0 From fa439d7c7b34a0cb07afd6cbece1313f6af55ba4 Mon Sep 17 00:00:00 2001 From: claude-stand Date: Thu, 2 Jul 2026 18:19:59 +0300 Subject: [PATCH 2/3] fix(ui): match sidebar toggle breakpoint to navbar (md) so the tablet drawer opens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the navbar sm->md change on this branch: the two header sidebar toggles were still gated at sm, so in the 768-991 band the DESKTOP toggle was shown while the navbar used the MOBILE drawer collapse state — clicking it flipped the wrong atom and the drawer could not be opened (sidebar unreachable at 768/820, caught by QA). Gate the mobile toggle hiddenFrom=md and the desktop toggle visibleFrom=md so the mobile toggle drives the drawer across the whole tablet band. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/layouts/global/app-header.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/client/src/components/layouts/global/app-header.tsx b/apps/client/src/components/layouts/global/app-header.tsx index 96b0a75d..bf1add95 100644 --- a/apps/client/src/components/layouts/global/app-header.tsx +++ b/apps/client/src/components/layouts/global/app-header.tsx @@ -53,7 +53,13 @@ export function AppHeader() { aria-label={t("Sidebar toggle")} opened={mobileOpened} onClick={toggleMobile} - hiddenFrom="sm" + // Must match the AppShell navbar breakpoint (md). The navbar + // collapses to the MOBILE drawer below md, so the mobile toggle + // (which flips mobileOpened) must be the one visible across the + // whole md navbar change). + hiddenFrom="md" size="sm" /> @@ -63,7 +69,7 @@ export function AppHeader() { aria-label={t("Sidebar toggle")} opened={desktopOpened} onClick={toggleDesktop} - visibleFrom="sm" + visibleFrom="md" size="sm" /> -- 2.52.0 From 731a4f0dca23e7562bc01a5e3384c3c852e1fa2f Mon Sep 17 00:00:00 2001 From: agent_coder Date: Thu, 2 Jul 2026 22:02:37 +0300 Subject: [PATCH 3/3] refactor(#292 review): extract NAVBAR_COLLAPSE_BREAKPOINT constant (F2) The AppShell navbar breakpoint and both burger toggles' hiddenFrom/visibleFrom must be equal, or the sidebar becomes unreachable on tablet widths (the round-1 regression). A comment guarded that before; now a shared const does. Add NAVBAR_COLLAPSE_BREAKPOINT='md' to sidebar-atom.ts and reference it from the navbar breakpoint (global-app-shell) + both toggles (app-header). aside.breakpoint and the sm brand/search gates are intentionally separate contracts, left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/client/src/components/layouts/global/app-header.tsx | 5 +++-- .../src/components/layouts/global/global-app-shell.tsx | 3 ++- .../components/layouts/global/hooks/atoms/sidebar-atom.ts | 7 +++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/client/src/components/layouts/global/app-header.tsx b/apps/client/src/components/layouts/global/app-header.tsx index bf1add95..1cc4b5b6 100644 --- a/apps/client/src/components/layouts/global/app-header.tsx +++ b/apps/client/src/components/layouts/global/app-header.tsx @@ -12,6 +12,7 @@ import TopMenu from "@/components/layouts/global/top-menu.tsx"; import { Link } from "react-router-dom"; import { useAtom } from "jotai"; import { + NAVBAR_COLLAPSE_BREAKPOINT, desktopSidebarAtom, mobileSidebarAtom, } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts"; @@ -59,7 +60,7 @@ export function AppHeader() { // whole md navbar change). - hiddenFrom="md" + hiddenFrom={NAVBAR_COLLAPSE_BREAKPOINT} size="sm" /> @@ -69,7 +70,7 @@ export function AppHeader() { aria-label={t("Sidebar toggle")} opened={desktopOpened} onClick={toggleDesktop} - visibleFrom="md" + visibleFrom={NAVBAR_COLLAPSE_BREAKPOINT} size="sm" /> diff --git a/apps/client/src/components/layouts/global/global-app-shell.tsx b/apps/client/src/components/layouts/global/global-app-shell.tsx index 5bff0bab..1b41011e 100644 --- a/apps/client/src/components/layouts/global/global-app-shell.tsx +++ b/apps/client/src/components/layouts/global/global-app-shell.tsx @@ -6,6 +6,7 @@ import SettingsSidebar from "@/components/settings/settings-sidebar.tsx"; import { useAtom } from "jotai"; import { APP_NAVBAR_ID, + NAVBAR_COLLAPSE_BREAKPOINT, asideStateAtom, desktopSidebarAtom, mobileSidebarAtom, @@ -94,7 +95,7 @@ export default function GlobalAppShell({ // off-screen with no horizontal scroll. Collapsing the navbar to a toggle // drawer across the whole tablet band frees the full width for content // (the mobile drawer is closed by default, so nothing overlaps on load). - breakpoint: "md", + breakpoint: NAVBAR_COLLAPSE_BREAKPOINT, collapsed: { mobile: !mobileOpened, desktop: !desktopOpened, diff --git a/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts b/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts index f85f0fbc..c87c698c 100644 --- a/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts +++ b/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts @@ -7,6 +7,13 @@ import { atom } from "jotai"; // would create a shell -> chat-window -> shell import cycle). export const APP_NAVBAR_ID = "app-shell-navbar"; +// Single source of truth for the navbar collapse breakpoint. The AppShell navbar +// `breakpoint` and BOTH burger toggles' `hiddenFrom`/`visibleFrom` MUST use this +// exact value: if they drift, the sidebar becomes unreachable on tablet widths +// (the round-1 regression of #292). Kept here so the shell and the header share +// one constant the compiler enforces, instead of three hand-synced string literals. +export const NAVBAR_COLLAPSE_BREAKPOINT = "md"; + export const mobileSidebarAtom = atom(false); export const desktopSidebarAtom = atomWithWebStorage( -- 2.52.0