Unified chrome keeps the home logo top-left, measures bottom nav height for scroll clearance, and adds responsive content widths for mobile/desktop.
2.8 KiB
2.8 KiB
App Shell & Responsive Layout Design
Date: 2026-05-24
Status: Approved for implementation
Approach: Unified app chrome + dynamic nav inset (Approach 2)
Desktop nav: Option A — bottom bar on all screen sizes
Problem
- Page content and overlays appear under the fixed bottom nav because clearance uses a static
--bottom-nav-height: 6remthat does not match the real rendered nav height (safe-area insets, label toggling, active-state transforms). - The home logo in
AppPageHeadersits top-right and shifts vertically/horizontally as page titles vary in length. - Layout is mobile-first only; desktop gets narrow centered columns with no breakpoint strategy.
Solution
App chrome structure
- AppTopBar: Fixed top bar with
HomeLogoLinkalways top-left; consistent height via--app-topbar-height. - AppPageHeader: Content-only title/description block inside main — no logo.
- NavOffsetProvider:
ResizeObserveron bottom nav sets--app-nav-offsetondocument.documentElement. - Overlays: Toasts and reminder banner use
bottom: calc(var(--app-nav-offset) + offset).
Responsive (Option A)
| Breakpoint | Content width |
|---|---|
| default | max-w-lg |
md |
max-w-2xl |
lg (standard pages) |
max-w-4xl |
lg (wide: shop, customize) |
max-w-6xl |
Bottom nav remains on all sizes, centered in max-w-6xl.
CSS variables
--app-topbar-height: 3.5rem— fixed top chrome height--app-nav-offset: 0px— set dynamically by NavOffsetProvider; fallback 6rem in CSS--bottom-nav-height— retained as fallback default for--app-nav-offset
Files
components/layout/app-top-bar.tsx(new)components/layout/nav-offset-provider.tsx(new)components/layout/app-page-header.tsx— remove logocomponents/layout/app-shell.tsx— top bar + responsive widthscomponents/layout/app-nav.tsx— forwardRef for measurement; contain active transformcomponents/layout/global-bottom-nav.tsx— wrap with NavOffsetProvidercomponents/avatar/avatar-customize-layout.tsx— same shell patternapp/(app)/layout.tsx— dynamic padding classesapp/globals.css— CSS vars and utility classescomponents/ui/toast-provider.tsx— use--app-nav-offsetcomponents/reminders/in-app-reminder-banner.tsx— use--app-nav-offset
Edge cases
- Nav hidden (logged out / onboarding):
--app-nav-offsetreset to0px. - Safe-area: measured
offsetHeightincludes safe-area padding automatically. - Home page
/avatar: logo visible; optionalaria-current="page"when active.
Testing
- Scroll to bottom on Shop, Habits, Profile — content fully visible above nav.
- Trigger toast + reminder — no overlap with nav.
- Logo position stable across Avatar, Shop, Habits, Customize.
- Viewports: 375px, 768px, 1280px.