Unified chrome keeps the home logo top-left, measures bottom nav height for scroll clearance, and adds responsive content widths for mobile/desktop.
66 lines
2.8 KiB
Markdown
66 lines
2.8 KiB
Markdown
# 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
|
|
|
|
1. Page content and overlays appear under the fixed bottom nav because clearance uses a static `--bottom-nav-height: 6rem` that does not match the real rendered nav height (safe-area insets, label toggling, active-state transforms).
|
|
2. The home logo in `AppPageHeader` sits top-right and shifts vertically/horizontally as page titles vary in length.
|
|
3. Layout is mobile-first only; desktop gets narrow centered columns with no breakpoint strategy.
|
|
|
|
## Solution
|
|
|
|
### App chrome structure
|
|
|
|
- **AppTopBar:** Fixed top bar with `HomeLogoLink` always top-left; consistent height via `--app-topbar-height`.
|
|
- **AppPageHeader:** Content-only title/description block inside main — no logo.
|
|
- **NavOffsetProvider:** `ResizeObserver` on bottom nav sets `--app-nav-offset` on `document.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 logo
|
|
- `components/layout/app-shell.tsx` — top bar + responsive widths
|
|
- `components/layout/app-nav.tsx` — forwardRef for measurement; contain active transform
|
|
- `components/layout/global-bottom-nav.tsx` — wrap with NavOffsetProvider
|
|
- `components/avatar/avatar-customize-layout.tsx` — same shell pattern
|
|
- `app/(app)/layout.tsx` — dynamic padding classes
|
|
- `app/globals.css` — CSS vars and utility classes
|
|
- `components/ui/toast-provider.tsx` — use `--app-nav-offset`
|
|
- `components/reminders/in-app-reminder-banner.tsx` — use `--app-nav-offset`
|
|
|
|
### Edge cases
|
|
|
|
- Nav hidden (logged out / onboarding): `--app-nav-offset` reset to `0px`.
|
|
- Safe-area: measured `offsetHeight` includes safe-area padding automatically.
|
|
- Home page `/avatar`: logo visible; optional `aria-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.
|