/** * Navigation Component * Handles mobile menu, dropdowns, and accessibility */ (function () { "use strict"; class Navigation { constructor() { this.mobileMenuToggle = document.getElementById("mobileMenuToggle"); this.mobileMenu = document.getElementById("mobileMenu"); this.mobileMenuClose = document.getElementById("mobileMenuClose"); this.overlay = document.getElementById("mobileMenuOverlay"); this.body = document.body; this.init(); } init() { this.setupMobileMenu(); this.setupAccessibility(); this.highlightCurrentPage(); this.setupKeyboardNavigation(); } setupMobileMenu() { // Open mobile menu if (this.mobileMenuToggle) { this.mobileMenuToggle.addEventListener("click", () => this.openMobileMenu() ); } // Close mobile menu if (this.mobileMenuClose) { this.mobileMenuClose.addEventListener("click", () => this.closeMobileMenu() ); } if (this.overlay) { this.overlay.addEventListener("click", () => this.closeMobileMenu()); } // Close on ESC key document.addEventListener("keydown", (e) => { if ( e.key === "Escape" && this.mobileMenu && this.mobileMenu.classList.contains("active") ) { this.closeMobileMenu(); } }); } openMobileMenu() { if (this.mobileMenu) { this.mobileMenu.classList.add("active"); this.mobileMenu.setAttribute("aria-hidden", "false"); this.body.style.overflow = "hidden"; if (this.overlay) { this.overlay.classList.add("active"); } // Focus first link const firstLink = this.mobileMenu.querySelector("a"); if (firstLink) { setTimeout(() => firstLink.focus(), 100); } } } closeMobileMenu() { if (this.mobileMenu) { this.mobileMenu.classList.remove("active"); this.mobileMenu.setAttribute("aria-hidden", "true"); this.body.style.overflow = ""; if (this.overlay) { this.overlay.classList.remove("active"); } // Return focus to toggle button if (this.mobileMenuToggle) { this.mobileMenuToggle.focus(); } } } setupAccessibility() { // Wait for body to exist if (!document.body) return; // Add ARIA labels to nav items const navLinks = document.querySelectorAll(".nav-link"); navLinks.forEach((link) => { if (!link.getAttribute("aria-label")) { link.setAttribute( "aria-label", `Navigate to ${link.textContent.trim()}` ); } }); // Add skip to main content link if (!document.getElementById("skip-to-main")) { const skipLink = document.createElement("a"); skipLink.id = "skip-to-main"; skipLink.href = "#main-content"; skipLink.textContent = "Skip to main content"; skipLink.className = "skip-link"; document.body.insertBefore(skipLink, document.body.firstChild); // Add styles for skip link if (!document.getElementById("skip-link-styles")) { const style = document.createElement("style"); style.id = "skip-link-styles"; style.textContent = ` .skip-link { position: fixed; top: -100px; left: 0; padding: 10px 20px; background: #000; color: #fff; z-index: 10001; text-decoration: none; border-radius: 0 0 8px 0; } .skip-link:focus { top: 0; } `; document.head.appendChild(style); } } } highlightCurrentPage() { const currentPath = window.location.pathname; const navLinks = document.querySelectorAll(".nav-link, .mobile-link"); navLinks.forEach((link) => { const href = link.getAttribute("href"); if ( href && (currentPath === href || currentPath.startsWith(href + "/")) ) { link.classList.add("active"); link.setAttribute("aria-current", "page"); } else { link.classList.remove("active"); link.removeAttribute("aria-current"); } }); } setupKeyboardNavigation() { // Tab trap in mobile menu when open if (this.mobileMenu) { const focusableElements = this.mobileMenu.querySelectorAll( 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])' ); if (focusableElements.length > 0) { const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; this.mobileMenu.addEventListener("keydown", (e) => { if ( e.key === "Tab" && this.mobileMenu.classList.contains("active") ) { if (e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } }); } } } } // Initialize navigation when DOM is ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { new Navigation(); }); } else { new Navigation(); } })();