/** * Tractatus Framework - Responsive Navbar Component * Consistent, mobile-friendly navigation across all pages */ class TractatusNavbar { constructor() { this.mobileMenuOpen = false; this.init(); } init() { this.render(); this.attachEventListeners(); this.setActivePageIndicator(); } render() { const navHTML = ` `; // Always insert navbar at the very beginning of body // Check if there's already a tractatus navbar (to avoid duplicates) const existingNavbar = document.querySelector('nav.bg-white.border-b.border-gray-200.sticky'); if (existingNavbar) { existingNavbar.outerHTML = navHTML; } else { const placeholder = document.getElementById('navbar-placeholder'); if (placeholder) { placeholder.outerHTML = navHTML; } else { document.body.insertAdjacentHTML('afterbegin', navHTML); } } } attachEventListeners() { // Mobile Menu (Navigation Drawer) const mobileMenuBtn = document.getElementById('mobile-menu-btn'); const mobileMenuCloseBtn = document.getElementById('mobile-menu-close-btn'); const mobileMenu = document.getElementById('mobile-menu'); const mobileMenuPanel = document.getElementById('mobile-menu-panel'); const mobileMenuBackdrop = document.getElementById('mobile-menu-backdrop'); const toggleMobileMenu = () => { this.mobileMenuOpen = !this.mobileMenuOpen; if (this.mobileMenuOpen) { // Open: Show menu and slide panel in from right mobileMenu.classList.remove('hidden'); // Use setTimeout to ensure display change happens before animation setTimeout(() => { mobileMenuPanel.classList.remove('translate-x-full'); mobileMenuPanel.classList.add('translate-x-0'); }, 10); document.body.style.overflow = 'hidden'; // Prevent scrolling when menu is open } else { // Close: Slide panel out to right mobileMenuPanel.classList.remove('translate-x-0'); mobileMenuPanel.classList.add('translate-x-full'); // Hide menu after animation completes (300ms) setTimeout(() => { mobileMenu.classList.add('hidden'); }, 300); document.body.style.overflow = ''; } }; // Initialize panel in hidden state (off-screen to the right) if (mobileMenuPanel) { mobileMenuPanel.classList.add('translate-x-full'); } if (mobileMenuBtn) { mobileMenuBtn.addEventListener('click', toggleMobileMenu); } if (mobileMenuCloseBtn) { mobileMenuCloseBtn.addEventListener('click', toggleMobileMenu); } if (mobileMenuBackdrop) { mobileMenuBackdrop.addEventListener('click', toggleMobileMenu); } // Close mobile menu on navigation const mobileLinks = document.querySelectorAll('#mobile-menu a'); mobileLinks.forEach(link => { link.addEventListener('click', () => { if (this.mobileMenuOpen) { toggleMobileMenu(); } }); }); } setActivePageIndicator() { // Get current page path const currentPath = window.location.pathname; // Normalize paths (handle both /page.html and /page) const normalizePath = (path) => { if (path === '/' || path === '/index.html') return '/'; return path.replace('.html', '').replace(/\/$/, ''); }; const normalizedCurrent = normalizePath(currentPath); // Find all navigation links in mobile menu const mobileLinks = document.querySelectorAll('#mobile-menu a'); mobileLinks.forEach(link => { const linkPath = link.getAttribute('href'); const normalizedLink = normalizePath(linkPath); if (normalizedLink === normalizedCurrent) { // Add active styling with brand colors link.classList.add('border-l-4', 'bg-sky-50'); link.style.borderLeftColor = 'var(--tractatus-core-end)'; link.style.color = 'var(--tractatus-core-end)'; link.classList.remove('text-gray-700'); } }); } } // Auto-initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => new TractatusNavbar()); } else { new TractatusNavbar(); }