Make sidebar collapsible with hamburger toggle

Sidebar is now hidden by default, opened via a small hamburger
button in the top-left corner. Clicking a nav link or the overlay
closes it. Keeps focus on the search bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
mariosemes
2026-03-26 21:18:23 +01:00
parent 98e326266f
commit 97fb8d9663

View File

@@ -3,19 +3,56 @@
import { page } from '$app/stores';
let { children } = $props();
let sidebarOpen = $state(false);
function isActive(path) {
const current = $page.url.pathname;
if (path === '/') return current === '/';
return current.startsWith(path);
}
function closeSidebar() {
sidebarOpen = false;
}
</script>
<div class="flex h-screen overflow-hidden">
<div class="relative h-screen overflow-hidden">
<!-- Hamburger toggle -->
<button
onclick={() => sidebarOpen = !sidebarOpen}
class="fixed top-3 left-3 z-50 w-8 h-8 flex items-center justify-center rounded
bg-surface-raised/80 backdrop-blur border border-surface-border
hover:bg-surface-hover hover:border-surface-border-hover
text-text-secondary hover:text-text-primary transition-all duration-150"
aria-label="Toggle menu"
>
{#if sidebarOpen}
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
{:else}
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5M3.75 17.25h16.5" />
</svg>
{/if}
</button>
<!-- Overlay -->
{#if sidebarOpen}
<button
class="fixed inset-0 z-30 bg-black/50 backdrop-blur-sm"
onclick={closeSidebar}
aria-label="Close menu"
></button>
{/if}
<!-- Sidebar -->
<aside class="w-56 flex-shrink-0 bg-surface border-r border-surface-border flex flex-col">
<aside class="fixed top-0 left-0 z-40 h-full w-56 bg-surface border-r border-surface-border flex flex-col
transform transition-transform duration-200 ease-out
{sidebarOpen ? 'translate-x-0' : '-translate-x-full'}">
<!-- Logo -->
<div class="px-4 py-4 border-b border-surface-border">
<a href="/" class="flex items-center gap-2.5 group">
<a href="/" onclick={closeSidebar} class="flex items-center gap-2.5 group">
<div class="w-6 h-6 rounded bg-accent flex items-center justify-center">
<svg class="w-3.5 h-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
@@ -28,14 +65,14 @@
<!-- Nav -->
<nav class="flex-1 px-2 py-3 space-y-0.5 overflow-y-auto">
<span class="block px-2 py-1.5 text-2xs font-medium text-text-tertiary uppercase tracking-wider">Search</span>
<a href="/" class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
<a href="/" onclick={closeSidebar} class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
{isActive('/') && !isActive('/results') && !isActive('/admin') ? 'bg-surface-hover text-text-primary' : 'text-text-secondary hover:text-text-primary hover:bg-surface-hover'}">
<svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
</svg>
Search
</a>
<a href="/results?q=" class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
<a href="/results?q=" onclick={closeSidebar} class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
{isActive('/results') ? 'bg-surface-hover text-text-primary' : 'text-text-secondary hover:text-text-primary hover:bg-surface-hover'}">
<svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
@@ -44,14 +81,14 @@
</a>
<span class="block px-2 pt-4 pb-1.5 text-2xs font-medium text-text-tertiary uppercase tracking-wider">Manage</span>
<a href="/admin" class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
<a href="/admin" onclick={closeSidebar} class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
{isActive('/admin') && !isActive('/admin/categories') ? 'bg-surface-hover text-text-primary' : 'text-text-secondary hover:text-text-primary hover:bg-surface-hover'}">
<svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 21v-7.5a.75.75 0 01.75-.75h3a.75.75 0 01.75.75V21m-4.5 0H2.36m11.14 0H18m0 0h3.64m-1.39 0V9.349m-16.5 11.65V9.35m0 0a3.001 3.001 0 003.75-.615A2.993 2.993 0 009.75 9.75c.896 0 1.7-.393 2.25-1.016a2.993 2.993 0 002.25 1.016c.896 0 1.7-.393 2.25-1.016A3.001 3.001 0 0021 9.349m-18 0a2.997 2.997 0 00.177-.728C3.364 7.364 4 6 4.5 4.5h15c.5 1.5 1.136 2.864 1.323 3.621a2.997 2.997 0 00.177.728" />
</svg>
Stores
</a>
<a href="/admin/categories" class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
<a href="/admin/categories" onclick={closeSidebar} class="flex items-center gap-2.5 px-2 py-1.5 rounded text-sm transition-colors duration-100
{isActive('/admin/categories') ? 'bg-surface-hover text-text-primary' : 'text-text-secondary hover:text-text-primary hover:bg-surface-hover'}">
<svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z" />
@@ -62,8 +99,8 @@
</nav>
</aside>
<!-- Main content -->
<main class="flex-1 overflow-y-auto bg-surface">
<!-- Main content (full width) -->
<main class="h-full overflow-y-auto bg-surface">
{@render children()}
</main>
</div>