+<!-- Mobile menu overlay (Alpine.js controlled) -->
<div
- id="menu-overlay"
- class="fixed inset-0 bg-black/50 backdrop-blur opacity-0 invisible transition-all duration-300 z-40"
- aria-hidden="true"
+ x-data="{ menuOpen: false }"
+ @toggle-menu.window="menuOpen = !menuOpen"
+ @keydown.escape.window="menuOpen = false"
+ :class="{ 'opacity-0 invisible': !menuOpen, 'opacity-100 visible': menuOpen }"
+ class="fixed inset-0 bg-black/50 backdrop-blur transition-all duration-300 z-40"
+ :aria-hidden="!menuOpen"
+ @click="if ($event.target === $el) menuOpen = false"
>
<div
- id="hamburger-menu"
- class="fixed top-0 right-0 h-screen w-full max-w-sm bg-bg border-l border-border overflow-y-auto transform translate-x-full transition-transform duration-300 z-50"
+ class="fixed top-0 right-0 h-screen w-full max-w-sm bg-bg border-l border-border overflow-y-auto transform transition-transform duration-300 z-50"
+ :class="{ 'translate-x-full': !menuOpen, 'translate-x-0': menuOpen }"
>
<!-- Close button -->
<div class="flex items-center justify-between p-6 border-b border-border">
<span class="font-bold text-lg text-accent font-oxanium">Menu</span>
<button
- id="menu-close"
+ @click="menuOpen = false"
aria-label="{{ i18n "closeMenu" }}"
class="p-2 hover:bg-surface rounded transition-colors"
>
{{ range .Site.Menus.main }}
<a
href="{{ .URL }}"
+ @click="menuOpen = false"
class="block py-4 text-lg font-medium hover:text-accent transition-colors border-b border-border/30"
>
{{ i18n .Name }}
{{ end }}
<a
href="{{ $url }}"
+ @click="menuOpen = false"
class="flex-1 py-2 px-3 text-center rounded transition-colors {{ if $current }}bg-accent text-white{{ else }}bg-surface hover:bg-surface/80{{ end }}"
>
{{ $langName }}
</div>
<script>
- function initializeHamburgerMenu() {
+ // Make menuOpen accessible from the menu toggle button
+ document.addEventListener('DOMContentLoaded', () => {
const menuToggle = document.getElementById('menu-toggle');
- const menuClose = document.getElementById('menu-close');
- const menuOverlay = document.getElementById('menu-overlay');
- const hamburgerMenu = document.getElementById('hamburger-menu');
-
- if (!menuToggle || !menuOverlay || !hamburgerMenu) {
- console.warn('Hamburger menu elements not found', {
- menuToggle: !!menuToggle,
- menuOverlay: !!menuOverlay,
- hamburgerMenu: !!hamburgerMenu
- });
- return;
- }
-
- const openMenu = () => {
- menuOverlay.classList.remove('opacity-0', 'invisible');
- hamburgerMenu.classList.remove('translate-x-full');
- menuOverlay.setAttribute('aria-hidden', 'false');
- document.body.style.overflow = 'hidden';
- menuToggle.setAttribute('aria-expanded', 'true');
- };
-
- const closeMenu = () => {
- menuOverlay.classList.add('opacity-0', 'invisible');
- hamburgerMenu.classList.add('translate-x-full');
- menuOverlay.setAttribute('aria-hidden', 'true');
- document.body.style.overflow = '';
- menuToggle.setAttribute('aria-expanded', 'false');
- };
-
- // Toggle button click
- menuToggle.addEventListener('click', (e) => {
- e.preventDefault();
- e.stopPropagation();
- if (menuOverlay.classList.contains('opacity-0')) {
- openMenu();
- } else {
- closeMenu();
- }
- });
-
- // Close button click
- if (menuClose) {
- menuClose.addEventListener('click', (e) => {
+ if (menuToggle && window.__alpineInstances) {
+ menuToggle.addEventListener('click', (e) => {
e.preventDefault();
- closeMenu();
+ // Find the Alpine component and toggle it
+ const overlay = document.querySelector('[x-data*="menuOpen"]');
+ if (overlay && overlay.__x) {
+ overlay.__x.$data.menuOpen = !overlay.__x.$data.menuOpen;
+ }
});
}
-
- // Close on ESC key
- document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape' && !menuOverlay.classList.contains('opacity-0')) {
- closeMenu();
- }
- });
-
- // Close when clicking menu links
- const menuLinks = hamburgerMenu.querySelectorAll('a');
- menuLinks.forEach(link => {
- link.addEventListener('click', (e) => {
- closeMenu();
- });
- });
-
- // Close when clicking the overlay (but not the menu panel)
- menuOverlay.addEventListener('click', (e) => {
- if (e.target === menuOverlay) {
- closeMenu();
- }
- });
- }
-
- // Run when DOM is ready
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', initializeHamburgerMenu);
- } else {
- initializeHamburgerMenu();
- }
+ });
</script>