Linux maintainer tooling and workflows
 help / color / mirror / Atom feed
From: Rito Rhymes <rito@ritovision.com>
To: tools@kernel.org
Cc: Konstantin Ryabitsev <mricon@kernel.org>,
	Rito Rhymes <rito@ritovision.com>
Subject: [PATCH v1 1/2] add mobile drawer navigation with a compact sticky top bar
Date: Mon,  3 Aug 2026 11:35:01 -0400	[thread overview]
Message-ID: <20260803153502.32096-1-rito@ritovision.com> (raw)

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 <rito@ritovision.com>
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


             reply	other threads:[~2026-08-03 15:35 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-03 15:35 Rito Rhymes [this message]
2026-08-03 15:35 ` [PATCH v1 2/2] drop mobile banner rules made unreachable by the drawer Rito Rhymes
2026-08-03 17:08 ` [PATCH v1 1/2] add mobile drawer navigation with a compact sticky top bar Konstantin Ryabitsev
2026-08-03 17:15   ` Rito Rhymes
2026-08-04 19:25     ` Konstantin Ryabitsev
2026-08-04 20:04       ` Rito Rhymes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260803153502.32096-1-rito@ritovision.com \
    --to=rito@ritovision.com \
    --cc=mricon@kernel.org \
    --cc=tools@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox