[PATCH v1 1/2] add mobile drawer navigation with a compact sticky top bar
Rito Rhymes <[email protected]> Mon, 3 Aug 2026 11:35:01 -0400
| Newsgroups | org.kernel.linux.tools |
|---|---|
| Message-ID | <[email protected]> |
On small screens the navigation wraps into a dense block of links that is hard to scan and gives closely spaced tap targets, the banner consumes significant vertical space, and because content reflows into a single narrow column, returning to the navigation at the top of the page is a much longer scroll than on desktop. At the mobile breakpoint, hide the desktop banner/navigation and replace it with a compact sticky top bar carrying the same site title and logo. Add a hamburger control that opens a drawer menu with overlay, so navigation is reachable at any scroll position without returning to the top. It is driven by a checkbox/label toggle, so no JavaScript is required. All rules are scoped to the 848px media query; the desktop layout is unchanged. The checkbox is the only keyboard-operable control, so it is hidden with clip-path rather than display:none to keep it focusable, and Space toggles the drawer. The labels are pointer affordances only, marked aria-hidden and given no tabindex: a label does not respond to Enter or Space, so making it focusable would present a control that cannot be operated. Focus rings are drawn on whichever affordance is visible, moving to the close icon once the open drawer paints over the hamburger. The closed drawer is set visibility:hidden so its off-screen links stay out of the tab order. Signed-off-by: Rito Rhymes <[email protected]> Assisted-by: LLM [codegen, review] --- korgi/static/css/main.css | 164 ++++++++++++++++++++++++++++++++++++++ korgi/templates/base.html | 30 +++++++ 2 files changed, 194 insertions(+) diff --git a/korgi/static/css/main.css b/korgi/static/css/main.css index 95b6326..60538d9 100644 --- a/korgi/static/css/main.css +++ b/korgi/static/css/main.css @@ -339,7 +339,171 @@ dt { padding-bottom: 22px; } +/* Mobile nav: hidden on desktop. The toggle, overlay and drawer all + live inside #mobile-banner, so hiding it hides the whole control. */ +#mobile-banner { display: none; } + @media screen and (max-width: 848px) { + #banner { + display: none; + } + /* The checkbox is the actual control, so it stays reachable by + keyboard; only its rendering is suppressed. The labels are + pointer affordances and are hidden from assistive tech. */ + .mobile-nav-checkbox { + display: block; + position: absolute; + width: 1px; + height: 1px; + margin: 0; + padding: 0; + border: 0; + overflow: hidden; + clip-path: inset(50%); + } + .mobile-nav-checkbox:focus-visible ~ .hamburger-btn, + .mobile-nav-checkbox:focus-visible:checked ~ #mobile-drawer .close-btn svg { + outline: 2px solid #3357D6; + outline-offset: 2px; + border-radius: 0.2em; + } + /* While open the drawer paints over the hamburger, so the ring + would be hidden; show it on the close control instead. */ + .mobile-nav-checkbox:focus-visible:checked ~ .hamburger-btn { + outline: none; + } + #mobile-banner { + display: flex; + align-items: center; + position: sticky; + top: 0; + z-index: 1000; + background: #FFFFFF; + padding: 0.5em 1em; + border-bottom: thin solid #CCC8B8; + box-shadow: 0 0.1em 0.3em #CCC8B8; + } + #mobile-tux-gear { + width: 2.5em; + height: 3em; + background-image: url('../images/logos/tux.png'); + background-position: center; + background-repeat: no-repeat; + background-size: contain; + flex-shrink: 0; + } + #mobile-banner .mobile-site-title { + flex: 1; + margin: 0 0.5em; + font-family: oswald,helvetica,sans-serif; + font-weight: bold; + font-size: 1.4em; + line-height: 1.1; + text-shadow: 2px 2px 0 #F8F4EE; + } + #mobile-banner .mobile-site-title > a { + color: #2C1D00; + transition: none; + } + .hamburger-btn { + cursor: pointer; + flex-shrink: 0; + padding: 0.25em; + } + .hamburger-btn svg { + display: block; + } + #mobile-drawer { + display: block; + position: fixed; + top: 0; + right: 0; + width: 200px; + max-width: 75vw; + height: 100%; + background: #FFFFFF; + z-index: 1002; + transform: translateX(100%); + /* visibility keeps the off-screen links out of the tab order + while closed; it is delayed so the slide-out still animates. */ + visibility: hidden; + transition: transform 0.3s ease, visibility 0s linear 0.3s; + overflow-y: auto; + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.2); + } + #mobile-drawer ul { + list-style: none; + padding: 1em; + margin: 0; + } + #mobile-drawer li { + padding: 0.75em 0; + border-bottom: thin solid #F0EDE3; + } + #mobile-drawer li:last-child { + border-bottom: none; + } + #mobile-drawer li > a { + font-family: oswald, helvetica, sans-serif; + font-size: 1.1em; + color: #B39223; + } + #mobile-drawer li > a:hover, + #mobile-drawer li > a:active { + color: #4C3D00; + } + #drawer-tux { + width: 4em; + height: 5em; + margin: 1.5em auto 1em; + background-image: url('../images/logos/tux.png'); + background-position: center; + background-repeat: no-repeat; + background-size: contain; + } + .close-btn { + display: block; + cursor: pointer; + text-align: right; + padding: 1em; + } + .close-btn svg { + display: inline-block; + } + .nav-overlay { + display: block; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 1001; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0s linear 0.3s; + } + #mobile-nav-toggle:checked ~ #mobile-drawer { + transform: translateX(0); + visibility: visible; + transition: transform 0.3s ease, visibility 0s; + } + #mobile-nav-toggle:checked ~ .nav-overlay { + opacity: 1; + visibility: visible; + transition: opacity 0.3s ease, visibility 0s; + } + /* Drop the slide and fade, but keep the visibility swap so the + closed drawer stays out of the tab order. */ + @media (prefers-reduced-motion: reduce) { + #mobile-drawer, + #mobile-nav-toggle:checked ~ #mobile-drawer, + .nav-overlay, + #mobile-nav-toggle:checked ~ .nav-overlay { + transition: none; + } + } + #banner { width: auto; margin: 0; diff --git a/korgi/templates/base.html b/korgi/templates/base.html index ce1d5fb..76448bc 100644 --- a/korgi/templates/base.html +++ b/korgi/templates/base.html @@ -46,6 +46,36 @@ {% endfor %} </ul></nav> </header><!-- /#banner --> + <header id="mobile-banner" class="body"> + <div id="mobile-tux-gear"></div> + <h1 class="mobile-site-title"><a href="{{ SITEURL }}/">{{ SITENAME }}</a></h1> + <input type="checkbox" id="mobile-nav-toggle" class="mobile-nav-checkbox" aria-label="Navigation menu" /> + <label for="mobile-nav-toggle" class="hamburger-btn" aria-hidden="true"> + <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#2C1D00" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg> + </label> + + <label for="mobile-nav-toggle" class="nav-overlay" aria-hidden="true"></label> + + <nav id="mobile-drawer" role="navigation" aria-label="Mobile navigation"> + <label for="mobile-nav-toggle" class="close-btn" aria-hidden="true"> + <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#2C1D00" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> + </label> + <ul> + {% for title, link in MENUITEMS %} + <li><a href="{{ link }}">{{ title }}</a></li> + {% endfor %} + {% if DISPLAY_PAGES_ON_MENU %} + {% for page in PAGES %} + <li><a href="{{ SITEURL }}/{{ page.url }}">{{ page.title }}</a></li> + {% endfor %} + {% endif %} + {% for cat, null in categories %} + <li><a href="{{ SITEURL }}/{{ cat.url }}">{{ cat }}</a></li> + {% endfor %} + </ul> + <a href="{{ SITEURL }}/"><div id="drawer-tux"></div></a> + </nav><!-- /#mobile-drawer --> + </header><!-- /#mobile-banner --> {% block content %} {% endblock %} <section id="extras" class="body"> -- 2.51.0