From b53782810f5ff313773b0139a6aa24cfad5a8913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 8 Oct 2026 18:36:45 +0200 Subject: [PATCH] Keep the phone shell on a phone held sideways A phone in landscape is 844 to 932 px wide, past md, so it got the desktop rail and top bar: a sliver of content and no thumb-reachable navigation. Two variants now decide the shell: shell-desktop from md up except on a touch screen at most 520 px tall, shell-mobile otherwise. The sidebar, top bar, bottom nav, the main column's bottom clearance, the Coach button, the selection bar and the two full-height Coach pages all key on them instead of a bare md. --- src/app/coach/conversations/page.tsx | 2 +- src/app/coach/plans/page.tsx | 2 +- src/app/globals.css | 24 +++++++++++++++++++ .../data-list/selection-action-bar.tsx | 2 +- .../__tests__/layout-coach-fab.test.tsx | 4 +++- src/components/insights/layout-coach-fab.tsx | 2 +- src/components/layout/auth-shell.tsx | 4 ++-- src/components/layout/bottom-nav.tsx | 4 ++-- src/components/layout/sidebar-nav.tsx | 2 +- src/components/layout/top-bar.tsx | 9 ++++--- 10 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/app/coach/conversations/page.tsx b/src/app/coach/conversations/page.tsx index 32f0e0ce8..c12927c9c 100644 --- a/src/app/coach/conversations/page.tsx +++ b/src/app/coach/conversations/page.tsx @@ -331,7 +331,7 @@ export default function CoachConversationsPage() { // ~50px on notched devices; restored to match exactly. v1.42 — the // shell now takes that inset above the top bar at every width, so // the desktop height subtracts it too, and `/coach/plans` matches. - className="bg-background -mx-4 -mt-6 -mb-20 flex h-[calc(100dvh-8rem-env(safe-area-inset-top,0px)-env(safe-area-inset-bottom,0px))] min-h-[32rem] flex-col overflow-hidden md:-mx-6 md:h-[calc(100dvh-4rem-env(safe-area-inset-top,0px))]" + className="bg-background shell-desktop:h-[calc(100dvh-4rem-env(safe-area-inset-top,0px))] -mx-4 -mt-6 -mb-20 flex h-[calc(100dvh-8rem-env(safe-area-inset-top,0px)-env(safe-area-inset-bottom,0px))] min-h-[32rem] flex-col overflow-hidden md:-mx-6" > diff --git a/src/app/coach/plans/page.tsx b/src/app/coach/plans/page.tsx index b02fddcdc..4df3e06b7 100644 --- a/src/app/coach/plans/page.tsx +++ b/src/app/coach/plans/page.tsx @@ -296,7 +296,7 @@ export default function CoachPlansPage() { // mobile-only BottomNav band). The status-bar inset of an installed // app comes off as well, at every width: the shell takes it above // the top bar (`shell-safe-area`). - className="bg-background -mx-4 -mt-6 -mb-20 flex h-[calc(100dvh-8rem-env(safe-area-inset-top,0px)-env(safe-area-inset-bottom,0px))] min-h-[32rem] flex-col overflow-hidden md:-mx-6 md:h-[calc(100dvh-4rem-env(safe-area-inset-top,0px))]" + className="bg-background shell-desktop:h-[calc(100dvh-4rem-env(safe-area-inset-top,0px))] -mx-4 -mt-6 -mb-20 flex h-[calc(100dvh-8rem-env(safe-area-inset-top,0px)-env(safe-area-inset-bottom,0px))] min-h-[32rem] flex-col overflow-hidden md:-mx-6" > diff --git a/src/app/globals.css b/src/app/globals.css index 63361cc9f..81e798468 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -58,6 +58,30 @@ } } +/* + * `shell-desktop:` / `shell-mobile:` — which app shell the viewport gets. + * The desktop rail (sidebar, no bottom nav) from `md` up, EXCEPT a touch + * screen at most 520 px tall: a phone held sideways is 844–932 px wide but + * only 390–430 px tall, and the rail plus the top bar left it a sliver of + * content and no thumb-reachable navigation. It keeps the phone shell (top + * bar, bottom nav, capture button) instead. Everything that switches + * between the two shells keys on these, never on a bare `md:`, so the bars + * cannot disagree about which shell is showing. + */ +@custom-variant shell-desktop { + @media (min-width: 48rem) and (pointer: fine), + (min-width: 48rem) and (pointer: none), + (min-width: 48rem) and (min-height: 521px) { + @slot; + } +} + +@custom-variant shell-mobile { + @media (max-width: 47.98rem), (pointer: coarse) and (max-height: 520px) { + @slot; + } +} + @theme inline { /* Extra-small breakpoint (30rem / 480px) for the narrowest phones — the default scale starts at sm (640px), which leaves a gap between an iPhone SE diff --git a/src/components/data-list/selection-action-bar.tsx b/src/components/data-list/selection-action-bar.tsx index 5a275913a..f7bf6a1e2 100644 --- a/src/components/data-list/selection-action-bar.tsx +++ b/src/components/data-list/selection-action-bar.tsx @@ -82,7 +82,7 @@ export function SelectionActionBar({ // an `sm:` drop sat the bar behind the still-visible nav 640-767px. // z-30 keeps the bar above page content without needing to beat the // (now-cleared) nav's z-50. - className="bg-card border-border sticky bottom-[calc(env(safe-area-inset-bottom,0px)+5rem)] z-30 mt-2 flex items-center justify-between gap-2 rounded-lg border p-2 shadow-lg md:bottom-4" + className="bg-card border-border sticky bottom-[calc(env(safe-area-inset-bottom,0px)+5rem)] z-30 mt-2 flex items-center justify-between gap-2 rounded-lg border p-2 shadow-lg shell-desktop:bottom-4" >