diff --git a/src/app/globals.css b/src/app/globals.css index 36c28d12b..b230f566a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -271,6 +271,10 @@ the layered .edge-glass-header base and the unlayered max-width:639px guard so the values cannot drift apart. lg overrides to 1.5rem. */ --header-edge-pad: 1rem; + /* Borderless app-shell/header height (the min-h-14 bar). Page shells fill the + viewport below it via calc(100dvh - var(--shell-header-h)); one token so the + header height and those page-fill floors cannot silently drift apart. */ + --shell-header-h: 4rem; /* Keep in sync with mobile-composer-reserve.ts (phone dock clearance). */ --phone-dock-hidden-pad: 0.75rem; --phone-dock-differentials-compare-clearance: 12.5rem; diff --git a/src/components/clinical-dashboard/favourites-command-library-page.tsx b/src/components/clinical-dashboard/favourites-command-library-page.tsx index 1b1a1b305..ad07922be 100644 --- a/src/components/clinical-dashboard/favourites-command-library-page.tsx +++ b/src/components/clinical-dashboard/favourites-command-library-page.tsx @@ -1074,7 +1074,7 @@ export function FavouritesCommandLibraryPage({ query = "", demoMode }: { query?: return (
Favourites command library @@ -1128,14 +1128,14 @@ export function FavouritesCommandLibraryPage({ query = "", demoMode }: { query?: return (
Favourites command library
} + fallback={
} > {children} diff --git a/src/components/clinical-dashboard/medication-record-page.tsx b/src/components/clinical-dashboard/medication-record-page.tsx index e159a07df..42d973756 100644 --- a/src/components/clinical-dashboard/medication-record-page.tsx +++ b/src/components/clinical-dashboard/medication-record-page.tsx @@ -496,7 +496,10 @@ export function MedicationRecordPage({ const governance = data?.governance ?? (error ? undefined : fallbackGovernance); return ( -
+
diff --git a/src/components/differentials/differential-stream-page.tsx b/src/components/differentials/differential-stream-page.tsx index 3aba60696..eba178f6e 100644 --- a/src/components/differentials/differential-stream-page.tsx +++ b/src/components/differentials/differential-stream-page.tsx @@ -40,7 +40,7 @@ const streamCopy: Record< export function DifferentialStreamPage({ stream, query = "" }: DifferentialStreamPageProps) { const copy = streamCopy[stream]; return ( -
+

diff --git a/src/components/forms/form-detail-page.tsx b/src/components/forms/form-detail-page.tsx index 64051ca74..36004c05d 100644 --- a/src/components/forms/form-detail-page.tsx +++ b/src/components/forms/form-detail-page.tsx @@ -559,7 +559,7 @@ export function FormDetailPage({ form }: { form: FormRecord }) { return (

{notice ? ( diff --git a/src/components/formulation/formulation-ui.tsx b/src/components/formulation/formulation-ui.tsx index becae60a0..c80523e4e 100644 --- a/src/components/formulation/formulation-ui.tsx +++ b/src/components/formulation/formulation-ui.tsx @@ -13,7 +13,7 @@ export function FormulationPageShell({ children, className }: { children: ReactN className={cn( // Phone shell owns dock clearance; avoid 100dvh min-height overflowing the // inset scrollport the way service/form detail pages used to. - "max-sm:min-h-0 bg-[color:var(--background)] px-3 py-4 pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-4rem)] sm:px-5 sm:py-6 sm:pb-10 lg:px-7", + "max-sm:min-h-0 bg-[color:var(--background)] px-3 py-4 pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-var(--shell-header-h))] sm:px-5 sm:py-6 sm:pb-10 lg:px-7", className, )} > diff --git a/src/components/mode-home-page-skeleton.tsx b/src/components/mode-home-page-skeleton.tsx index 3280ca630..d15b2792e 100644 --- a/src/components/mode-home-page-skeleton.tsx +++ b/src/components/mode-home-page-skeleton.tsx @@ -32,7 +32,7 @@ export function ModeHomePageSkeleton() { export function ModeHomeRouteLoading() { return ( -
+
); @@ -59,7 +59,11 @@ export function DocumentSearchPageSkeleton() { export function DocumentViewerPageSkeleton() { return ( -
+
Loading document diff --git a/src/components/mode-home-template.tsx b/src/components/mode-home-template.tsx index 4fc9af56a..34c55dc7e 100644 --- a/src/components/mode-home-template.tsx +++ b/src/components/mode-home-template.tsx @@ -146,7 +146,7 @@ export function ModeHomeMain({
{notice ? ( diff --git a/src/components/specifiers/specifier-ui.tsx b/src/components/specifiers/specifier-ui.tsx index 09ab25f8f..101d842d8 100644 --- a/src/components/specifiers/specifier-ui.tsx +++ b/src/components/specifiers/specifier-ui.tsx @@ -26,7 +26,7 @@ export function SpecifierPageShell({ children, className }: { children: ReactNod className={cn( // Phone shell owns dock clearance; avoid 100dvh min-height overflowing the // inset scrollport the way service/form detail pages used to. - "max-sm:min-h-0 bg-[color:var(--background)] px-3 py-4 pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-4rem)] sm:px-5 sm:py-6 sm:pb-10 lg:px-7", + "max-sm:min-h-0 bg-[color:var(--background)] px-3 py-4 pb-4 text-[color:var(--text)] sm:min-h-[calc(100dvh-var(--shell-header-h))] sm:px-5 sm:py-6 sm:pb-10 lg:px-7", className, )} > diff --git a/src/components/therapy-compass/therapy-compass.css b/src/components/therapy-compass/therapy-compass.css index ba98ea575..64ac06016 100644 --- a/src/components/therapy-compass/therapy-compass.css +++ b/src/components/therapy-compass/therapy-compass.css @@ -1,5 +1,5 @@ .tc-root { - min-height: calc(100dvh - 4rem); + min-height: calc(100dvh - var(--shell-header-h)); background: var(--surface-chrome); color: var(--text); } @@ -1293,7 +1293,7 @@ padding: 32px 40px 40px; } .tc-root .tc-workspace-006 { - min-height: calc(100dvh - 4rem); + min-height: calc(100dvh - var(--shell-header-h)); background: var(--surface-chrome); color: var(--text); } diff --git a/src/components/ui-primitives.tsx b/src/components/ui-primitives.tsx index 79591013f..6bb92c20e 100644 --- a/src/components/ui-primitives.tsx +++ b/src/components/ui-primitives.tsx @@ -132,7 +132,7 @@ export const searchPageCanvas = "bg-[color:var(--background)] text-[color:var(-- // --mobile-composer-reserve so it can collapse when the dock hides. Do not bake // a second dock-sized safe-area pad into page shells. export const searchPageShell = - "min-h-[calc(100dvh-4rem)] overflow-x-hidden px-3 py-3 pb-4 sm:px-5 sm:py-5 sm:pb-8 lg:px-6"; + "min-h-[calc(100dvh-var(--shell-header-h))] overflow-x-hidden px-3 py-3 pb-4 sm:px-5 sm:py-5 sm:pb-8 lg:px-6"; export const searchPageContainer = "mx-auto w-full max-w-[1500px]"; // Canonical content-page width. Detail pages (service / form / differential), // medication record + prescribing workspace, and the forms results view converge diff --git a/tests/mobile-interaction-regressions.test.ts b/tests/mobile-interaction-regressions.test.ts index 1fde939a4..d2001e93f 100644 --- a/tests/mobile-interaction-regressions.test.ts +++ b/tests/mobile-interaction-regressions.test.ts @@ -27,12 +27,14 @@ describe("mobile interaction regressions", () => { const differentialsHomeSource = source("src/components/clinical-dashboard/differentials-home.tsx"); expect(presentationSource).toMatch( - /data-testid="differential-presentation-page"\s+className="[^"]*min-h-0[^"]*overflow-x-clip[^"]*sm:min-h-\[calc\(100dvh-4rem\)\]/, + /data-testid="differential-presentation-page"\s+className="[^"]*min-h-0[^"]*overflow-x-clip[^"]*sm:min-h-\[calc\(100dvh-var\(--shell-header-h\)\)\]/, ); expect(favouritesSource).toMatch( - /data-testid="favourites-hub"\s+className="[^"]*min-h-0[^"]*overflow-x-clip[^"]*sm:min-h-\[calc\(100dvh-4rem\)\]/, + /data-testid="favourites-hub"\s+className="[^"]*min-h-0[^"]*overflow-x-clip[^"]*sm:min-h-\[calc\(100dvh-var\(--shell-header-h\)\)\]/, + ); + expect(favouritesSource).toContain( + '"grid min-h-0 min-w-0 overflow-x-clip sm:min-h-[calc(100dvh-var(--shell-header-h))]"', ); - expect(favouritesSource).toContain('"grid min-h-0 min-w-0 overflow-x-clip sm:min-h-[calc(100dvh-4rem)]"'); // overflow-x-hidden would force overflow-y:auto and nest a scrollport under #main-content. expect(differentialsHomeSource).toMatch( /data-testid="differentials-search-results"[\s\S]*?className="[^"]*overflow-x-clip[^"]*"/,