Skip to content

Commit 8a5bdae

Browse files
jamezpclaude
andcommitted
Add responsive hamburger menu for mobile navigation
Implements collapsible navigation menu for mobile devices with hamburger toggle button. Menu appears only on screens ≤768px, automatically closes when selecting a link or clicking outside. Includes full dark mode support and smooth transitions. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: James R. Perkins <jperkins@ibm.com>
1 parent 7fe9294 commit 8a5bdae

3 files changed

Lines changed: 177 additions & 34 deletions

File tree

public/css/bootstrap-theme.css

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,20 +512,80 @@ footer#projsubnav_footer ul.level1 a {
512512
}
513513

514514
/* ============================================
515-
RESPONSIVE
515+
MOBILE NAVIGATION - Hamburger Menu
516516
============================================ */
517+
/* Hide hamburger on desktop */
518+
.navbar-toggler {
519+
display: none !important;
520+
}
521+
522+
/* Show hamburger on mobile */
517523
@media (max-width: 768px) {
524+
.navbar-toggler {
525+
display: block !important;
526+
border: 2px solid var(--resteasy-primary) !important;
527+
background: white !important;
528+
padding: 0.5rem 0.75rem !important;
529+
border-radius: 0.375rem !important;
530+
margin: 0.5rem 0 !important;
531+
transition: all 0.15s ease !important;
532+
}
533+
534+
.navbar-toggler:hover {
535+
background: var(--resteasy-bg-light) !important;
536+
border-color: var(--resteasy-primary-dark) !important;
537+
}
538+
539+
.navbar-toggler:focus {
540+
box-shadow: 0 0 0 0.2rem rgba(44, 95, 122, 0.25) !important;
541+
outline: none !important;
542+
}
543+
544+
.navbar-toggler-icon {
545+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(44, 95, 122, 1)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
546+
display: inline-block !important;
547+
width: 1.5em !important;
548+
height: 1.5em !important;
549+
vertical-align: middle !important;
550+
background-repeat: no-repeat !important;
551+
background-position: center !important;
552+
background-size: 100% !important;
553+
}
554+
555+
/* Collapsible nav */
556+
.navbar-collapse {
557+
width: 100% !important;
558+
}
559+
560+
.navbar-collapse.collapse:not(.show) {
561+
display: none !important;
562+
}
563+
564+
.navbar-collapse.collapse.show {
565+
display: block !important;
566+
}
567+
518568
#proj_nav ul.sf-menu {
519569
flex-direction: column !important;
520570
align-items: stretch !important;
571+
width: 100% !important;
572+
margin-top: 0.5rem !important;
521573
}
522574

523575
#proj_nav ul.sf-menu > li {
524576
width: 100% !important;
577+
border-bottom: 1px solid var(--resteasy-border) !important;
578+
}
579+
580+
#proj_nav ul.sf-menu > li:last-child {
581+
border-bottom: none !important;
525582
}
526583

527-
#proj_nav ul.sf-menu > li > a {
584+
#proj_nav ul.sf-menu > li > a,
585+
#proj_nav ul.sf-menu > li > button {
528586
padding: 0.75rem 1rem !important;
587+
width: 100% !important;
588+
text-align: left !important;
529589
}
530590

531591
#proj_nav ul.sf-menu li ul {
@@ -546,6 +606,36 @@ footer#projsubnav_footer ul.level1 a {
546606
background: rgba(255, 255, 255, 0.1) !important;
547607
color: white !important;
548608
}
609+
}
610+
611+
/* Dark mode hamburger */
612+
[data-bs-theme="dark"] .navbar-toggler {
613+
background: #0d1117 !important;
614+
border-color: var(--resteasy-accent) !important;
615+
}
616+
617+
[data-bs-theme="dark"] .navbar-toggler:hover {
618+
background: #161b22 !important;
619+
border-color: var(--resteasy-accent-light) !important;
620+
}
621+
622+
[data-bs-theme="dark"] .navbar-toggler:focus {
623+
box-shadow: 0 0 0 0.2rem rgba(74, 158, 187, 0.25) !important;
624+
}
625+
626+
[data-bs-theme="dark"] .navbar-toggler-icon {
627+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(74, 158, 187, 1)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
628+
}
629+
630+
@media (max-width: 768px) {
631+
[data-bs-theme="dark"] #proj_nav ul.sf-menu > li {
632+
border-bottom-color: var(--resteasy-border) !important;
633+
}
634+
}
635+
636+
/* ============================================
637+
RESPONSIVE
638+
============================================ */
549639

550640
#proj_checklist ul {
551641
grid-template-columns: 1fr !important;

public/js/theme-toggle.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,54 @@
7373
window.toggleTheme = toggleTheme;
7474
})();
7575

76+
/**
77+
* Mobile Navigation Toggle
78+
* Handles hamburger menu collapse on mobile devices
79+
*/
80+
(function() {
81+
'use strict';
82+
83+
document.addEventListener('DOMContentLoaded', () => {
84+
const toggler = document.querySelector('.navbar-toggler');
85+
const navbarCollapse = document.querySelector('.navbar-collapse');
86+
87+
if (toggler && navbarCollapse) {
88+
toggler.addEventListener('click', () => {
89+
const isExpanded = toggler.getAttribute('aria-expanded') === 'true';
90+
91+
if (isExpanded) {
92+
navbarCollapse.classList.remove('show');
93+
toggler.setAttribute('aria-expanded', 'false');
94+
} else {
95+
navbarCollapse.classList.add('show');
96+
toggler.setAttribute('aria-expanded', 'true');
97+
}
98+
});
99+
100+
// Close menu when clicking a link (mobile)
101+
const navLinks = navbarCollapse.querySelectorAll('a');
102+
navLinks.forEach(link => {
103+
link.addEventListener('click', () => {
104+
if (window.innerWidth <= 768) {
105+
navbarCollapse.classList.remove('show');
106+
toggler.setAttribute('aria-expanded', 'false');
107+
}
108+
});
109+
});
110+
111+
// Close menu when clicking outside
112+
document.addEventListener('click', (e) => {
113+
if (window.innerWidth <= 768) {
114+
if (!toggler.contains(e.target) && !navbarCollapse.contains(e.target)) {
115+
navbarCollapse.classList.remove('show');
116+
toggler.setAttribute('aria-expanded', 'false');
117+
}
118+
}
119+
});
120+
}
121+
});
122+
})();
123+
76124
/**
77125
* Active Navigation Highlighting
78126
* Highlights the current navigation item based on the URL

templates/partials/nav.html

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,43 @@
88
</div>
99
<div id="proj_nav" class="">
1010
<div class="container">
11-
<ul class="sf-menu">
12-
<li class="open" data-nav="overview">
13-
<span class="notch">&nbsp;</span>
14-
<a href="{site.url.absolute}" class="menu-title">Overview</a>
15-
</li>
16-
<li class="open" data-nav="downloads">
17-
<span class="notch">&nbsp;</span>
18-
<a href="{site.url('/downloads').absolute}" class="menu-title">Downloads</a>
19-
</li>
20-
<li class="open" data-nav="docs">
21-
<span class="notch">&nbsp;</span>
22-
<a href="{site.url('/docs').absolute}" class="menu-title">Documentation</a>
23-
</li>
24-
<li class="open" data-nav="community">
25-
<span class="notch">&nbsp;</span>
26-
<a href="{site.url('/community').absolute}" class="menu-title">Community</a>
27-
</li>
28-
<li class="open" data-nav="blogs">
29-
<span class="notch">&nbsp;</span>
30-
<a href="{site.url('/blogs').absolute}" class="menu-title">Blog</a>
31-
</li>
32-
<li id="buildmenu" class="open" data-nav="build">
33-
<span class="notch">&nbsp;</span>
34-
<a href="{site.url('/build').absolute}" class="menu-title">Build</a>
35-
</li>
36-
<li>
37-
<button id="theme-toggle" type="button" aria-label="Toggle theme">
38-
<i class="fas fa-moon"></i>
39-
<span class="theme-toggle-text">Dark</span>
40-
</button>
41-
</li>
42-
</ul>
11+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
12+
<span class="navbar-toggler-icon"></span>
13+
</button>
14+
<div class="collapse navbar-collapse" id="navbarNav">
15+
<ul class="sf-menu">
16+
<li class="open" data-nav="overview">
17+
<span class="notch">&nbsp;</span>
18+
<a href="{site.url.absolute}" class="menu-title">Overview</a>
19+
</li>
20+
<li class="open" data-nav="downloads">
21+
<span class="notch">&nbsp;</span>
22+
<a href="{site.url('/downloads').absolute}" class="menu-title">Downloads</a>
23+
</li>
24+
<li class="open" data-nav="docs">
25+
<span class="notch">&nbsp;</span>
26+
<a href="{site.url('/docs').absolute}" class="menu-title">Documentation</a>
27+
</li>
28+
<li class="open" data-nav="community">
29+
<span class="notch">&nbsp;</span>
30+
<a href="{site.url('/community').absolute}" class="menu-title">Community</a>
31+
</li>
32+
<li class="open" data-nav="blogs">
33+
<span class="notch">&nbsp;</span>
34+
<a href="{site.url('/blogs').absolute}" class="menu-title">Blog</a>
35+
</li>
36+
<li id="buildmenu" class="open" data-nav="build">
37+
<span class="notch">&nbsp;</span>
38+
<a href="{site.url('/build').absolute}" class="menu-title">Build</a>
39+
</li>
40+
<li>
41+
<button id="theme-toggle" type="button" aria-label="Toggle theme">
42+
<i class="fas fa-moon"></i>
43+
<span class="theme-toggle-text">Dark</span>
44+
</button>
45+
</li>
46+
</ul>
47+
</div>
4348
</div><!-- end container -->
4449
</div>
4550
</div>

0 commit comments

Comments
 (0)