Design and implement mobile-friendly navigation patterns including hamburger menus, off-canvas drawers, and touch-optimized interfaces.
Mobile navigation is one of the most critical aspects of responsive design. With limited screen real estate, traditional desktop navigation patterns simply don't work on mobile devices. Users need intuitive, accessible ways to navigate your site without overwhelming the interface.
Modern mobile navigation patterns have evolved to prioritize usability, accessibility, and performance. From hamburger menus to bottom navigation tabs, each pattern serves specific use cases and user contexts.
In this lesson, you'll learn to implement various mobile navigation patterns, understand when to use each approach, and create touch-friendly interfaces that enhance the mobile user experience.
Different mobile navigation patterns serve different needs and contexts. Understanding when and how to use each pattern is crucial for effective mobile design.
Common Mobile Navigation Patterns:
1. Hamburger Menu (Off-Canvas):
1.hamburger-menu {
2 position: fixed;
3 top: 0;
4 left: -250px;
5 width: 250px;
6 height: 100vh;
7 background: white;
8 transition: transform 0.3s ease;
9 z-index: 1000;
10}
11
12.hamburger-menu.open {
13 transform: translateX(250px);
14}
15
16.hamburger-toggle {
17 display: none;
18 flex-direction: column;
19 cursor: pointer;
20 padding: 0.5rem;
21}
22
23.hamburger-line {
24 width: 25px;
25 height: 3px;
26 background: #333;
27 margin: 3px 0;
28 transition: 0.3s;
29}
30
31@media (max-width: 768px) {
32 .hamburger-toggle {
33 display: flex;
34 }
35
36 .main-nav {
37 display: none;
38 }
39}2. Bottom Tab Navigation:
1.bottom-nav {
2 position: fixed;
3 bottom: 0;
4 left: 0;
5 right: 0;
6 background: white;
7 border-top: 1px solid #e2e8f0;
8 padding: 0.5rem 0;
9 z-index: 1000;
10}
11
12.bottom-nav-items {
13 display: flex;
14 justify-content: space-around;
15 align-items: center;
16}
17
18.bottom-nav-item {
19 display: flex;
20 flex-direction: column;
21 align-items: center;
22 padding: 0.5rem;
23 min-width: 44px; /* Touch target size */
24 min-height: 44px;
25 text-decoration: none;
26 color: #64748b;
27 transition: color 0.2s;
28}
29
30.bottom-nav-item.active,
31.bottom-nav-item:hover {
32 color: #3b82f6;
33}3. Collapsible/Accordion Navigation:
1.accordion-nav {
2 background: white;
3 border-radius: 8px;
4 overflow: hidden;
5 box-shadow: 0 2px 8px rgba(0,0,0,0.1);
6}
7
8.accordion-item {
9 border-bottom: 1px solid #e2e8f0;
10}
11
12.accordion-header {
13 width: 100%;
14 padding: 1rem;
15 background: none;
16 border: none;
17 text-align: left;
18 cursor: pointer;
19 display: flex;
20 justify-content: space-between;
21 align-items: center;
22 min-height: 44px;
23}
24
25.accordion-content {
26 max-height: 0;
27 overflow: hidden;
28 transition: max-height 0.3s ease;
29}
30
31.accordion-content.open {
32 max-height: 200px; /* Adjust based on content */
33}
34
35.accordion-submenu {
36 padding: 0.5rem 2rem;
37 background: #f8fafc;
38}Key Design Principles:
Accessibility is crucial in mobile navigation. Users with disabilities, different devices, or varying interaction preferences must be able to navigate effectively.
Accessible Hamburger Menu Implementation:
1<nav class="mobile-nav" role="navigation" aria-label="Main navigation">
2 <button
3 class="menu-toggle"
4 type="button"
5 aria-expanded="false"
6 aria-controls="mobile-menu"
7 aria-label="Toggle navigation menu">
8 <span class="hamburger-line"></span>
9 <span class="hamburger-line"></span>
10 <span class="hamburger-line"></span>
11 </button>
12
13 <div
14 class="mobile-menu"
15 id="mobile-menu"
16 aria-hidden="true">
17 <ul class="menu-list" role="list">
18 <li role="listitem">
19 <a href="/"
20 class="menu-link"
21 tabindex="-1">
22 Home
23 </a>
24 </li>
25 <li role="listitem">
26 <a href="/about"
27 class="menu-link"
28 tabindex="-1">
29 About
30 </a>
31 </li>
32 </ul>
33 </div>
34</nav>JavaScript for Accessible Interactions:
1class AccessibleMobileNav {
2 constructor() {
3 this.menuToggle = document.querySelector('.menu-toggle');
4 this.mobileMenu = document.querySelector('.mobile-menu');
5 this.menuLinks = this.mobileMenu.querySelectorAll('.menu-link');
6 this.isOpen = false;
7
8 this.init();
9 }
10
11 init() {
12 this.menuToggle.addEventListener('click', () => this.toggleMenu());
13
14 // Close menu on escape key
15 document.addEventListener('keydown', (e) => {
16 if (e.key === 'Escape' && this.isOpen) {
17 this.closeMenu();
18 }
19 });
20
21 // Close menu when clicking outside
22 document.addEventListener('click', (e) => {
23 if (!this.mobileMenu.contains(e.target) &&
24 !this.menuToggle.contains(e.target) &&
25 this.isOpen) {
26 this.closeMenu();
27 }
28 });
29 }
30
31 toggleMenu() {
32 this.isOpen ? this.closeMenu() : this.openMenu();
33 }
34
35 openMenu() {
36 this.isOpen = true;
37 this.mobileMenu.classList.add('open');
38 this.menuToggle.setAttribute('aria-expanded', 'true');
39 this.mobileMenu.setAttribute('aria-hidden', 'false');
40
41 // Enable tab navigation for menu items
42 this.menuLinks.forEach(link => link.removeAttribute('tabindex'));
43
44 // Focus first menu item
45 this.menuLinks[0]?.focus();
46 }
47
48 closeMenu() {
49 this.isOpen = false;
50 this.mobileMenu.classList.remove('open');
51 this.menuToggle.setAttribute('aria-expanded', 'false');
52 this.mobileMenu.setAttribute('aria-hidden', 'true');
53
54 // Disable tab navigation for menu items
55 this.menuLinks.forEach(link => link.setAttribute('tabindex', '-1'));
56
57 // Return focus to toggle button
58 this.menuToggle.focus();
59 }
60}
61
62// Initialize when DOM is ready
63document.addEventListener('DOMContentLoaded', () => {
64 new AccessibleMobileNav();
65});Focus Management and Keyboard Navigation:
1/* Focus indicators for keyboard navigation */
2.menu-toggle:focus,
3.menu-link:focus {
4 outline: 2px solid #3b82f6;
5 outline-offset: 2px;
6}
7
8/* Ensure focus is visible even in mobile contexts */
9.menu-toggle:focus:not(:focus-visible) {
10 outline: none;
11}
12
13.menu-toggle:focus-visible {
14 outline: 2px solid #3b82f6;
15 outline-offset: 2px;
16}
17
18/* High contrast mode support */
19@media (prefers-contrast: high) {
20 .menu-link {
21 border: 1px solid transparent;
22 }
23
24 .menu-link:focus,
25 .menu-link:hover {
26 border-color: currentColor;
27 }
28}
29
30/* Reduced motion preferences */
31@media (prefers-reduced-motion: reduce) {
32 .mobile-menu,
33 .hamburger-line {
34 transition: none;
35 }
36}Screen Reader Optimization:
Modern mobile navigation must accommodate touch gestures, varying screen sizes, and different interaction patterns.
Gesture-Based Navigation:
1.swipe-nav {
2 touch-action: pan-y; /* Allow vertical scrolling */
3 position: relative;
4 overflow: hidden;
5}
6
7.swipe-nav-content {
8 display: flex;
9 transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
10 will-change: transform;
11}
12
13.swipe-nav-item {
14 flex: 0 0 100%;
15 min-height: 100vh;
16 padding: 2rem;
17}
18
19/* Pull-to-refresh indicator */
20.pull-refresh {
21 position: absolute;
22 top: -60px;
23 left: 50%;
24 transform: translateX(-50%);
25 width: 40px;
26 height: 40px;
27 border-radius: 50%;
28 background: #3b82f6;
29 display: flex;
30 align-items: center;
31 justify-content: center;
32 color: white;
33 transition: transform 0.3s ease;
34}
35
36.pull-refresh.active {
37 transform: translateX(-50%) translateY(80px);
38}JavaScript for Touch Interactions:
1class TouchNavigation {
2 constructor(element) {
3 this.element = element;
4 this.startX = 0;
5 this.currentX = 0;
6 this.startY = 0;
7 this.currentY = 0;
8 this.isDragging = false;
9 this.threshold = 50; // Minimum distance for gesture
10
11 this.init();
12 }
13
14 init() {
15 this.element.addEventListener('touchstart', (e) => this.handleStart(e), { passive: true });
16 this.element.addEventListener('touchmove', (e) => this.handleMove(e), { passive: false });
17 this.element.addEventListener('touchend', (e) => this.handleEnd(e), { passive: true });
18
19 // Mouse events for desktop testing
20 this.element.addEventListener('mousedown', (e) => this.handleStart(e));
21 this.element.addEventListener('mousemove', (e) => this.handleMove(e));
22 this.element.addEventListener('mouseup', (e) => this.handleEnd(e));
23 }
24
25 handleStart(e) {
26 const touch = e.touches ? e.touches[0] : e;
27 this.startX = touch.clientX;
28 this.startY = touch.clientY;
29 this.isDragging = true;
30
31 this.element.style.transition = 'none';
32 }
33
34 handleMove(e) {
35 if (!this.isDragging) return;
36
37 const touch = e.touches ? e.touches[0] : e;
38 this.currentX = touch.clientX;
39 this.currentY = touch.clientY;
40
41 const deltaX = this.currentX - this.startX;
42 const deltaY = this.currentY - this.startY;
43
44 // Determine if this is a horizontal swipe
45 if (Math.abs(deltaX) > Math.abs(deltaY)) {
46 e.preventDefault(); // Prevent scrolling
47
48 // Apply transform for visual feedback
49 this.element.style.transform = `translateX(${deltaX}px)`;
50 }
51 }
52
53 handleEnd(e) {
54 if (!this.isDragging) return;
55
56 this.isDragging = false;
57 this.element.style.transition = '';
58
59 const deltaX = this.currentX - this.startX;
60
61 if (Math.abs(deltaX) > this.threshold) {
62 if (deltaX > 0) {
63 this.onSwipeRight();
64 } else {
65 this.onSwipeLeft();
66 }
67 }
68
69 // Reset position
70 this.element.style.transform = '';
71 }
72
73 onSwipeRight() {
74 console.log('Swiped right - go back');
75 // Implement navigation logic
76 }
77
78 onSwipeLeft() {
79 console.log('Swiped left - go forward');
80 // Implement navigation logic
81 }
82}Responsive Navigation Containers:
1.nav-container {
2 container-type: inline-size;
3 container-name: navigation;
4}
5
6/* Default mobile-first navigation */
7.navigation {
8 display: flex;
9 flex-direction: column;
10 position: fixed;
11 top: 0;
12 left: 0;
13 width: 100%;
14 background: white;
15 z-index: 1000;
16 transform: translateY(-100%);
17 transition: transform 0.3s ease;
18}
19
20.navigation.open {
21 transform: translateY(0);
22}
23
24/* Container query adjustments */
25@container navigation (min-width: 768px) {
26 .navigation {
27 position: static;
28 flex-direction: row;
29 transform: none;
30 background: transparent;
31 }
32
33 .mobile-only {
34 display: none;
35 }
36}
37
38@container navigation (min-width: 1024px) {
39 .navigation {
40 justify-content: space-between;
41 padding: 0 2rem;
42 }
43
44 .nav-item {
45 margin: 0 1rem;
46 }
47}Performance Optimizations:
1/* Use transform and opacity for smooth animations */
2.mobile-menu {
3 will-change: transform, opacity;
4 backface-visibility: hidden;
5 perspective: 1000px;
6}
7
8/* Optimize for 60fps animations */
9@keyframes slideIn {
10 from {
11 transform: translate3d(-100%, 0, 0);
12 opacity: 0;
13 }
14 to {
15 transform: translate3d(0, 0, 0);
16 opacity: 1;
17 }
18}
19
20/* Lazy load non-critical navigation items */
21.nav-submenu {
22 content-visibility: auto;
23 contain-intrinsic-size: 0 200px;
24}Build a comprehensive mobile navigation system with multiple patterns, accessibility features, and touch interactions.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Mobile Navigation Patterns</title>
7 <style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 font-family: 'Inter', system-ui, sans-serif;
16 line-height: 1.6;
17 color: #1e293b;
18 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19 min-height: 100vh;
20 padding-top: 70px; /* Account for fixed header */
21 }
22
23 /* Main Header */
24 .main-header {
25 position: fixed;
26 top: 0;
27 left: 0;
28 right: 0;
29 background: rgba(255, 255, 255, 0.95);
30 backdrop-filter: blur(10px);
31 padding: 1rem 2rem;
32 box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
33 z-index: 1000;
34 display: flex;
35 justify-content: space-between;
36 align-items: center;
37 }
38
39 .logo {
40 font-size: 1.5rem;
41 font-weight: 800;
42 background: linear-gradient(135deg, #667eea, #764ba2);
43 -webkit-background-clip: text;
44 -webkit-text-fill-color: transparent;
45 background-clip: text;
46 }
47
48 /* Desktop Navigation (hidden on mobile) */
49 .desktop-nav {
50 display: none;
51 }
52
53 .desktop-nav ul {
54 display: flex;
55 list-style: none;
56 gap: 2rem;
57 }
58
59 .desktop-nav a {
60 text-decoration: none;
61 color: #1e293b;
62 font-weight: 500;
63 padding: 0.5rem 1rem;
64 border-radius: 8px;
65 transition: all 0.3s ease;
66 }
67
68 .desktop-nav a:hover,
69 .desktop-nav a:focus {
70 background: #f1f5f9;
71 color: #667eea;
72 outline: 2px solid #667eea;
73 }
74
75 /* Mobile Menu Toggle */
76 .mobile-menu-toggle {
77 display: flex;
78 flex-direction: column;
79 cursor: pointer;
80 padding: 0.5rem;
81 background: none;
82 border: none;
83 border-radius: 4px;
84 transition: all 0.3s ease;
85 }
86
87 .mobile-menu-toggle:focus {
88 outline: 2px solid #667eea;
89 background: #f1f5f9;
90 }
91
92 .hamburger-line {
93 width: 25px;
94 height: 3px;
95 background: #1e293b;
96 margin: 3px 0;
97 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
98 border-radius: 2px;
99 }
100
101 /* Animated hamburger states */
102 .mobile-menu-toggle.active .hamburger-line:nth-child(1) {
103 transform: rotate(45deg) translate(6px, 6px);
104 }
105
106 .mobile-menu-toggle.active .hamburger-line:nth-child(2) {
107 opacity: 0;
108 }
109
110 .mobile-menu-toggle.active .hamburger-line:nth-child(3) {
111 transform: rotate(-45deg) translate(6px, -6px);
112 }
113
114 /* Off-Canvas Mobile Menu */
115 .mobile-menu {
116 position: fixed;
117 top: 70px;
118 left: 0;
119 width: 100%;
120 height: calc(100vh - 70px);
121 background: white;
122 transform: translateX(-100%);
123 transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
124 overflow-y: auto;
125 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
126 }
127
128 .mobile-menu.open {
129 transform: translateX(0);
130 }
131
132 .mobile-menu-list {
133 list-style: none;
134 padding: 2rem 0;
135 }
136
137 .mobile-menu-item {
138 border-bottom: 1px solid #e2e8f0;
139 }
140
141 .mobile-menu-link {
142 display: block;
143 padding: 1.5rem 2rem;
144 text-decoration: none;
145 color: #1e293b;
146 font-size: 1.125rem;
147 font-weight: 500;
148 transition: all 0.3s ease;
149 min-height: 44px;
150 display: flex;
151 align-items: center;
152 justify-content: space-between;
153 }
154
155 .mobile-menu-link:hover,
156 .mobile-menu-link:focus {
157 background: #f8fafc;
158 color: #667eea;
159 outline: none;
160 border-left: 4px solid #667eea;
161 }
162
163 /* Submenu handling */
164 .has-submenu .mobile-menu-link::after {
165 content: '›';
166 font-size: 1.5rem;
167 transition: transform 0.3s ease;
168 }
169
170 .has-submenu.open .mobile-menu-link::after {
171 transform: rotate(90deg);
172 }
173
174 .submenu {
175 max-height: 0;
176 overflow: hidden;
177 transition: max-height 0.3s ease;
178 background: #f8fafc;
179 }
180
181 .submenu.open {
182 max-height: 300px;
183 }
184
185 .submenu-link {
186 display: block;
187 padding: 1rem 3rem;
188 text-decoration: none;
189 color: #64748b;
190 font-size: 1rem;
191 transition: all 0.3s ease;
192 min-height: 44px;
193 display: flex;
194 align-items: center;
195 }
196
197 .submenu-link:hover,
198 .submenu-link:focus {
199 background: #e2e8f0;
200 color: #1e293b;
201 outline: none;
202 }
203
204 /* Main Content */
205 .container {
206 max-width: 1200px;
207 margin: 0 auto;
208 padding: 2rem;
209 background: rgba(255, 255, 255, 0.95);
210 border-radius: 20px;
211 margin-top: 2rem;
212 margin-bottom: 2rem;
213 box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
214 backdrop-filter: blur(10px);
215 }
216
217 .hero {
218 text-align: center;
219 margin-bottom: 3rem;
220 }
221
222 .hero-title {
223 font-size: clamp(2rem, 5vw, 3.5rem);
224 font-weight: 800;
225 background: linear-gradient(135deg, #667eea, #764ba2);
226 -webkit-background-clip: text;
227 -webkit-text-fill-color: transparent;
228 background-clip: text;
229 margin-bottom: 1rem;
230 }
231
232 .hero-subtitle {
233 font-size: clamp(1rem, 2.5vw, 1.25rem);
234 color: #64748b;
235 max-width: 60ch;
236 margin: 0 auto;
237 }
238
239 /* Navigation Patterns Demo */
240 .patterns-grid {
241 display: grid;
242 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
243 gap: 2rem;
244 margin: 3rem 0;
245 }
246
247 .pattern-card {
248 background: white;
249 border-radius: 16px;
250 padding: 2rem;
251 box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
252 transition: all 0.3s ease;
253 border: 1px solid #e2e8f0;
254 position: relative;
255 overflow: hidden;
256 }
257
258 .pattern-card::before {
259 content: '';
260 position: absolute;
261 top: 0;
262 left: 0;
263 right: 0;
264 height: 4px;
265 background: linear-gradient(90deg, #667eea, #764ba2);
266 }
267
268 .pattern-card:hover {
269 transform: translateY(-8px);
270 box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
271 }
272
273 .pattern-title {
274 font-size: 1.5rem;
275 font-weight: 700;
276 margin-bottom: 1rem;
277 color: #1e293b;
278 }
279
280 .pattern-description {
281 color: #64748b;
282 margin-bottom: 1.5rem;
283 line-height: 1.6;
284 }
285
286 .pattern-demo {
287 background: #f8fafc;
288 border-radius: 8px;
289 padding: 1rem;
290 margin: 1rem 0;
291 border: 1px solid #e2e8f0;
292 }
293
294 .demo-nav {
295 display: flex;
296 justify-content: space-between;
297 align-items: center;
298 padding: 0.5rem;
299 background: #667eea;
300 color: white;
301 border-radius: 6px;
302 font-size: 0.875rem;
303 }
304
305 .demo-menu-btn {
306 background: rgba(255, 255, 255, 0.2);
307 border: none;
308 color: white;
309 padding: 0.25rem 0.5rem;
310 border-radius: 4px;
311 cursor: pointer;
312 }
313
314 /* Bottom Navigation Demo */
315 .bottom-nav-demo {
316 position: relative;
317 height: 200px;
318 background: #f1f5f9;
319 border-radius: 8px;
320 overflow: hidden;
321 }
322
323 .demo-bottom-nav {
324 position: absolute;
325 bottom: 0;
326 left: 0;
327 right: 0;
328 background: white;
329 border-top: 1px solid #e2e8f0;
330 padding: 0.5rem 0;
331 display: flex;
332 justify-content: space-around;
333 }
334
335 .demo-nav-item {
336 display: flex;
337 flex-direction: column;
338 align-items: center;
339 padding: 0.5rem;
340 color: #64748b;
341 font-size: 0.75rem;
342 cursor: pointer;
343 transition: color 0.2s;
344 min-width: 44px;
345 }
346
347 .demo-nav-item:hover,
348 .demo-nav-item.active {
349 color: #667eea;
350 }
351
352 .demo-icon {
353 font-size: 1.25rem;
354 margin-bottom: 0.25rem;
355 }
356
357 /* Responsive Design */
358 @media (min-width: 768px) {
359 .desktop-nav {
360 display: block;
361 }
362
363 .mobile-menu-toggle {
364 display: none;
365 }
366
367 .mobile-menu {
368 display: none;
369 }
370
371 body {
372 padding-top: 80px;
373 }
374
375 .main-header {
376 padding: 1.5rem 2rem;
377 }
378 }
379
380 @media (max-width: 480px) {
381 .container {
382 margin: 1rem;
383 padding: 1.5rem;
384 border-radius: 12px;
385 }
386
387 .patterns-grid {
388 grid-template-columns: 1fr;
389 gap: 1.5rem;
390 }
391
392 .mobile-menu-link {
393 padding: 1.25rem 1.5rem;
394 font-size: 1rem;
395 }
396
397 .submenu-link {
398 padding: 1rem 2rem;
399 }
400 }
401
402 /* Accessibility and animations */
403 @media (prefers-reduced-motion: reduce) {
404 .mobile-menu,
405 .hamburger-line,
406 .pattern-card,
407 .mobile-menu-link {
408 transition: none;
409 }
410 }
411
412 @media (prefers-contrast: high) {
413 .mobile-menu-link:focus,
414 .desktop-nav a:focus {
415 outline: 3px solid currentColor;
416 outline-offset: 2px;
417 }
418 }
419
420 /* Animation delays for staggered entrance */
421 .pattern-card:nth-child(1) { animation-delay: 0.1s; }
422 .pattern-card:nth-child(2) { animation-delay: 0.2s; }
423 .pattern-card:nth-child(3) { animation-delay: 0.3s; }
424 .pattern-card:nth-child(4) { animation-delay: 0.4s; }
425
426 .pattern-card {
427 animation: fadeInUp 0.6s ease-out forwards;
428 opacity: 0;
429 transform: translateY(30px);
430 }
431
432 @keyframes fadeInUp {
433 to {
434 opacity: 1;
435 transform: translateY(0);
436 }
437 }
438 </style>
439</head>
440<body>
441 <!-- Main Header with Navigation -->
442 <header class="main-header">
443 <div class="logo">🧭 NavPatterns</div>
444
445 <!-- Desktop Navigation -->
446 <nav class="desktop-nav" role="navigation" aria-label="Main navigation">
447 <ul>
448 <li><a href="#home">Home</a></li>
449 <li><a href="#patterns">Patterns</a></li>
450 <li><a href="#examples">Examples</a></li>
451 <li><a href="#accessibility">Accessibility</a></li>
452 <li><a href="#contact">Contact</a></li>
453 </ul>
454 </nav>
455
456 <!-- Mobile Menu Toggle -->
457 <button
458 class="mobile-menu-toggle"
459 type="button"
460 aria-expanded="false"
461 aria-controls="mobile-menu"
462 aria-label="Toggle navigation menu">
463 <span class="hamburger-line"></span>
464 <span class="hamburger-line"></span>
465 <span class="hamburger-line"></span>
466 </button>
467 </header>
468
469 <!-- Mobile Off-Canvas Menu -->
470 <nav class="mobile-menu" id="mobile-menu" role="navigation" aria-label="Mobile navigation" aria-hidden="true">
471 <ul class="mobile-menu-list" role="list">
472 <li class="mobile-menu-item">
473 <a href="#home" class="mobile-menu-link" tabindex="-1">
474 🏠 Home
475 </a>
476 </li>
477 <li class="mobile-menu-item has-submenu">
478 <button class="mobile-menu-link" tabindex="-1" aria-expanded="false">
479 📱 Patterns
480 </button>
481 <ul class="submenu" role="list">
482 <li><a href="#hamburger" class="submenu-link" tabindex="-1">Hamburger Menu</a></li>
483 <li><a href="#bottom-nav" class="submenu-link" tabindex="-1">Bottom Navigation</a></li>
484 <li><a href="#accordion" class="submenu-link" tabindex="-1">Accordion Menu</a></li>
485 <li><a href="#tab-bar" class="submenu-link" tabindex="-1">Tab Bar</a></li>
486 </ul>
487 </li>
488 <li class="mobile-menu-item">
489 <a href="#examples" class="mobile-menu-link" tabindex="-1">
490 💡 Examples
491 </a>
492 </li>
493 <li class="mobile-menu-item">
494 <a href="#accessibility" class="mobile-menu-link" tabindex="-1">
495 ♿ Accessibility
496 </a>
497 </li>
498 <li class="mobile-menu-item">
499 <a href="#contact" class="mobile-menu-link" tabindex="-1">
500 📧 Contact
501 </a>
502 </li>
503 </ul>
504 </nav>
505
506 <!-- Main Content -->
507 <div class="container">
508 <header class="hero">
509 <h1 class="hero-title">🧭 Mobile Navigation Patterns</h1>
510 <p class="hero-subtitle">Explore modern, accessible mobile navigation patterns that enhance user experience across all devices</p>
511 </header>
512
513 <!-- Navigation Patterns Grid -->
514 <section class="patterns-grid">
515 <article class="pattern-card" id="hamburger">
516 <h2 class="pattern-title">🍔 Hamburger Menu</h2>
517 <p class="pattern-description">
518 Classic off-canvas navigation that slides in from the side. Perfect for apps with many navigation options.
519 </p>
520 <div class="pattern-demo">
521 <div class="demo-nav">
522 <span>My App</span>
523 <button class="demo-menu-btn">☰</button>
524 </div>
525 <p style="padding: 1rem; font-size: 0.875rem; color: #64748b;">
526 <strong>Best for:</strong> Complex navigation hierarchies, content-focused apps
527 </p>
528 </div>
529 </article>
530
531 <article class="pattern-card" id="bottom-nav">
532 <h2 class="pattern-title">📱 Bottom Navigation</h2>
533 <p class="pattern-description">
534 Tab-based navigation at the bottom of the screen. Provides quick access to top-level views.
535 </p>
536 <div class="pattern-demo">
537 <div class="bottom-nav-demo">
538 <div class="demo-bottom-nav">
539 <div class="demo-nav-item active">
540 <div class="demo-icon">🏠</div>
541 <div>Home</div>
542 </div>
543 <div class="demo-nav-item">
544 <div class="demo-icon">🔍</div>
545 <div>Search</div>
546 </div>
547 <div class="demo-nav-item">
548 <div class="demo-icon">❤️</div>
549 <div>Favorites</div>
550 </div>
551 <div class="demo-nav-item">
552 <div class="demo-icon">👤</div>
553 <div>Profile</div>
554 </div>
555 </div>
556 </div>
557 <p style="padding: 1rem; font-size: 0.875rem; color: #64748b;">
558 <strong>Best for:</strong> 3-5 top-level destinations, thumb-friendly navigation
559 </p>
560 </div>
561 </article>
562
563 <article class="pattern-card" id="accordion">
564 <h2 class="pattern-title">🪗 Accordion Menu</h2>
565 <p class="pattern-description">
566 Collapsible sections that expand to show sub-navigation. Great for organizing hierarchical content.
567 </p>
568 <div class="pattern-demo">
569 <div style="border: 1px solid #e2e8f0; border-radius: 6px; overflow: hidden;">
570 <div style="background: white; padding: 0.75rem; border-bottom: 1px solid #e2e8f0; cursor: pointer;">
571 📚 Categories ▼
572 </div>
573 <div style="background: #f8fafc; padding: 0.5rem 1.5rem; font-size: 0.875rem; border-bottom: 1px solid #e2e8f0;">
574 • Web Design
575 </div>
576 <div style="background: #f8fafc; padding: 0.5rem 1.5rem; font-size: 0.875rem; border-bottom: 1px solid #e2e8f0;">
577 • Mobile Apps
578 </div>
579 <div style="background: white; padding: 0.75rem; cursor: pointer;">
580 🛠️ Tools ▶
581 </div>
582 </div>
583 <p style="padding: 1rem 0; font-size: 0.875rem; color: #64748b;">
584 <strong>Best for:</strong> Deep navigation hierarchies, content categorization
585 </p>
586 </div>
587 </article>
588
589 <article class="pattern-card" id="tab-bar">
590 <h2 class="pattern-title">📑 Tab Bar</h2>
591 <p class="pattern-description">
592 Horizontal scrollable tabs for category-based navigation. Common in content and e-commerce apps.
593 </p>
594 <div class="pattern-demo">
595 <div style="display: flex; overflow-x: auto; background: white; border: 1px solid #e2e8f0; border-radius: 6px;">
596 <div style="flex: 0 0 auto; padding: 0.75rem 1.5rem; background: #667eea; color: white; font-size: 0.875rem;">All</div>
597 <div style="flex: 0 0 auto; padding: 0.75rem 1.5rem; color: #64748b; font-size: 0.875rem; cursor: pointer;">Popular</div>
598 <div style="flex: 0 0 auto; padding: 0.75rem 1.5rem; color: #64748b; font-size: 0.875rem; cursor: pointer;">Recent</div>
599 <div style="flex: 0 0 auto; padding: 0.75rem 1.5rem; color: #64748b; font-size: 0.875rem; cursor: pointer;">Trending</div>
600 <div style="flex: 0 0 auto; padding: 0.75rem 1.5rem; color: #64748b; font-size: 0.875rem; cursor: pointer;">Featured</div>
601 </div>
602 <p style="padding: 1rem 0; font-size: 0.875rem; color: #64748b;">
603 <strong>Best for:</strong> Content filtering, category switching
604 </p>
605 </div>
606 </article>
607 </section>
608
609 <!-- Accessibility Section -->
610 <section style="background: white; padding: 2rem; border-radius: 16px; margin: 3rem 0; box-shadow: 0 4px 12px rgba(0,0,0,0.05);">
611 <h2 style="font-size: 2rem; margin-bottom: 1rem; color: #1e293b;">♿ Accessibility Features</h2>
612 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;">
613 <div>
614 <h3 style="color: #667eea; margin-bottom: 0.5rem;">🎯 Touch Targets</h3>
615 <p style="color: #64748b; font-size: 0.9rem;">Minimum 44px tap areas for comfortable interaction</p>
616 </div>
617 <div>
618 <h3 style="color: #667eea; margin-bottom: 0.5rem;">⌨️ Keyboard Navigation</h3>
619 <p style="color: #64748b; font-size: 0.9rem;">Full keyboard accessibility with proper focus management</p>
620 </div>
621 <div>
622 <h3 style="color: #667eea; margin-bottom: 0.5rem;">📢 Screen Readers</h3>
623 <p style="color: #64748b; font-size: 0.9rem;">ARIA labels and semantic HTML for assistive technologies</p>
624 </div>
625 <div>
626 <h3 style="color: #667eea; margin-bottom: 0.5rem;">🎨 High Contrast</h3>
627 <p style="color: #64748b; font-size: 0.9rem;">Support for high contrast and reduced motion preferences</p>
628 </div>
629 </div>
630 </section>
631 </div>
632
633 <script>
634 document.addEventListener('DOMContentLoaded', function() {
635 console.log('🧭 Mobile Navigation Patterns - Interactive Demo Loaded');
636
637 // Mobile menu functionality
638 class MobileNavigation {
639 constructor() {
640 this.menuToggle = document.querySelector('.mobile-menu-toggle');
641 this.mobileMenu = document.querySelector('.mobile-menu');
642 this.menuLinks = document.querySelectorAll('.mobile-menu-link');
643 this.submenuButtons = document.querySelectorAll('.has-submenu .mobile-menu-link');
644 this.isOpen = false;
645
646 this.init();
647 }
648
649 init() {
650 // Main menu toggle
651 this.menuToggle.addEventListener('click', () => this.toggleMenu());
652
653 // Submenu toggles
654 this.submenuButtons.forEach(button => {
655 button.addEventListener('click', (e) => this.toggleSubmenu(e));
656 });
657
658 // Close menu on escape key
659 document.addEventListener('keydown', (e) => {
660 if (e.key === 'Escape' && this.isOpen) {
661 this.closeMenu();
662 }
663 });
664
665 // Close menu when clicking outside
666 document.addEventListener('click', (e) => {
667 if (!this.mobileMenu.contains(e.target) &&
668 !this.menuToggle.contains(e.target) &&
669 this.isOpen) {
670 this.closeMenu();
671 }
672 });
673
674 // Handle regular menu links
675 this.menuLinks.forEach(link => {
676 if (!link.parentElement.classList.contains('has-submenu')) {
677 link.addEventListener('click', () => {
678 console.log('Navigation to:', link.textContent.trim());
679 this.closeMenu();
680 });
681 }
682 });
683 }
684
685 toggleMenu() {
686 this.isOpen ? this.closeMenu() : this.openMenu();
687 }
688
689 openMenu() {
690 this.isOpen = true;
691 this.mobileMenu.classList.add('open');
692 this.menuToggle.classList.add('active');
693 this.menuToggle.setAttribute('aria-expanded', 'true');
694 this.mobileMenu.setAttribute('aria-hidden', 'false');
695
696 // Enable tab navigation
697 this.menuLinks.forEach(link => link.removeAttribute('tabindex'));
698
699 // Focus first menu item
700 this.menuLinks[0]?.focus();
701
702 console.log('📱 Mobile menu opened');
703 }
704
705 closeMenu() {
706 this.isOpen = false;
707 this.mobileMenu.classList.remove('open');
708 this.menuToggle.classList.remove('active');
709 this.menuToggle.setAttribute('aria-expanded', 'false');
710 this.mobileMenu.setAttribute('aria-hidden', 'true');
711
712 // Disable tab navigation
713 this.menuLinks.forEach(link => link.setAttribute('tabindex', '-1'));
714
715 // Close all submenus
716 document.querySelectorAll('.has-submenu.open').forEach(item => {
717 item.classList.remove('open');
718 item.querySelector('.submenu').classList.remove('open');
719 });
720
721 // Return focus to toggle
722 this.menuToggle.focus();
723
724 console.log('📱 Mobile menu closed');
725 }
726
727 toggleSubmenu(e) {
728 e.preventDefault();
729
730 const button = e.target;
731 const parentItem = button.parentElement;
732 const submenu = parentItem.querySelector('.submenu');
733 const isOpen = parentItem.classList.contains('open');
734
735 // Close all other submenus
736 document.querySelectorAll('.has-submenu.open').forEach(item => {
737 if (item !== parentItem) {
738 item.classList.remove('open');
739 item.querySelector('.submenu').classList.remove('open');
740 }
741 });
742
743 // Toggle current submenu
744 if (isOpen) {
745 parentItem.classList.remove('open');
746 submenu.classList.remove('open');
747 button.setAttribute('aria-expanded', 'false');
748 } else {
749 parentItem.classList.add('open');
750 submenu.classList.add('open');
751 button.setAttribute('aria-expanded', 'true');
752 }
753
754 console.log('Submenu toggled:', button.textContent.trim());
755 }
756 }
757
758 // Interactive demos for pattern cards
759 function initPatternDemos() {
760 // Bottom nav demo
761 const bottomNavItems = document.querySelectorAll('.demo-nav-item');
762 bottomNavItems.forEach(item => {
763 item.addEventListener('click', function() {
764 bottomNavItems.forEach(i => i.classList.remove('active'));
765 this.classList.add('active');
766
767 const label = this.textContent.trim();
768 console.log('Bottom nav clicked:', label);
769 });
770 });
771
772 // Pattern card interactions
773 const patternCards = document.querySelectorAll('.pattern-card');
774 patternCards.forEach((card, index) => {
775 card.addEventListener('click', function() {
776 const title = this.querySelector('.pattern-title').textContent;
777 console.log('Pattern card clicked:', title);
778
779 // Add visual feedback
780 this.style.transform = 'translateY(-12px) scale(1.02)';
781 setTimeout(() => {
782 this.style.transform = '';
783 }, 300);
784 });
785
786 // Add entrance animation delay
787 card.style.animationDelay = (index * 0.1) + 's';
788 });
789 }
790
791 // Touch gesture support
792 class TouchGestureHandler {
793 constructor() {
794 this.startX = 0;
795 this.currentX = 0;
796 this.threshold = 50;
797
798 this.init();
799 }
800
801 init() {
802 document.addEventListener('touchstart', (e) => this.handleStart(e), { passive: true });
803 document.addEventListener('touchmove', (e) => this.handleMove(e), { passive: true });
804 document.addEventListener('touchend', (e) => this.handleEnd(e), { passive: true });
805 }
806
807 handleStart(e) {
808 this.startX = e.touches[0].clientX;
809 }
810
811 handleMove(e) {
812 this.currentX = e.touches[0].clientX;
813 }
814
815 handleEnd(e) {
816 const deltaX = this.currentX - this.startX;
817
818 if (Math.abs(deltaX) > this.threshold) {
819 if (deltaX > 0) {
820 console.log('👆 Swipe right detected');
821 // Could open menu from left edge
822 } else {
823 console.log('👆 Swipe left detected');
824 // Could close menu
825 }
826 }
827 }
828 }
829
830 // Performance monitoring
831 function logPerformanceMetrics() {
832 const navigation = performance.getEntriesByType('navigation')[0];
833 if (navigation) {
834 console.log('📊 Page load performance:');
835 console.log('- DOMContentLoaded:', Math.round(navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart) + 'ms');
836 console.log('- Load complete:', Math.round(navigation.loadEventEnd - navigation.loadEventStart) + 'ms');
837 }
838
839 // Monitor animation performance
840 let frameCount = 0;
841 let lastTime = performance.now();
842
843 function countFrames() {
844 frameCount++;
845 const currentTime = performance.now();
846
847 if (currentTime - lastTime >= 1000) {
848 console.log('🎬 Animation FPS:', frameCount);
849 frameCount = 0;
850 lastTime = currentTime;
851 }
852
853 requestAnimationFrame(countFrames);
854 }
855
856 requestAnimationFrame(countFrames);
857 }
858
859 // Initialize all functionality
860 new MobileNavigation();
861 initPatternDemos();
862 new TouchGestureHandler();
863 logPerformanceMetrics();
864
865 console.log('🚀 Mobile navigation system initialized successfully!');
866 console.log('📱 Try the mobile menu toggle and explore different patterns');
867 console.log('👆 Test touch gestures and accessibility features');
868 });
869 </script>
870</body>
871</html>This comprehensive example demonstrates advanced mobile navigation patterns:
Key Features Implemented:
JavaScript Features:
The implementation follows modern web standards and provides a robust foundation for mobile navigation systems.
In the next lesson, we'll explore touch-friendly interface design, learning how to create interfaces optimized for touch interaction, gesture support, and mobile usability patterns that enhance the overall user experience.
Your progress is saved in this browser, on this device only.