Mobile Navigation
Three patterns for primary navigation on small screens: a bottom tab bar for 2–5 equal-weight destinations, a navigation drawer for deeper hierarchies, and a bottom sheet overflow menu for secondary actions. Use one pattern per app — mixing them creates confusion.
Bottom tab bar
The default mobile navigation pattern. Fixed to the bottom of the viewport with an icon and label for each destination. Active tab uses the accent color; inactive tabs use text-tertiary. Badge counts appear above the icon for unread indicators.
Home
Your feed and recent activity
Tap each tab to switch screens. The Inbox tab carries a badge count.
Navigation drawer
A panel that slides in from the left. Use when you have 6 or more destinations, need section groupings, or want to show the app brand prominently. Triggered by a hamburger button in the app header. Dismissed by the close button, a backdrop tap, or selecting an item.
Tap ☰ to open the drawer
Tap the hamburger icon to open the drawer. Select an item or tap the backdrop to close.
Bottom sheet overflow
A "More" tab that opens a bottom sheet with secondary destinations. Use when you have 3–4 primary tabs plus additional items that don't warrant a full drawer. The sheet keeps the tab bar clean while still surfacing secondary actions within thumb reach.
Tap "More" to open sheet
More
Tap "More" to open the sheet. Tap an item or the backdrop to dismiss.
Pattern comparison
| Pattern | Destinations | Best for | Avoid when |
|---|---|---|---|
| Bottom tab bar | 2–5 | Peer destinations with equal importance; frequent tab switching | 6+ destinations; hierarchical sections; desktop-first apps |
| Navigation drawer | 5–15 | Many destinations; section groupings; utility-dense apps | 3 or fewer destinations; frequent switching between sections |
| Bottom sheet overflow | 3 tabs + overflow | Mostly 3 primary tabs with occasional secondary actions | Overflow contains primary destinations; deep navigation flows |
Platform guidance
| Platform | Preferred pattern | Notes |
|---|---|---|
| iOS | Bottom tab bar | Maps to UITabBarController / SwiftUI TabView. Users expect it at the bottom. Keep tabs 2–5; use .badge() for counts; filled icons for active state per iOS HIG. |
| Android | Bottom navigation bar | Material 3 NavigationBar. Same 2–5 tab rule. Navigation drawer is still common for utility-dense apps (NavigationDrawer / ModalNavigationDrawer). |
| Web (mobile) | Bottom tab bar or drawer | Fix to bottom with position: fixed. Use env(safe-area-inset-bottom) for iPhone home indicator clearance. Add backdrop-filter: blur for frosted glass. Avoid sticky — it rises with the keyboard. |
| Web (desktop) | Top navbar or sidebar | Bottom-anchored navigation feels out of place on desktop. Switch to a top navbar or collapsible sidebar at your md breakpoint (~768px). |
Design decisions
- →Never stack patterns. A bottom tab bar and a navigation drawer in the same app creates competing affordances. Pick one as the primary navigation container and use the other only for utility (e.g., a sheet for a profile menu is fine alongside a tab bar).
- →Tab labels are required. Icon-only bottom tabs fail accessibility and are harder for new users to learn. Always pair icons with short text labels (1–2 words max). The bottom tab bar has enough vertical space — use it.
- →Thumb zone placement. Bottom-anchored navigation falls within natural thumb reach on phones held in one hand. Top navigation forces a stretch. On screens larger than ~6 inches or in landscape, consider a side rail instead.
- →Backdrop blur on the tab bar. backdrop-filter: blur(20px) saturate(180%) lets content scroll behind the bar with a frosted glass effect, giving visual continuity without a hard edge. Ensure the surface color has enough opacity to maintain contrast in both light and dark modes.
- →Drawer width. A drawer wider than 80% of the screen competes too much with the backdrop affordance (users need to see enough of the page to understand they can tap to close). 75–80% is the sweet spot; on tablets use a fixed width (~320px).
Accessibility
- →Bottom tab bar: role="tablist" on the container; role="tab" + aria-selected on each button. Never icon-only — labels are required for VoiceOver / TalkBack discoverability.
- →Navigation drawer: role="navigation" with aria-label. The trigger button needs aria-expanded and aria-controls. When open, trap focus inside the drawer and mark background content aria-hidden.
- →Bottom sheet overflow: role="dialog" + aria-modal="true" on the sheet panel. Escape key and backdrop tap must both dismiss. Focus moves to the first item on open; returns to the trigger on close.
- →Badge counts must be readable by screen readers. Wrap the count in a span with aria-label, e.g. aria-label="3 unread messages". Don't rely on the visual number alone.
- →Minimum touch target: 44×44px per Apple HIG, 48×48dp per Material. The tab bar meets this with py-2.5 + icon + label combined height — do not reduce padding.
- →On iOS, SwiftUI TabView and NavigationSplitView handle all accessibility automatically. Only build custom navigation when you need behavior the system doesn't provide, and replicate all ARIA semantics.