admin: fix mobile sidebar menu inaccessible in portrait mode (#8556)

* admin: fix mobile sidebar menu inaccessible in portrait mode

The hamburger button only toggled the user dropdown, leaving the
sidebar navigation inaccessible on mobile devices in portrait mode.

Add a dedicated sidebar toggle button (visible only on mobile), give
the sidebar an id so Bootstrap collapse can target it, add a backdrop
overlay for the open state, and auto-close the sidebar when a nav
link is clicked.

Fixes #8550

* admin: address review feedback on mobile sidebar

- Remove redundant JS show/hide.bs.collapse listeners; CSS sibling
  selector already handles backdrop visibility
- Use const instead of var for non-reassigned variables
- Move inline style on user icon to CSS class

* admin: add aria attributes to user-menu toggler, use CSS variable for navbar height

- Add aria-controls, aria-expanded, and aria-label to the user-menu
  toggle button for assistive technology
- Extract hard-coded 56px navbar height into --navbar-height CSS
  custom property used by sidebar and backdrop positioning

* admin: extract hideSidebar helper, use toggler visibility for breakpoint check

- Extract duplicated collapse-hide logic into a hideSidebar helper
- Replace hardcoded window.innerWidth < 768 with a check on the
  sidebar toggler's computed display, decoupling JS from CSS breakpoints
- Add aria-expanded="false" to sidebar toggle button

---------

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Chris Lu
2026-03-08 12:32:14 -07:00
committed by GitHub
parent 587c24ec89
commit e25558e4d8
4 changed files with 85 additions and 21 deletions

View File

@@ -31,6 +31,9 @@ function initializeDashboard() {
// Set up submenu behavior
setupSubmenuBehavior();
// Set up mobile sidebar behavior
setupMobileSidebar();
}
// HTMX event listeners
@@ -194,6 +197,33 @@ function setupSubmenuBehavior() {
}
// Mobile sidebar toggle and backdrop behavior
function setupMobileSidebar() {
const sidebar = document.getElementById('sidebarMenu');
const backdrop = document.getElementById('sidebarBackdrop');
if (!sidebar || !backdrop) return;
const hideSidebar = () => {
const bsCollapse = bootstrap.Collapse.getInstance(sidebar);
if (bsCollapse) {
bsCollapse.hide();
}
};
// Close sidebar when backdrop is clicked
backdrop.addEventListener('click', hideSidebar);
// Close sidebar when a nav link is clicked (on mobile)
const sidebarToggler = document.querySelector("button[data-bs-target='#sidebarMenu']");
sidebar.querySelectorAll('a.nav-link:not([data-bs-toggle="collapse"])').forEach(function (link) {
link.addEventListener('click', function () {
if (sidebarToggler && getComputedStyle(sidebarToggler).display !== 'none') {
hideSidebar();
}
});
});
}
// Loading indicator functions
function showLoadingIndicator() {
const indicator = document.getElementById('loading-indicator');