Replace the catch-all placeholder with an AppShell that renders one primary nav per breakpoint (chosen with matchMedia via useBreakpoint): a dark pill bottom bar below 768 px, a 96 px rail with the "M" mark at 768-1199 px, and a 208 px sidebar with the MealQuest wordmark and the "Our kitchen / Shared household" card at 1200 px and up. Nav order is Home, Plan, Discover, Quick, Settings; NavLink marks the active item aria-current="page", with nested Plan and Settings paths keeping their parent current. The mobile scroll area reserves 100 px plus the safe area beneath its content. Register every handoff route plus /settings/household, /settings/regulars and /settings/calendar, each with a placeholder heading. Remove the default body margin so the shell fills the viewport. Tests force each breakpoint with a matchMedia mock and assert the routes, nav order, each layout's structure, aria-current and the mobile bottom padding from the stylesheet. Maestro task: 42a4695a-6bd0-49a1-925e-387858dbee48 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
21 lines
760 B
TypeScript
21 lines
760 B
TypeScript
import type { LucideIcon } from 'lucide-react'
|
|
import { Calendar, Compass, House, SlidersHorizontal, Zap } from 'lucide-react'
|
|
|
|
export type NavItem = {
|
|
to: string
|
|
label: string
|
|
icon: LucideIcon
|
|
// Match the path exactly; otherwise nested paths (/plan/shopping,
|
|
// /settings/ai) also mark the item current.
|
|
end: boolean
|
|
}
|
|
|
|
// The primary nav, in the same order in every layout.
|
|
export const navItems: readonly NavItem[] = [
|
|
{ to: '/', label: 'Home', icon: House, end: true },
|
|
{ to: '/plan', label: 'Plan', icon: Calendar, end: false },
|
|
{ to: '/discover', label: 'Discover', icon: Compass, end: false },
|
|
{ to: '/quick', label: 'Quick', icon: Zap, end: false },
|
|
{ to: '/settings', label: 'Settings', icon: SlidersHorizontal, end: false },
|
|
]
|