refactor: enhance dropdown functionality for improved mobile responsiveness and accessibility
This commit is contained in:
parent
3defd1b68c
commit
92bb628a2b
2 changed files with 54 additions and 14 deletions
11
dist/css/styles.css
vendored
11
dist/css/styles.css
vendored
|
|
@ -865,6 +865,9 @@ button:focus-visible {
|
|||
.nav-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
/* Improve touch responsiveness */
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.nav-dropdown-trigger {
|
||||
|
|
@ -874,6 +877,11 @@ button:focus-visible {
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
/* Improve touch responsiveness */
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
touch-action: manipulation;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
|
|
@ -980,6 +988,9 @@ button:focus-visible {
|
|||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease;
|
||||
/* Prevent layout shift and improve performance */
|
||||
will-change: max-height;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-dropdown-menu {
|
||||
|
|
|
|||
57
dist/index.html
vendored
57
dist/index.html
vendored
|
|
@ -1746,9 +1746,22 @@
|
|||
// Navigation dropdowns
|
||||
const dropdownTriggers = document.querySelectorAll('.nav-dropdown-trigger');
|
||||
|
||||
// Function to close all dropdowns
|
||||
function closeAllDropdowns() {
|
||||
dropdownTriggers.forEach(trigger => {
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
}
|
||||
|
||||
dropdownTriggers.forEach(trigger => {
|
||||
trigger.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// On mobile, only allow dropdowns when nav menu is open
|
||||
if (window.innerWidth <= 900 && !bodyEl.classList.contains('nav-open')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
// Close all other dropdowns
|
||||
|
|
@ -1759,25 +1772,25 @@
|
|||
});
|
||||
|
||||
// Toggle current dropdown
|
||||
trigger.setAttribute('aria-expanded', !isExpanded);
|
||||
});
|
||||
trigger.setAttribute('aria-expanded', String(!isExpanded));
|
||||
}, { passive: false });
|
||||
});
|
||||
|
||||
// Close dropdowns when clicking outside
|
||||
document.addEventListener('click', () => {
|
||||
dropdownTriggers.forEach(trigger => {
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
});
|
||||
// Close dropdowns when clicking outside (only on desktop)
|
||||
document.addEventListener('click', (e) => {
|
||||
// Don't close if clicking on a dropdown trigger or menu
|
||||
const isDropdownClick = e.target.closest('.nav-dropdown');
|
||||
if (!isDropdownClick && window.innerWidth > 900) {
|
||||
closeAllDropdowns();
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
// Close dropdowns on escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
dropdownTriggers.forEach(trigger => {
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
closeAllDropdowns();
|
||||
}
|
||||
});
|
||||
}, { passive: true });
|
||||
|
||||
// Mark JS ready to enable transitions after initial paint (prevents slide-in glitch)
|
||||
requestAnimationFrame(() => bodyEl.classList.add('js-ready'));
|
||||
|
|
@ -1804,6 +1817,8 @@
|
|||
nav.setAttribute('aria-hidden', bodyEl.classList.contains('nav-open') ? 'false' : 'true');
|
||||
} else {
|
||||
nav.removeAttribute('aria-hidden');
|
||||
// Restore scroll when switching to desktop
|
||||
document.body.style.overflow = '';
|
||||
if (bodyEl.classList.contains('nav-open')) {
|
||||
closeMenu({ restoreFocus: false });
|
||||
} else {
|
||||
|
|
@ -1821,6 +1836,12 @@
|
|||
navToggle?.setAttribute('aria-expanded', 'true');
|
||||
if (navOverlay) navOverlay.hidden = false;
|
||||
nav.setAttribute('data-open', 'true');
|
||||
|
||||
// Prevent scroll on mobile when menu is open
|
||||
if (window.innerWidth <= 900) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
const firstLink = nav.querySelector('a');
|
||||
firstLink && firstLink.focus();
|
||||
trapFocus(nav);
|
||||
|
|
@ -1833,6 +1854,13 @@
|
|||
navToggle?.setAttribute('aria-expanded', 'false');
|
||||
if (navOverlay) navOverlay.hidden = true;
|
||||
nav.removeAttribute('data-open');
|
||||
|
||||
// Restore scroll
|
||||
document.body.style.overflow = '';
|
||||
|
||||
// Close all dropdowns when closing menu
|
||||
closeAllDropdowns();
|
||||
|
||||
if (restoreFocus && lastFocusedBeforeMenu) {
|
||||
lastFocusedBeforeMenu.focus();
|
||||
}
|
||||
|
|
@ -1848,7 +1876,8 @@
|
|||
}
|
||||
if (nav) {
|
||||
nav.addEventListener('click', (event) => {
|
||||
const target = event.target instanceof Element ? event.target.closest('.nav-link') : null;
|
||||
// Only close menu when clicking actual links, not dropdown triggers
|
||||
const target = event.target instanceof Element ? event.target.closest('a.nav-link, a.nav-dropdown-item') : null;
|
||||
if (!target) return;
|
||||
if (window.innerWidth <= 900) closeMenu();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue