diff --git a/web/src/lib/components/MobileNavDrawer.svelte b/web/src/lib/components/MobileNavDrawer.svelte
new file mode 100644
index 00000000..f9f2d7c7
--- /dev/null
+++ b/web/src/lib/components/MobileNavDrawer.svelte
@@ -0,0 +1,138 @@
+
+
+
+
+{#if mobileNav.value}
+
+{/if}
+
+
diff --git a/web/src/lib/components/MobileNavDrawer.test.ts b/web/src/lib/components/MobileNavDrawer.test.ts
new file mode 100644
index 00000000..c094d85b
--- /dev/null
+++ b/web/src/lib/components/MobileNavDrawer.test.ts
@@ -0,0 +1,43 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { render, screen } from '@testing-library/svelte';
+import MobileNavDrawer from './MobileNavDrawer.svelte';
+import { mobileNav, openMobileNav, closeMobileNav } from '$lib/stores/mobileNav.svelte';
+
+describe('MobileNavDrawer', () => {
+ beforeEach(() => {
+ closeMobileNav();
+ });
+
+ it('renders inert + aria-hidden when closed', () => {
+ render(MobileNavDrawer);
+ const aside = screen.getByLabelText('Main navigation');
+ expect(aside.getAttribute('aria-hidden')).toBe('true');
+ expect(aside.hasAttribute('inert')).toBe(true);
+ });
+
+ it('flips to visible when mobileNav opens', async () => {
+ render(MobileNavDrawer);
+ openMobileNav();
+ await Promise.resolve();
+ const aside = screen.getByLabelText('Main navigation');
+ expect(aside.getAttribute('aria-hidden')).toBe('false');
+ expect(aside.hasAttribute('inert')).toBe(false);
+ });
+
+ it('renders all nav items + Settings + Log out', () => {
+ openMobileNav();
+ render(MobileNavDrawer);
+ expect(screen.getByText('Home')).toBeInTheDocument();
+ expect(screen.getByText('Artists')).toBeInTheDocument();
+ expect(screen.getByText('Settings')).toBeInTheDocument();
+ expect(screen.getByText('Log out')).toBeInTheDocument();
+ });
+
+ it('Escape key closes the drawer', () => {
+ openMobileNav();
+ render(MobileNavDrawer);
+ expect(mobileNav.value).toBe(true);
+ window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
+ expect(mobileNav.value).toBe(false);
+ });
+});