m4: mobile nav — hamburger + slide-in drawer
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 5s
CI & Build / Python tests (push) Successful in 10s
CI & Build / Build & push image (push) Successful in 29s

Below sm the sidebar had no replacement (nav was inaccessible on phones).
The sidebar aside is now a responsive drawer: fixed + off-canvas with a
hamburger toggle and backdrop on mobile, static in-flow on sm+ (same markup,
no duplication). Closes on nav-click, backdrop, Esc, or route change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-21 00:03:15 -04:00
co-authored by Claude Opus 4.8
parent 894dd4ac42
commit ecbdd12ba9
+37 -2
View File
@@ -21,6 +21,7 @@ const ui = useUiStore();
const managing = ref(false); const managing = ref(false);
const showShortcuts = ref(false); const showShortcuts = ref(false);
const paletteOpen = ref(false); const paletteOpen = ref(false);
const drawer = ref(false);
const searchInput = ref<HTMLInputElement | null>(null); const searchInput = ref<HTMLInputElement | null>(null);
const searchText = ref(typeof route.query.q === "string" ? route.query.q : ""); const searchText = ref(typeof route.query.q === "string" ? route.query.q : "");
let searchTimer: ReturnType<typeof setTimeout> | undefined; let searchTimer: ReturnType<typeof setTimeout> | undefined;
@@ -79,6 +80,10 @@ function onKeydown(e: KeyboardEvent) {
showShortcuts.value = false; showShortcuts.value = false;
return; return;
} }
if (e.key === "Escape" && drawer.value) {
drawer.value = false;
return;
}
if (e.metaKey || e.ctrlKey || e.altKey) return; if (e.metaKey || e.ctrlKey || e.altKey) return;
if (gPending) { if (gPending) {
gPending = false; gPending = false;
@@ -148,6 +153,7 @@ watch(
() => route.name, () => route.name,
(name) => { (name) => {
if (name !== "search") searchText.value = ""; if (name !== "search") searchText.value = "";
drawer.value = false;
}, },
); );
@@ -163,6 +169,26 @@ async function signOut() {
class="sticky top-0 z-20 border-b border-neutral-200 bg-neutral-50/90 backdrop-blur dark:border-neutral-800 dark:bg-neutral-950/90" class="sticky top-0 z-20 border-b border-neutral-200 bg-neutral-50/90 backdrop-blur dark:border-neutral-800 dark:bg-neutral-950/90"
> >
<div class="flex items-center gap-3 px-4 py-3"> <div class="flex items-center gap-3 px-4 py-3">
<button
type="button"
class="icon-btn sm:hidden"
title="Menu"
aria-label="Open menu"
@click="drawer = true"
>
<svg
class="h-5 w-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="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>
</button>
<div class="flex shrink-0 items-center gap-2"> <div class="flex shrink-0 items-center gap-2">
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-brand text-sm font-black text-neutral-900"> <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-brand text-sm font-black text-neutral-900">
TS TS
@@ -203,8 +229,17 @@ async function signOut() {
</header> </header>
<div class="flex flex-1"> <div class="flex flex-1">
<aside class="hidden w-56 shrink-0 border-r border-neutral-200 p-3 sm:block dark:border-neutral-800"> <div
<nav class="flex flex-col gap-0.5 text-sm"> v-if="drawer"
class="fixed inset-0 z-30 bg-black/40 sm:hidden"
aria-hidden="true"
@click="drawer = false"
></div>
<aside
class="fixed inset-y-0 left-0 z-40 w-64 -translate-x-full overflow-y-auto border-r border-neutral-200 bg-neutral-50 p-3 transition-transform duration-200 sm:static sm:z-auto sm:w-56 sm:translate-x-0 dark:border-neutral-800 dark:bg-neutral-950"
:class="drawer ? 'translate-x-0' : ''"
>
<nav class="flex flex-col gap-0.5 text-sm" @click="drawer = false">
<RouterLink to="/" class="nav-link" :class="route.name === 'board' ? 'nav-link-active' : ''"> <RouterLink to="/" class="nav-link" :class="route.name === 'board' ? 'nav-link-active' : ''">
<Icon name="note" /> Notes <Icon name="note" /> Notes
</RouterLink> </RouterLink>