From ebe5ab7516b8ea5d3c9633b4c024e58bf5d57df7 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 10 Jul 2025 16:25:32 -0400 Subject: [PATCH] Improve mobile sidebar UX --- __tests__/responsive.test.js | 13 +++++++++++++ assets/theme.css | 18 +++++++++++++++++- assets/theme.js | 15 +++++++++++++++ templates/layout.njk | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/__tests__/responsive.test.js b/__tests__/responsive.test.js index b64329b..44b2440 100644 --- a/__tests__/responsive.test.js +++ b/__tests__/responsive.test.js @@ -100,6 +100,19 @@ test('sidebar opens on small screens', async () => { expect(sidebarLeft).toBe('0px'); }); +test('clicking outside closes sidebar on small screens', async () => { + const page = await browser.newPage(); + await page.setViewport({ width: 500, height: 800 }); + await page.goto(`http://localhost:${port}/`); + await page.waitForSelector('#sidebar-toggle'); + await page.click('#sidebar-toggle'); + await new Promise(r => setTimeout(r, 300)); + await page.click('main'); + await new Promise(r => setTimeout(r, 300)); + const bodyClass = await page.evaluate(() => document.body.classList.contains('sidebar-open')); + expect(bodyClass).toBe(false); +}); + test('sidebar toggles on large screens', async () => { const page = await browser.newPage(); await page.setViewport({ width: 1024, height: 800 }); diff --git a/assets/theme.css b/assets/theme.css index 722a064..2d6da37 100644 --- a/assets/theme.css +++ b/assets/theme.css @@ -23,7 +23,9 @@ body { align-items: center; padding: 0.5rem 1rem; background: var(--sidebar-bg); - position: relative; + position: sticky; + top: 0; + z-index: 1100; } .search-input { margin-left: auto; @@ -117,6 +119,20 @@ main { text-decoration: none; color: var(--text-color); } + +.sidebar-overlay { + display: none; +} +body.sidebar-open .sidebar-overlay { + display: block; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.3); + z-index: 999; +} @media (max-width: 768px) { .sidebar { position: fixed; diff --git a/assets/theme.js b/assets/theme.js index 2d0ebfc..518779e 100644 --- a/assets/theme.js +++ b/assets/theme.js @@ -3,6 +3,8 @@ document.addEventListener('DOMContentLoaded', () => { const themeToggle = document.getElementById('theme-toggle'); const searchInput = document.getElementById('search-input'); const searchResults = document.getElementById('search-results'); + const sidebar = document.getElementById('sidebar'); + const sidebarOverlay = document.getElementById('sidebar-overlay'); const root = document.documentElement; function setTheme(theme) { @@ -20,6 +22,10 @@ document.addEventListener('DOMContentLoaded', () => { document.body.classList.toggle('sidebar-open'); }); + sidebarOverlay?.addEventListener('click', () => { + document.body.classList.remove('sidebar-open'); + }); + themeToggle?.addEventListener('click', () => { const next = root.dataset.theme === 'dark' ? 'light' : 'dark'; setTheme(next); @@ -76,6 +82,15 @@ document.addEventListener('DOMContentLoaded', () => { if (!searchResults.contains(e.target) && e.target !== searchInput) { searchResults.style.display = 'none'; } + if ( + window.innerWidth <= 768 && + document.body.classList.contains('sidebar-open') && + sidebar && + !sidebar.contains(e.target) && + e.target !== sidebarToggle + ) { + document.body.classList.remove('sidebar-open'); + } }); // breadcrumbs diff --git a/templates/layout.njk b/templates/layout.njk index 3d8a88a..109ef52 100644 --- a/templates/layout.njk +++ b/templates/layout.njk @@ -8,6 +8,7 @@
{% include "partials/header.njk" %} +