/** * BottomTabBar — fixed-bottom navigation for phone (≤767px). * * UI-SPEC §BottomTabBar: * - Fixed bottom, full-width, 56px + env(safe-area-inset-bottom) * - Background: var(--color-surface-dim) * - Border-top: 1px solid var(--color-border) * - Two equal tabs: Calendar (CalendarDays icon) and Lists (List icon) * - Active tab: icon + label in var(--color-member-0), 2px border-bottom indicator * - Inactive tab: icon + label in var(--color-text-muted) * - Touch target: min 44px guaranteed by 56px bar height * - z-index: 200 (below dialogs at 300) * * Uses react-router NavLink for real-URL active detection (D-17). * Only rendered on phone (≤767px) — DesktopNav handles desktop navigation. * Returns null on desktop so it does not overlay the AppNav sidebar avatar/Settings * button (FIX 4). Same breakpoint (767px) as AppNav's phone/desktop switch. */ import { NavLink } from 'react-router'; import { CalendarDays, List } from 'lucide-react'; function isPhone(): boolean { return typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches; } const tabBase: React.CSSProperties = { flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '3px', textDecoration: 'none', fontSize: 'var(--text-label-size, 13px)', fontWeight: 400, lineHeight: 'var(--text-label-line-height, 1.4)', fontFamily: 'var(--font-family-base)', color: 'var(--color-text-muted)', minHeight: '44px', borderBottom: '2px solid transparent', transition: 'color 0.1s ease, border-color 0.1s ease', userSelect: 'none', WebkitTapHighlightColor: 'transparent', }; const tabActiveOverride: React.CSSProperties = { color: 'var(--color-member-0)', borderBottom: '2px solid var(--color-member-0)', }; export function BottomTabBar() { // Phone-only: return null on desktop (≥768px) so the fixed bar does not overlay // the AppNav sidebar's Settings/avatar button (FIX 4). Consistent with the // isPhone() breakpoint used in AppNav and CalendarShell. if (!isPhone()) { return null; } return ( ); }