Navigation Patterns
How to structure navigation in Apple-inspired interfaces — from hierarchical push navigation to flat tab bars, sidebars, and everything in between.
Navigation Models
Apple's HIG defines three fundamental navigation structures. Choosing the right model is the single most important navigation decision — it determines how users form a mental map of your app and how they move through content.
Navigation Bar
The navigation bar sits at the top of the screen and provides context: where you are, where you came from (back button), and actions specific to the current screen. It transforms from a large title (on scroll up / at rest) to a compact title (on scroll down).
Tab Bar
The tab bar lives at the bottom of the screen on iOS, providing always-visible switching between the top-level sections of your app. On web, translate this to a persistent bottom nav (mobile) or top navigation tabs (desktop). Tab bars are flat — every item is always one tap away.
Click any tab to switch — try them all
- ✓ 3–5 tabs maximum
- ✓ Show all tabs at all times
- ✓ Use filled icon for selected, outline for unselected
- ✓ Keep each tab's navigation stack independent
- ✓ Show badge counts for notifications
- ✗ More than 5 tabs (use More pattern instead)
- ✗ Hide or disable tabs conditionally
- ✗ Use the tab bar for context-specific actions
- ✗ Mix icon-only and text+icon tabs in the same bar
- ✗ Reset tab stack when user switches and returns
Sidebar
Introduced prominently on iPad and macOS, the sidebar is the definitive navigation for complex apps with many sections. On web and SaaS, the sidebar is the dominant navigation pattern — it scales to dozens of items and communicates application structure at a glance.
Push vs. Modal vs. Sheet
Choosing how to present a new screen is one of the most consequential decisions in navigation design. Each presentation style carries a specific implicit promise to the user about context, urgency, and how to dismiss.
When: Drilling into related content
When: Task completion required before returning
When: Short-lived contextual actions only
Search Pattern
Search is a navigation shortcut — it lets users bypass the navigation hierarchy entirely. Apple's search pattern has four distinct states, each requiring specific visual treatment to communicate what's happening and what's expected next.
Navigation in Web & SaaS
Apple's native navigation patterns translate directly to web and SaaS products. Each native pattern has a well-established web equivalent — the key is preserving the underlying behavioral contract, not copying the pixel-for-pixel visual.
| Pattern | Native Origin | Web Translation | Best For | Max Items |
|---|---|---|---|---|
| Tab Bar | iOS | Bottom nav (mobile) / Top tabs (desktop) | Simple apps, 3–5 sections | 5 |
| Sidebar | iPad / macOS | Persistent left nav, collapsible on mobile | Complex apps, many sections | Unlimited |
| Navigation Bar | iOS | Top bar with back link + breadcrumbs | Hierarchical content, admin portals | 3 actions |
| Toolbar | macOS | Action bar / command bar | Document & editor apps | 8–10 |
| Push Navigation | iOS UINavigationController | URL routing, history.pushState | Any content with depth | — |
| Modal Sheet | iOS / macOS sheet | Dialog, drawer, slide-over panel | Task flows, quick edits | — |
| Split View | iPad / macOS | Master-detail layout (list + content pane) | Email, files, messaging | — |
Common Mistakes
These mistakes appear repeatedly in interfaces that aren't following HIG principles. Each one creates friction or confusion — and each has a straightforward fix.
Code: Web Navigation Components
Production-ready CSS for the three core navigation components. Each uses the design token system for colors, spacing, and blur values.
/* ── Glass Navigation Bar ── */
.nav-bar {
position: sticky;
top: 0;
z-index: var(--z-raised);
height: 52px;
padding: 0 var(--space-5);
display: flex;
align-items: center;
justify-content: space-between;
/* Liquid Glass effect */
background: var(--color-glass);
backdrop-filter: blur(var(--blur-regular));
-webkit-backdrop-filter: blur(var(--blur-regular));
border-bottom: 1px solid var(--color-border);
}
.nav-bar-title {
font-size: 17px;
font-weight: var(--weight-semibold);
color: var(--color-text-primary);
position: absolute;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
}
.nav-bar-back {
display: flex;
align-items: center;
gap: var(--space-2);
background: none;
border: none;
color: var(--color-accent);
font-size: 17px;
cursor: pointer;
padding: var(--space-2) 0;
transition: opacity var(--duration-fast) var(--easing-standard);
}
.nav-bar-back:hover { opacity: 0.75; }
.nav-bar-action {
background: none;
border: none;
color: var(--color-accent);
font-size: 17px;
cursor: pointer;
padding: var(--space-2) 0;
}
/* Large title variant */
.nav-bar.nav-bar--large {
height: auto;
flex-direction: column;
align-items: stretch;
padding: 0 var(--space-5) var(--space-4);
}
.nav-bar--large .nav-bar-row {
height: 52px;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-bar--large .nav-bar-large-title {
font-size: 34px;
font-weight: var(--weight-bold);
letter-spacing: -0.025em;
color: var(--color-text-primary);
margin-top: var(--space-1);
}
/* ── Tab Bar ── */
.tab-bar {
display: flex;
justify-content: space-around;
align-items: stretch;
height: 49px; /* iOS standard */
padding-bottom: env(safe-area-inset-bottom, 0px);
background: var(--color-glass);
backdrop-filter: blur(var(--blur-regular));
-webkit-backdrop-filter: blur(var(--blur-regular));
border-top: 1px solid var(--color-border);
}
/* Fixed to bottom on mobile */
@media (max-width: 768px) {
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: var(--z-raised);
}
}
.tab-bar-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
background: none;
border: none;
cursor: pointer;
color: var(--color-text-tertiary);
font-size: 10px;
font-weight: var(--weight-medium);
padding: var(--space-2) var(--space-4);
transition: color var(--duration-fast) var(--easing-standard),
transform var(--duration-fast) var(--easing-spring);
}
.tab-bar-item:hover {
color: var(--color-text-secondary);
}
.tab-bar-item.active {
color: var(--color-accent);
}
.tab-bar-item:active {
transform: scale(0.92);
}
.tab-bar-item svg {
width: 24px;
height: 24px;
stroke-width: 1.8;
transition: stroke-width var(--duration-fast) var(--easing-standard);
}
/* Active: use filled icon (set stroke-width higher or swap to filled SVG) */
.tab-bar-item.active svg {
stroke-width: 2.4;
}
/* Badge */
.tab-bar-badge {
position: absolute;
top: 4px;
right: calc(50% - 20px);
min-width: 16px;
height: 16px;
padding: 0 4px;
border-radius: var(--radius-full);
background: var(--color-danger);
color: white;
font-size: 10px;
font-weight: var(--weight-bold);
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--color-bg-primary);
}
/* ── Sidebar Navigation ── */
.sidebar {
width: var(--sidebar-width, 260px);
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: var(--z-sidebar);
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--color-glass);
backdrop-filter: blur(var(--blur-prominent));
-webkit-backdrop-filter: blur(var(--blur-prominent));
border-right: 1px solid var(--color-border);
}
/* Offset main content */
.main-content {
margin-left: var(--sidebar-width, 260px);
}
/* Section label */
.sidebar-section-label {
padding: var(--space-4) var(--space-5) var(--space-1);
font-size: 10px;
font-weight: var(--weight-semibold);
color: var(--color-text-tertiary);
letter-spacing: 0.08em;
text-transform: uppercase;
display: block;
}
/* Nav item */
.sidebar-nav-item {
display: flex;
align-items: center;
gap: var(--space-2);
padding: 6px var(--space-5);
font-size: 15px;
font-weight: var(--weight-regular);
color: var(--color-text-secondary);
text-decoration: none;
border: none;
background: none;
width: 100%;
text-align: left;
cursor: pointer;
position: relative;
transition: color var(--duration-fast) var(--easing-standard),
background var(--duration-fast) var(--easing-standard);
}
.sidebar-nav-item:hover {
color: var(--color-text-primary);
background: var(--color-fill-quaternary);
}
.sidebar-nav-item.active {
color: var(--color-accent);
background: var(--color-accent-tint);
font-weight: var(--weight-medium);
}
/* Active indicator bar */
.sidebar-nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 4px;
bottom: 4px;
width: 3px;
background: var(--color-accent);
border-radius: 0 2px 2px 0;
}
/* Mobile: collapsed sidebar becomes a sheet */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
transition: transform var(--duration-normal) var(--easing-standard);
}
.sidebar.open {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
}