diff --git a/__tests__/components/mobile/mobile-tab-bar.test.tsx b/__tests__/components/mobile/mobile-tab-bar.test.tsx
new file mode 100644
index 0000000..66d6170
--- /dev/null
+++ b/__tests__/components/mobile/mobile-tab-bar.test.tsx
@@ -0,0 +1,57 @@
+// @vitest-environment jsdom
+import { describe, it, expect, vi } from 'vitest'
+import { render, screen } from '@testing-library/react'
+import { MobileTabBar } from '@/components/mobile/mobile-tab-bar'
+
+let pathname = '/m/products/p1'
+vi.mock('next/navigation', () => ({
+ usePathname: () => pathname,
+}))
+
+function setPathname(p: string) { pathname = p }
+
+describe('MobileTabBar', () => {
+ it('toont 3 tabs als activeProductId aanwezig is', () => {
+ setPathname('/m/products/p1')
+ render()
+ expect(screen.getByLabelText('Backlog')).toBeTruthy()
+ expect(screen.getByLabelText('Solo')).toBeTruthy()
+ expect(screen.getByLabelText('Settings')).toBeTruthy()
+ })
+
+ it('toont alleen Settings als activeProductId null is', () => {
+ setPathname('/m/settings')
+ render()
+ expect(screen.queryByLabelText('Backlog')).toBeNull()
+ expect(screen.queryByLabelText('Solo')).toBeNull()
+ expect(screen.getByLabelText('Settings')).toBeTruthy()
+ })
+
+ it('Backlog-tab is aria-current op /m/products/[id]', () => {
+ setPathname('/m/products/p1')
+ render()
+ expect(screen.getByLabelText('Backlog').getAttribute('aria-current')).toBe('page')
+ expect(screen.getByLabelText('Solo').getAttribute('aria-current')).toBeNull()
+ })
+
+ it('Solo-tab is aria-current op /m/products/[id]/solo', () => {
+ setPathname('/m/products/p1/solo')
+ render()
+ expect(screen.getByLabelText('Solo').getAttribute('aria-current')).toBe('page')
+ expect(screen.getByLabelText('Backlog').getAttribute('aria-current')).toBeNull()
+ })
+
+ it('Settings-tab is aria-current op /m/settings', () => {
+ setPathname('/m/settings')
+ render()
+ expect(screen.getByLabelText('Settings').getAttribute('aria-current')).toBe('page')
+ })
+
+ it('tap-targets >=44x44 (h-14 = 56px breedtevulling per tab)', () => {
+ setPathname('/m/products/p1')
+ render()
+ const tab = screen.getByLabelText('Backlog')
+ expect(tab.className).toContain('h-14')
+ expect(tab.className).toContain('flex-1')
+ })
+})
diff --git a/components/mobile/mobile-tab-bar.tsx b/components/mobile/mobile-tab-bar.tsx
new file mode 100644
index 0000000..5845f81
--- /dev/null
+++ b/components/mobile/mobile-tab-bar.tsx
@@ -0,0 +1,68 @@
+'use client'
+
+import Link from 'next/link'
+import { usePathname } from 'next/navigation'
+import { ListTree, Activity, Settings } from 'lucide-react'
+import { cn } from '@/lib/utils'
+
+interface MobileTabBarProps {
+ activeProductId: string | null
+}
+
+export function MobileTabBar({ activeProductId }: MobileTabBarProps) {
+ const pathname = usePathname()
+
+ const tabs: Array<{ href: string; icon: typeof ListTree; label: string; match: (p: string) => boolean }> = []
+
+ if (activeProductId) {
+ tabs.push(
+ {
+ href: `/m/products/${activeProductId}`,
+ icon: ListTree,
+ label: 'Backlog',
+ match: (p) => p === `/m/products/${activeProductId}`,
+ },
+ {
+ href: `/m/products/${activeProductId}/solo`,
+ icon: Activity,
+ label: 'Solo',
+ match: (p) => p.startsWith(`/m/products/${activeProductId}/solo`),
+ },
+ )
+ }
+
+ tabs.push({
+ href: '/m/settings',
+ icon: Settings,
+ label: 'Settings',
+ match: (p) => p.startsWith('/m/settings'),
+ })
+
+ return (
+
+ )
+}