Merge pull request #325 from PR0M3TH3AN/codex/fix-sidebar-ui/ux-issues

Improve mobile sidebar interactions
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-06 12:03:01 -04:00
commit 7286295a3a
4 files changed
+122 -25

No files matched your search

+41 -7
View File
@@ -1419,26 +1419,52 @@ footer a:hover {
left: 0;
width: 16rem; /* Tailwind's w-64 */
height: 100vh;
height: 100dvh;
background-color: #0f172a;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
transition: transform 0.3s ease;
transition: transform 0.3s ease, box-shadow 0.3s ease;
/* Keep below modal stacks (z-50+) so dialogs overlay the drawer. */
z-index: 40;
box-shadow: none;
}
#sidebar.sidebar-open {
box-shadow: 0 0.75rem 1.5rem rgba(15, 23, 42, 0.45);
}
#sidebarOverlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.55);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
/* Sits above page chrome but below modals and the sidebar itself. */
z-index: 30;
}
body.sidebar-open #sidebarOverlay {
opacity: 1;
pointer-events: auto;
}
/* Mobile (max-width: 767px): Hide sidebar by default */
@media (max-width: 767px) {
#sidebar {
transform: translateX(-100%);
transform: translate3d(-100%, 0, 0);
width: min(16rem, calc(100vw - 3rem));
}
/* When the sidebar-open class is added, slide the sidebar in */
#sidebar.sidebar-open {
transform: translateX(0);
transform: translate3d(0, 0, 0);
}
/* Optionally shift main content when sidebar is open */
#app.sidebar-open {
transform: translateX(16rem);
transition: transform 0.3s ease;
body.sidebar-open {
overflow: hidden;
}
}
@@ -1447,6 +1473,14 @@ footer a:hover {
#sidebar {
transform: translateX(0) !important;
}
#sidebar.sidebar-open {
box-shadow: none;
}
#sidebarOverlay {
display: none;
}
}
/* Collapsed/expanded classes if needed on desktop */
+10 -4
View File
@@ -51,19 +51,21 @@
<link href="css/markdown.css" rel="stylesheet" />
</head>
<body class="bg-gray-100">
<!--
<!--
SIDEBAR CONTAINER:
The <aside id="sidebar"> inside handles visibility.
The <aside id="sidebar"> inside handles visibility.
By default, no special position or z-index on #sidebarContainer.
-->
<div id="sidebarContainer">
<!-- components/sidebar.html gets injected here -->
</div>
<!--
<div id="sidebarOverlay" class="md:hidden" aria-hidden="true"></div>
<!--
MAIN CONTENT:
md:ml-64 ensures content is shifted on desktop so the pinned sidebar doesn't overlap.
On mobile, we also toggle .sidebar-open to shift the entire content.
On mobile we float the sidebar over the content and dim the background overlay.
-->
<!--
`fade-in` is scrubbed by js/index.js after the first animation so that
@@ -84,6 +86,10 @@
<button
id="mobileMenuBtn"
class="md:hidden ml-2 mr-4 flex items-center justify-center w-10 h-10 rounded-full bg-transparent hover:bg-transparent focus:outline-none focus:ring-2 focus:ring-[#0f172a] z-[60] fade-in"
type="button"
aria-expanded="false"
aria-controls="sidebar"
aria-label="Toggle navigation menu"
>
<img
src="assets/svg/mobile-sidebar-menu-icon.svg"
+56 -7
View File
@@ -170,12 +170,61 @@ async function bootstrapInterface() {
const mobileMenuBtn = document.getElementById("mobileMenuBtn");
const sidebar = document.getElementById("sidebar");
const appElement = document.getElementById("app");
if (mobileMenuBtn && sidebar && appElement) {
mobileMenuBtn.addEventListener("click", () => {
sidebar.classList.toggle("sidebar-open");
appElement.classList.toggle("sidebar-open");
});
const sidebarOverlay = document.getElementById("sidebarOverlay");
const setSidebarState = (isOpen) => {
if (sidebar) {
sidebar.classList.toggle("sidebar-open", isOpen);
}
document.body.classList.toggle("sidebar-open", isOpen);
if (mobileMenuBtn) {
mobileMenuBtn.setAttribute("aria-expanded", isOpen ? "true" : "false");
}
};
const closeSidebar = () => setSidebarState(false);
const isMobileViewport = () => {
if (typeof window.matchMedia === "function") {
return window.matchMedia("(max-width: 767px)").matches;
}
return window.innerWidth < 768;
};
const toggleSidebar = () => {
if (!sidebar) return;
const isMobile = isMobileViewport();
if (!isMobile) return;
const shouldOpen = !sidebar.classList.contains("sidebar-open");
setSidebarState(shouldOpen);
};
if (mobileMenuBtn && sidebar) {
mobileMenuBtn.addEventListener("click", toggleSidebar);
}
if (sidebarOverlay) {
sidebarOverlay.addEventListener("click", closeSidebar);
}
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && sidebar && sidebar.classList.contains("sidebar-open")) {
closeSidebar();
}
});
if (typeof window.matchMedia === "function") {
const desktopQuery = window.matchMedia("(min-width: 768px)");
const onDesktopChange = (event) => {
if (event.matches) {
closeSidebar();
}
};
if (typeof desktopQuery.addEventListener === "function") {
desktopQuery.addEventListener("change", onDesktopChange);
} else if (typeof desktopQuery.addListener === "function") {
desktopQuery.addListener(onDesktopChange);
}
}
const footerDropdownButton = document.getElementById("footerDropdownButton");
@@ -195,7 +244,7 @@ async function bootstrapInterface() {
try {
const sidebarModule = await import("./sidebar.js");
if (typeof sidebarModule.setupSidebarNavigation === "function") {
sidebarModule.setupSidebarNavigation();
sidebarModule.setupSidebarNavigation({ closeSidebar });
}
} catch (error) {
console.error("Failed to set up sidebar navigation:", error);
+15 -7
View File
@@ -1,7 +1,7 @@
// sidebar.js
import { setHashView } from "./index.js";
export function setupSidebarNavigation() {
export function setupSidebarNavigation({ closeSidebar } = {}) {
const sidebarLinks = document.querySelectorAll('#sidebar a[href^="#view="]');
sidebarLinks.forEach((link) => {
link.addEventListener("click", (e) => {
@@ -16,13 +16,21 @@ export function setupSidebarNavigation() {
setHashView(viewName); // This changes the hash and loads the view.
// --- NEW: if on mobile, close the sidebar automatically. ---
if (window.innerWidth < 768) {
const sidebar = document.getElementById("sidebar");
const app = document.getElementById("app");
if (sidebar && app) {
sidebar.classList.remove("sidebar-open");
app.classList.remove("sidebar-open");
const isMobile =
typeof window.matchMedia === "function"
? window.matchMedia("(max-width: 767px)").matches
: window.innerWidth < 768;
if (isMobile) {
if (typeof closeSidebar === "function") {
closeSidebar();
return;
}
const sidebar = document.getElementById("sidebar");
if (sidebar) {
sidebar.classList.remove("sidebar-open");
}
document.body.classList.remove("sidebar-open");
}
});
});