Skip to content

feat: Implement site header with responsive navigation, mobile menu functionality.#21

Open
Jozz77 wants to merge 8 commits intoOWASP-BLT:mainfrom
Jozz77:Fix-Navigation-hamburger-menu-icon-missing-on-mobile-viewports
Open

feat: Implement site header with responsive navigation, mobile menu functionality.#21
Jozz77 wants to merge 8 commits intoOWASP-BLT:mainfrom
Jozz77:Fix-Navigation-hamburger-menu-icon-missing-on-mobile-viewports

Conversation

@Jozz77
Copy link
Copy Markdown
Contributor

@Jozz77 Jozz77 commented Mar 9, 2026

Implement Responsive Mobile Navigation

Clear description of changes

  • Mobile-First Navigation: Added a hidden-by-default mobile navigation menu and a hamburger toggle button to the site header.
  • Overlay Logic: Positioned the mobile menu using absolute positioning so that it overlays the page content when opened, ensuring a stable and professional layout that doesn't push down the main content.
  • Interactive Toggle: Added JavaScript logic to handle the smooth opening/closing of the menu, including a transition between "Hamburger" and "Close (X)" icons.
  • Auto-Cleanup: Implemented a resize observer that automatically closes the mobile menu if the browser window is expanded to desktop width, preventing UI inconsistencies.
  • Active Link Highlighting: Extended the existing "active link" logic to ensure the current page is correctly highlighted in both desktop and mobile views.

Motivation and context

Previously, the site's navigation was completely inaccessible on mobile devices. When the viewport width dropped below 768px, the horizontal menu would disappear without any alternative navigation method. These changes ensure a fully responsive experience, allowing mobile users to navigate between courses and other university pages easily.

Related issues

Fixes #20

Screenshots

Screenshot (19) Screenshot (20)

Testing performed

  • Build Verification: Successfully ran bundle exec jekyll build to ensure Jekyll templates and Tailwind CSS classes are processing correctly.
  • Cross-Device Simulation: Verified the layout behavior at various breakpoints using browser developer tools.
  • Responsive Logic: Tested the transition between mobile and desktop views to ensure the menu state resets correctly upon window resizing.
  • Visual Check: Confirmed the menu correctly overlays page content and maintains high contrast for accessibility.

Summary by CodeRabbit

  • New Features
    • Responsive mobile navigation with a toggle button (menu/close icons) and accessibility attributes.
    • Mobile menu displays navigation in a vertical stack and auto-collapses when switching to desktop view.
    • Active navigation links highlight in both mobile and desktop menus.
    • Improved course search and filtering behavior for more consistent results.

@owasp-blt
Copy link
Copy Markdown

owasp-blt bot commented Mar 9, 2026

👋 Thanks for opening this pull request, @Jozz77!

Before your PR is reviewed, please ensure:

  • Your code follows the project's coding style and guidelines.
  • You have written or updated tests for your changes.
  • The commit messages are clear and descriptive.
  • You have linked any relevant issues (e.g., Closes #123).

🔍 Our team will review your PR shortly. If you have questions, feel free to ask in the comments.

🚀 Keep up the great work! — OWASP BLT

@owasp-blt
Copy link
Copy Markdown

owasp-blt bot commented Mar 9, 2026

📊 Monthly Leaderboard

Hi @Jozz77! Here's how you rank for March 2026:

Rank User Open PRs PRs (merged) PRs (closed) Reviews Comments Total
#32 @TonyStark-19 0 1 0 0 0 10
#33 @Jozz77 2 0 0 0 4 10
#34 @Arpanchristian2507 0 0 0 1 1 7

Scoring this month (across OWASP-BLT org): Open PRs (+1 each), Merged PRs (+10), Closed (not merged) (−2), Reviews (+5; first two per PR in-month), Comments (+2, excludes CodeRabbit). Run /leaderboard on any issue or PR to see your rank!

@owasp-blt
Copy link
Copy Markdown

owasp-blt bot commented Mar 9, 2026

👋 Hi @Jozz77!

This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:

  • The PR author
  • coderabbitai
  • copilot

Once a valid peer review is submitted, this check will pass automatically. Thank you!

⚠️ Peer review enforcement is active.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a responsive mobile navigation: header DOM now includes a hamburger/close toggle and a vertical mobile menu. Client JS toggles menu open/close, swaps icons, updates aria-expanded, highlights active links in mobile menu, and auto-closes on resize. Tailwind safelist updated for new utilities.

Changes

Cohort / File(s) Summary
Header — mobile UI
\_includes/header.html
Adds a mobile menu button (menu/close icons, aria attributes) and a #mobile-menu vertical container; preserves desktop nav, updates class names and logo img formatting.
Client JS — toggle, highlight & filtering tweaks
assets/js/main.js
Implements mobile-menu toggle (open/close, icon swap, aria-expanded, transition timing, resize handler to close >=768px); applies active class to mobile links; simplifies course filter/search value handling by removing fallback guards and trimming change.
Layout — Tailwind safelist
\_layouts/default.html
Adds Tailwind safelist entries max-h-[500px] and opacity-100; minor HTML formatting/structure spacing changes only.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant Browser
participant JS as "assets/js/main.js"
participant Header as "header DOM (#mobile-menu)"
User->>Browser: tap mobile menu button
Browser->>JS: click event
JS->>Header: toggle classes (hidden / max-h-[500px] / opacity-100), swap icons, set aria-expanded
JS->>Browser: start transition timer
Browser->>Header: render menu open/closed
Browser->>JS: window resize event
JS->>Header: hide mobile menu if width >= 768px

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main changes: implementing a responsive site header with mobile menu functionality, which directly aligns with the primary objective of fixing the missing hamburger menu on mobile viewports.
Linked Issues check ✅ Passed The pull request successfully addresses all coding requirements from issue #20: renders a hamburger menu icon for mobile viewports, implements open/close toggle functionality with icon switching, creates a dropdown navigation menu with proper styling, and ensures navigation is accessible across all viewport sizes.
Out of Scope Changes check ✅ Passed The changes are focused and in-scope: mobile navigation button/menu in header, mobile menu toggle JavaScript, responsive styling configuration in Tailwind, and navigation highlighting logic. All modifications directly support the mobile navigation feature objective with no extraneous changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@_includes/header.html`:
- Around line 27-29: Add explicit button semantics and expose its controlled
region: update the element with id "mobile-menu-button" to include
type="button", add aria-controls pointing to "mobile-menu", and add an
aria-expanded attribute; then modify the toggle logic in assets/js/main.js (the
code that opens/closes the mobile menu and references the mobile-menu-button
element) to keep aria-expanded in sync by setting it to "true" when opening and
"false" when closing via setAttribute on the mobile-menu-button element.

In `@assets/js/main.js`:
- Around line 89-131: The open/close timeouts for the mobile menu are not
cancelled, causing stale timers to run and leave mobileMenu, menuIcon, and
closeIcon out of sync; introduce a module-scoped timer variable (e.g.,
mobileMenuTimer) and before calling any setTimeout in the click handler or the
resize listener call clearTimeout(mobileMenuTimer) to cancel the previous
pending animation, then assign the new setTimeout id to mobileMenuTimer; also
ensure any early-return code paths still clear the timer so the panel and icons
always end in the intended state.
- Around line 99-100: The runtime-only class 'max-h-[500px]' is added via
mobileMenu.classList.add('max-h-[500px]') in assets/js/main.js but may be purged
by Tailwind; update your Tailwind configuration to either include
assets/js/main.js (or its directory) in the content array or add 'max-h-[500px]'
to the safelist in tailwind.config.* so the utility is emitted in production
CSS, then rebuild and verify the utility exists in the generated CSS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 49179f1a-85bd-4556-aa18-4e76255c2aaf

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6301f and 8beb778.

📒 Files selected for processing (2)
  • _includes/header.html
  • assets/js/main.js

Copy link
Copy Markdown
Contributor

@e-esakman e-esakman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fix the issues flagged by coderabbitai.

…te change. Also fixed exposing the menu state to assistive tech.
@Jozz77
Copy link
Copy Markdown
Contributor Author

Jozz77 commented Mar 9, 2026

Fixed clearing pending animation timers before scheduling another state change. Also fixed exposing the menu state to assistive tech.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/js/main.js`:
- Around line 90-148: Replace the implicit "hidden" check with an explicit
boolean state (e.g., isMobileMenuOpen) tracked alongside mobileMenuTimer inside
the mobileMenuButton click handler and the window.resize handler: initialize
isMobileMenuOpen = false; on open set isMobileMenuOpen = true, remove 'hidden'
then run the timeout to add transition classes; on close set isMobileMenuOpen =
false, start the timeout to add 'hidden' after the transition; always use
isMobileMenuOpen to decide open vs close (instead of
mobileMenu.classList.contains('hidden')), and ensure window.resize clears
mobileMenuTimer, sets isMobileMenuOpen = false and resets the classes and
aria-expanded consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 4b46f630-000f-4daf-ad65-69cb64b08858

📥 Commits

Reviewing files that changed from the base of the PR and between 8beb778 and 01df9bc.

📒 Files selected for processing (3)
  • _includes/header.html
  • _layouts/default.html
  • assets/js/main.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • _includes/header.html

Copy link
Copy Markdown

@arnavkirti arnavkirti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implement coderabbit suggestions, otherwise LGTM.

@owasp-blt owasp-blt bot added has-peer-review PR has received peer review and removed needs-peer-review PR needs peer review labels Mar 9, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
assets/js/main.js (1)

89-104: ⚠️ Potential issue | 🔴 Critical

Critical: openMenu and closeMenu functions are called but never defined.

Lines 92, 94, and 101 call openMenu() and closeMenu(), but these functions do not exist in the file. This will throw a ReferenceError at runtime, completely breaking mobile navigation.

The variables mobileMenuTimer, menuIcon, and closeIcon (lines 85-87) are declared but unused, confirming the function bodies were omitted.

🐛 Proposed fix: Add the missing function definitions
     if (mobileMenuButton && mobileMenu) {
+        const openMenu = () => {
+            if (mobileMenuTimer) {
+                clearTimeout(mobileMenuTimer);
+                mobileMenuTimer = null;
+            }
+
+            mobileMenu.classList.remove('hidden');
+            mobileMenuButton.setAttribute('aria-expanded', 'true');
+
+            mobileMenuTimer = setTimeout(() => {
+                mobileMenu.classList.remove('max-h-0', 'opacity-0');
+                mobileMenu.classList.add('max-h-[500px]', 'opacity-100');
+                mobileMenuTimer = null;
+            }, 10);
+
+            menuIcon.classList.add('hidden');
+            closeIcon.classList.remove('hidden');
+        };
+
+        const closeMenu = (hideImmediately = false) => {
+            if (mobileMenuTimer) {
+                clearTimeout(mobileMenuTimer);
+                mobileMenuTimer = null;
+            }
+
+            mobileMenu.classList.remove('max-h-[500px]', 'opacity-100');
+            mobileMenu.classList.add('max-h-0', 'opacity-0');
+            mobileMenuButton.setAttribute('aria-expanded', 'false');
+            menuIcon.classList.remove('hidden');
+            closeIcon.classList.add('hidden');
+
+            if (hideImmediately) {
+                mobileMenu.classList.add('hidden');
+                return;
+            }
+
+            mobileMenuTimer = setTimeout(() => {
+                mobileMenu.classList.add('hidden');
+                mobileMenuTimer = null;
+            }, 300);
+        };
+
         mobileMenuButton.addEventListener('click', () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/main.js` around lines 89 - 104, The event handlers reference
missing functions openMenu and closeMenu (and declared-but-unused
mobileMenuTimer, menuIcon, closeIcon), causing runtime errors; implement an
openMenu() that sets mobileMenuButton.setAttribute('aria-expanded','true'), adds
the visible class to mobileMenu, swaps menuIcon/closeIcon visibility and manages
mobileMenuTimer for auto-close if needed, and implement closeMenu(force = false)
that clears mobileMenuTimer, sets aria-expanded to 'false', removes the visible
class from mobileMenu, swaps icons back, and when called with force (from the
resize handler) immediately closes without waiting; ensure these functions are
referenced by the existing mobileMenuButton and window resize listeners so the
click and resize handlers work correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@assets/js/main.js`:
- Around line 89-104: The event handlers reference missing functions openMenu
and closeMenu (and declared-but-unused mobileMenuTimer, menuIcon, closeIcon),
causing runtime errors; implement an openMenu() that sets
mobileMenuButton.setAttribute('aria-expanded','true'), adds the visible class to
mobileMenu, swaps menuIcon/closeIcon visibility and manages mobileMenuTimer for
auto-close if needed, and implement closeMenu(force = false) that clears
mobileMenuTimer, sets aria-expanded to 'false', removes the visible class from
mobileMenu, swaps icons back, and when called with force (from the resize
handler) immediately closes without waiting; ensure these functions are
referenced by the existing mobileMenuButton and window resize listeners so the
click and resize handlers work correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: b2aa54c5-7bcb-4e42-a45d-057fcc59840a

📥 Commits

Reviewing files that changed from the base of the PR and between 01df9bc and 5af570b.

📒 Files selected for processing (1)
  • assets/js/main.js

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
assets/js/main.js (1)

90-99: ⚠️ Potential issue | 🟠 Major

Clear pending close timer in openMenu() to prevent race condition.

If the user clicks the menu button during the 300ms close transition, openMenu() is called but the pending mobileMenuTimer from closeMenu() will still fire and add hidden, leaving the menu in a broken state.

🛠️ Proposed fix
 const openMenu = () => {
+    if (mobileMenuTimer) {
+        clearTimeout(mobileMenuTimer);
+        mobileMenuTimer = null;
+    }
     mobileMenu.classList.remove('hidden');
     // Trigger reflow for transition
     mobileMenu.offsetHeight;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/main.js` around lines 90 - 99, openMenu currently doesn't cancel
the pending close timer from closeMenu, so a previously set mobileMenuTimer can
still fire and re-hide the menu; update openMenu to check for an active
mobileMenuTimer, call clearTimeout(mobileMenuTimer) and set mobileMenuTimer =
null (or undefined) before performing the open steps; ensure mobileMenuTimer is
the same timer variable used by closeMenu and is in scope so the timeout is
properly cancelled.
🧹 Nitpick comments (1)
assets/js/main.js (1)

89-89: Consider guarding against missing icon elements.

The condition only checks for mobileMenuButton and mobileMenu, but menuIcon and closeIcon are accessed unconditionally inside. If the HTML structure changes or elements fail to load, this will throw TypeError: Cannot read properties of null.

♻️ Suggested defensive check
-    if (mobileMenuButton && mobileMenu) {
+    if (mobileMenuButton && mobileMenu && menuIcon && closeIcon) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/main.js` at line 89, The click/toggle block checks only
mobileMenuButton and mobileMenu but later references menuIcon and closeIcon
unconditionally; update the guard to also verify menuIcon and closeIcon are
non-null (or early-return if any are missing) before using them, e.g., expand
the if condition that includes mobileMenuButton and mobileMenu to include
menuIcon and closeIcon or add explicit null checks inside the handler for the
menuIcon and closeIcon elements referenced in the toggle logic (symbols:
mobileMenuButton, mobileMenu, menuIcon, closeIcon).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@assets/js/main.js`:
- Around line 90-99: openMenu currently doesn't cancel the pending close timer
from closeMenu, so a previously set mobileMenuTimer can still fire and re-hide
the menu; update openMenu to check for an active mobileMenuTimer, call
clearTimeout(mobileMenuTimer) and set mobileMenuTimer = null (or undefined)
before performing the open steps; ensure mobileMenuTimer is the same timer
variable used by closeMenu and is in scope so the timeout is properly cancelled.

---

Nitpick comments:
In `@assets/js/main.js`:
- Line 89: The click/toggle block checks only mobileMenuButton and mobileMenu
but later references menuIcon and closeIcon unconditionally; update the guard to
also verify menuIcon and closeIcon are non-null (or early-return if any are
missing) before using them, e.g., expand the if condition that includes
mobileMenuButton and mobileMenu to include menuIcon and closeIcon or add
explicit null checks inside the handler for the menuIcon and closeIcon elements
referenced in the toggle logic (symbols: mobileMenuButton, mobileMenu, menuIcon,
closeIcon).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: cc9a4ceb-a62e-499b-abfe-716e30feb409

📥 Commits

Reviewing files that changed from the base of the PR and between 5af570b and 35a9def.

📒 Files selected for processing (1)
  • assets/js/main.js

@Jozz77
Copy link
Copy Markdown
Contributor Author

Jozz77 commented Mar 9, 2026

@arnavkirti Please I've implemented the coderabbit suggestions.

Copy link
Copy Markdown
Contributor

@DonnieBLT DonnieBLT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fix the conflicts

…Fix-Navigation-hamburger-menu-icon-missing-on-mobile-viewports
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
assets/js/main.js (1)

14-29: Trim filter text and null-guard data-search before .includes().

This keeps whitespace-only input from acting like a real query and prevents a runtime error if a card is missing data-search.

♻️ Suggested hardening
-            const searchValue = searchFilter.value.toLowerCase();
+            const searchValue = searchFilter.value.trim().toLowerCase();
...
-                const courseSearch = course.getAttribute('data-search');
+                const courseSearch = course.getAttribute('data-search') ?? '';
...
-                const searchMatch = !searchValue || courseSearch.includes(searchValue);
+                const searchMatch = !searchValue || courseSearch.includes(searchValue);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/main.js` around lines 14 - 29, The filter logic uses raw input and
directly calls .includes on a possibly missing attribute; trim and null-guard
the values: call .trim().toLowerCase() on searchFilter.value (and optionally
categoryFilter.value and levelFilter.value) when creating
searchValue/categoryValue/levelValue, and guard
course.getAttribute('data-search') before using .includes — e.g., normalize
courseSearch to an empty string if null, then perform the includes check in the
course filtering block (references: searchFilter, categoryFilter, levelFilter,
coursesGrid, 'course-card', data-search).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@assets/js/main.js`:
- Around line 14-29: The filter logic uses raw input and directly calls
.includes on a possibly missing attribute; trim and null-guard the values: call
.trim().toLowerCase() on searchFilter.value (and optionally categoryFilter.value
and levelFilter.value) when creating searchValue/categoryValue/levelValue, and
guard course.getAttribute('data-search') before using .includes — e.g.,
normalize courseSearch to an empty string if null, then perform the includes
check in the course filtering block (references: searchFilter, categoryFilter,
levelFilter, coursesGrid, 'course-card', data-search).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: cdfd9a43-d760-4150-bdb2-3ec4190f934b

📥 Commits

Reviewing files that changed from the base of the PR and between 35a9def and f8c4249.

📒 Files selected for processing (1)
  • assets/js/main.js

@Jozz77
Copy link
Copy Markdown
Contributor Author

Jozz77 commented Mar 13, 2026

@DonnieBLT thanks for your feedback

Merge conflicts have now been resolved

…Fix-Navigation-hamburger-menu-icon-missing-on-mobile-viewports
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Navigation hamburger menu icon missing on mobile viewports

4 participants