diff --git a/static/css/main.css b/static/css/main.css index c13627c..036c20e 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -2926,15 +2926,50 @@ a.citation-count-badge:hover { font-size: 0.95rem; } + .nav-item.has-megamenu:hover .nav-arrow { + transform: none; + } + + .nav-item.has-megamenu.open .nav-arrow { + transform: rotate(180deg); + } + .megamenu { position: static; - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--glass-border); + max-height: 0; + overflow: hidden; + margin: 0; + padding: 0; + border: none; border-radius: var(--radius-md); transform: none; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none; + opacity: 0; + visibility: hidden; + pointer-events: none; + } + + .nav-item.has-megamenu:hover .megamenu { + opacity: 0; + visibility: hidden; + max-height: 0; + pointer-events: none; + transform: none; + } + + .nav-item.has-megamenu.open .megamenu { + max-height: 2000px; + overflow: visible; + margin-top: 0.25rem; + padding: 0; + background: rgba(255, 255, 255, 0.03); + border: 1px solid var(--glass-border); + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: none; } .megamenu-inner { @@ -2956,14 +2991,28 @@ a.citation-count-badge:hover { max-width: none; } - .megamenu-product-item.has-subproducts .megamenu-sublist { - max-height: none; - opacity: 1; - border-top-color: var(--glass-border); + .megamenu-product-item.has-subproducts:hover .megamenu-sub-chevron, + .megamenu-product-item.has-subproducts:focus-within .megamenu-sub-chevron { + transform: none; + color: var(--text-muted); } - .nav-item.has-megamenu:hover .megamenu { - transform: none; + .megamenu-product-item.has-subproducts:hover .megamenu-sublist, + .megamenu-product-item.has-subproducts:focus-within .megamenu-sublist { + max-height: 0; + opacity: 0; + border-top-color: transparent; + } + + .megamenu-product-item.has-subproducts.open .megamenu-sub-chevron { + transform: rotate(180deg); + color: var(--accent-blue-light); + } + + .megamenu-product-item.has-subproducts.open .megamenu-sublist { + max-height: 320px; + opacity: 1; + border-top-color: var(--glass-border); } .sub-product-grid { diff --git a/static/js/main.js b/static/js/main.js index 38bac99..39f4c4f 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -12,6 +12,12 @@ const SELECTORS = { citationCopy: '.citation-copy', }; +const MOBILE_NAV_MQ = window.matchMedia('(max-width: 768px)'); + +function isMobileNav() { + return MOBILE_NAV_MQ.matches; +} + function initNavbarScroll() { const navbar = document.querySelector(SELECTORS.navbar); if (!navbar) return; @@ -28,6 +34,26 @@ function initNavbarScroll() { onScroll(); } +function closeMobileNavMenu() { + const toggle = document.querySelector(SELECTORS.navbarToggle); + const menu = document.querySelector(SELECTORS.navbarMenu); + const productsItem = document.querySelector(SELECTORS.productsNavItem); + if (!toggle || !menu) return; + + menu.classList.remove('open'); + toggle.classList.remove('open'); + toggle.setAttribute('aria-expanded', 'false'); + + if (productsItem) { + productsItem.classList.remove('open'); + const productsTrigger = productsItem.querySelector('.nav-link'); + if (productsTrigger) productsTrigger.setAttribute('aria-expanded', 'false'); + productsItem.querySelectorAll('.megamenu-product-item.has-subproducts.open').forEach((item) => { + item.classList.remove('open'); + }); + } +} + function initMobileMenu() { const toggle = document.querySelector(SELECTORS.navbarToggle); const menu = document.querySelector(SELECTORS.navbarMenu); @@ -37,22 +63,26 @@ function initMobileMenu() { const isOpen = menu.classList.toggle('open'); toggle.classList.toggle('open', isOpen); toggle.setAttribute('aria-expanded', String(isOpen)); + if (!isOpen) { + const productsItem = document.querySelector(SELECTORS.productsNavItem); + if (productsItem) { + productsItem.classList.remove('open'); + const productsTrigger = productsItem.querySelector('.nav-link'); + if (productsTrigger) productsTrigger.setAttribute('aria-expanded', 'false'); + } + } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && menu.classList.contains('open')) { - menu.classList.remove('open'); - toggle.classList.remove('open'); - toggle.setAttribute('aria-expanded', 'false'); + closeMobileNavMenu(); toggle.focus(); } }); document.addEventListener('click', (e) => { if (!menu.contains(e.target) && !toggle.contains(e.target)) { - menu.classList.remove('open'); - toggle.classList.remove('open'); - toggle.setAttribute('aria-expanded', 'false'); + closeMobileNavMenu(); } }); } @@ -80,18 +110,21 @@ function initMegaMenu() { } function openMenu() { + if (isMobileNav()) return; cancelClose(); productsItem.classList.add('open'); trigger.setAttribute('aria-expanded', 'true'); } function beginClose() { + if (isMobileNav()) return; productsItem.classList.remove('open'); trigger.setAttribute('aria-expanded', 'false'); megamenu.classList.add('is-closing'); } function scheduleClose(event) { + if (isMobileNav()) return; if (event && isPointerInMenu(event.relatedTarget)) return; clearTimeout(hideTimer); @@ -111,9 +144,21 @@ function initMegaMenu() { megamenu.addEventListener('mouseenter', openMenu); megamenu.addEventListener('mouseleave', scheduleClose); + trigger.addEventListener('click', (e) => { + if (!isMobileNav()) return; + e.preventDefault(); + const isOpen = productsItem.classList.toggle('open'); + trigger.setAttribute('aria-expanded', String(isOpen)); + }); + trigger.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); + if (isMobileNav()) { + const isOpen = productsItem.classList.toggle('open'); + trigger.setAttribute('aria-expanded', String(isOpen)); + return; + } const isOpen = productsItem.classList.toggle('open'); if (isOpen) { openMenu(); @@ -124,10 +169,27 @@ function initMegaMenu() { trigger.setAttribute('aria-expanded', String(isOpen)); } if (e.key === 'Escape') { + if (isMobileNav()) { + productsItem.classList.remove('open'); + trigger.setAttribute('aria-expanded', 'false'); + return; + } cancelClose(); beginClose(); } }); + + productsItem.querySelectorAll('.megamenu-product-item.has-subproducts').forEach((item) => { + const link = item.querySelector('.megamenu-product-link'); + if (!link) return; + + link.addEventListener('click', (e) => { + if (!isMobileNav()) return; + if (item.classList.contains('open')) return; + e.preventDefault(); + item.classList.add('open'); + }); + }); } function initFAQAccordion() {