/** * AppNav — top navigation bar (phone) / left sidebar (tablet/desktop). * * UI-SPEC §AppNav: * - Phone: 48px top bar, app name "FamilySync" left, user color swatch right * - Tablet/Desktop: 240px left sidebar, app name + ColorLegend * - Accent colors NOT on chrome (60/30/10 split) * * Reviewer note (UI-SPEC §Post-Verification): * - Phone-nav avatar/color swatch needs aria-label + title (icon-only affordance) * - Grid is primary focal point; AppNav is secondary chrome */ import { NavLink } from 'react-router' import { CalendarDays, List } from 'lucide-react' import { ColorLegend, type LegendMember } from './ColorLegend.js' interface AppNavProps { members?: LegendMember[] currentUserColor?: string currentUserName?: string /** Called when the user avatar is tapped — opens the Settings sheet. */ onOpenSettings?: () => void } export function AppNav({ members = [], currentUserColor, currentUserName, onOpenSettings }: AppNavProps) { const isMobile = typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches if (isMobile) { return ( ) } return ( ) } /** Phone: 48px top bar — app name left, user avatar right */ function PhoneNav({ currentUserColor, currentUserName, onOpenSettings, }: { currentUserColor?: string currentUserName?: string onOpenSettings?: () => void }) { const displayName = currentUserName ?? 'User' const color = currentUserColor ?? 'var(--color-member-0)' return (
{/* App name */} FamilySync {/* User avatar — promoted to button to open Settings sheet (D-09) */}
) } /** Tablet/Desktop: 240px left sidebar — app name + nav links + color legend + avatar */ function DesktopNav({ members, currentUserColor, currentUserName, onOpenSettings, }: { members: LegendMember[] currentUserColor?: string currentUserName?: string onOpenSettings?: () => void }) { const navLinkStyle = ({ isActive }: { isActive: boolean }): React.CSSProperties => ({ display: 'flex', alignItems: 'center', gap: '10px', padding: 'var(--space-2) var(--space-3, 12px)', borderRadius: 'var(--space-1, 4px)', textDecoration: 'none', fontSize: 'var(--text-body-size, 15px)', fontWeight: isActive ? 600 : 400, color: isActive ? 'var(--color-member-0)' : 'var(--color-text-primary)', background: isActive ? 'color-mix(in srgb, var(--color-member-0) 10%, transparent)' : 'transparent', minHeight: '44px', transition: 'color 0.1s ease, background 0.1s ease', }) return ( ) }