Contract-freeze · UX approval · portal-ux.md §5 gap

Mobile bottom navigation · Approval Frames

375px (phone / Android TWA viewport) frames for the host shell's primary mobile navigation. docs/design-system/portal-ux.md §5 records a real product gap: the design system's mobile guideline specifies a BottomNav below md (≤768px) and the BottomNav primitive exists in @fuzefront/design-system, but it is not wired into the host shell — the shell is drawer-only today, so a phone user has no persistent, thumb-reach navigation. These frames freeze how the bottom nav mounts in the shell, how it coexists with the existing slide-in drawer, and the loading / empty / error / safe-area states before any shell code is written. Built in the fuse-seam design system (tokens only).

Shell chrome present on every authenticated route. Implemented by mobile-frontend-engineer (responsive shell chrome), not in a federated remote. Reuses the existing drawer (SidePanel / .sidebar-scrim in Layout.tsx) rather than adding a second overlay.

Walk the flow

Frame 01 · 375px

Bottom nav mounted

The bar in the mobile shell: 3 primary destinations + More, the active-tab fuse-seam indicator, a tab notification badge, collapsed top bar, scrollable content behind a fixed bar.

defaultactivebadge
Frame 02 · 375px

"More" → drawer

How bottom nav coexists with the drawer: tapping More opens the existing SidePanel over a scrim for the long-tail (Billing, Connectors, Profile, Help, Status). Scrim dismiss; More shows pressed.

drawer-opencoexistence
Frame 03 · 375px

States & edges

Loading (nav renders instantly, never blank), empty (no registered apps), error (apps registry failed — nav still navigable), plus safe-area/notch, ≥44px touch targets, reduced motion and long-label truncation.

loadingemptyerrorsafe-areatouch-44

What approving this builds

Approving these frames approves this inventory. The implementation builds exactly this flow and these components — it cannot quietly invent a different architecture. No new npm package: the BottomNav primitive already exists in @fuzefront/design-system; this is shell wiring in frontend/src.

Flows
ShellBottomNav → /dashboard (shell-global)
Components (new, in frontend/src)
ShellBottomNav useShellNavItems
Reused (not rebuilt)
@fuzefront/design-system → BottomNav SidePanel (drawer) Layout (isSidebarOpen / scrim)
Packages
— none new (shell code) —

Design decisions frozen here

Open questions for approval

  • Is Home / Apps / Orgs / More the right four for the bar, or should Billing or Connectors take a primary slot over Orgs? (All others live under More.)
  • When a federated app is active (/app/<slug>), should the bar switch to the app's injected nav, or always stay portal-level with Apps highlighted? These frames assume stay portal-level.
  • Should the bar hide on scroll-down to maximise content height, or stay pinned? Frames assume stay pinned (simplest, most predictable for thumb reach).
● Awaiting approval

Design system: @fuzefront/design-system (fuse-seam) · Primitive: BottomNav (design-system/components/mobile/BottomNav.d.ts) · Shell: frontend/src/components/{Layout,SidePanel,ShellBottomNav}.tsx + index.css · Owner: mobile-frontend-engineer.
Reference gap: docs/design-system/portal-ux.md §5 · Mobile guideline: docs/design-system/artifact/project/README.md ("Mobile & responsiveness").
Approval is written per-flow into manifest.json by the design-approval workflow — reply @claude approve / @claude reject: <reason>. Re-stamp after any edit: node scripts/stamp-frames.mjs --write --feature mobile-bottom-nav.

Review · mobile-bottom-nav approve or reject each flow ↓
mobile-bottom-navpendingApproveReject