From 631547a75142326a7c71bdf123e1475217a5ad73 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 22 Apr 2026 12:42:56 +0200 Subject: chore: replace with extracted danix.xyz-hacker theme (danix2-hugo-theme) --- assets/js/menu.js | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 assets/js/menu.js (limited to 'assets/js/menu.js') diff --git a/assets/js/menu.js b/assets/js/menu.js new file mode 100644 index 0000000..3f32642 --- /dev/null +++ b/assets/js/menu.js @@ -0,0 +1,112 @@ +document.addEventListener('DOMContentLoaded', () => { + const menuToggle = document.getElementById('menu-toggle'); + const menuOverlay = document.getElementById('menu-overlay'); + const menuPanel = document.getElementById('hamburger-menu'); + + function openMenu() { + if (!menuOverlay || !menuPanel) return; + + // Show overlay + menuOverlay.classList.remove('opacity-0'); + menuOverlay.classList.remove('invisible'); + + // Slide menu in + menuPanel.classList.remove('translate-x-full'); + + // Manage accessibility + menuToggle.setAttribute('aria-expanded', 'true'); + menuPanel.removeAttribute('aria-hidden'); + + // Control body overflow + document.body.style.overflow = 'hidden'; + + // Focus first focusable element in menu + const firstFocusable = menuPanel.querySelector('a, button'); + if (firstFocusable) { + setTimeout(() => firstFocusable.focus(), 50); + } + } + + function closeMenu() { + if (!menuOverlay || menuOverlay.classList.contains('opacity-0')) return; + + // Hide overlay + menuOverlay.classList.add('opacity-0'); + menuOverlay.classList.add('invisible'); + + // Slide menu out + menuPanel.classList.add('translate-x-full'); + + // Manage accessibility + menuToggle.setAttribute('aria-expanded', 'false'); + menuPanel.setAttribute('aria-hidden', 'true'); + + // Restore body overflow + document.body.style.overflow = ''; + + // Return focus to toggle button + menuToggle.focus(); + } + + function toggleMenu() { + if (menuOverlay && menuOverlay.classList.contains('opacity-0')) { + openMenu(); + } else { + closeMenu(); + } + } + + // Toggle menu when clicking the hamburger button + if (menuToggle) { + menuToggle.addEventListener('click', toggleMenu); + } + + // Close menu when clicking on the overlay + if (menuOverlay) { + menuOverlay.addEventListener('click', (e) => { + if (e.target === menuOverlay) { + closeMenu(); + } + }); + } + + // Close menu when clicking menu items + const menuLinks = document.querySelectorAll('#hamburger-menu a, #hamburger-menu button'); + menuLinks.forEach(link => { + link.addEventListener('click', closeMenu); + }); + + // Close menu on Escape key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && menuOverlay && !menuOverlay.classList.contains('opacity-0')) { + closeMenu(); + } + }); + + // Focus trap: keep tab within menu when open + if (menuPanel) { + menuPanel.addEventListener('keydown', (e) => { + if (e.key !== 'Tab') return; + + const focusableElements = menuPanel.querySelectorAll('a, button, [tabindex]:not([tabindex="-1"])'); + if (focusableElements.length === 0) return; + + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + const isMenuOpen = !menuOverlay.classList.contains('opacity-0'); + + if (!isMenuOpen) return; + + // Shift+Tab on first element: move to last + if (e.shiftKey && document.activeElement === firstElement) { + e.preventDefault(); + lastElement.focus(); + } + // Tab on last element: move to first + else if (!e.shiftKey && document.activeElement === lastElement) { + e.preventDefault(); + firstElement.focus(); + } + }); + } +}); -- cgit v1.2.3