fix(web): mobile navigation — add a bottom tab bar + top bar
ci / web (push) Failing after 49s
ci / rust (push) Successful in 55s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 13s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
apple / swift (push) Successful in 1m19s
ci / docs-site (push) Failing after 37s
docker / deploy-docs (push) Successful in 16s
ci / web (push) Failing after 49s
ci / rust (push) Successful in 55s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 13s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
apple / swift (push) Successful in 1m19s
ci / docs-site (push) Failing after 37s
docker / deploy-docs (push) Successful in 16s
The app shell's only navigation was the desktop sidebar (`hidden … sm:flex`), so on phones (< sm) it was hidden with no replacement — you couldn't navigate at all. Add a responsive mobile layout shown only below `sm`: a top bar (brand + language switcher) and a fixed bottom tab bar with the five nav items (icon + label). The desktop sidebar is unchanged. Page content gets bottom padding so the fixed bar doesn't cover it, and the bar respects the iOS `safe-area-inset-bottom`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ export function AppShell({ children }: { children: ReactNode }) {
|
|||||||
useLocale()
|
useLocale()
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen">
|
<div className="flex min-h-screen">
|
||||||
|
{/* Desktop sidebar (≥ sm). */}
|
||||||
<aside className="hidden w-60 shrink-0 flex-col border-r bg-card/40 p-4 sm:flex">
|
<aside className="hidden w-60 shrink-0 flex-col border-r bg-card/40 p-4 sm:flex">
|
||||||
<div className="mb-6 flex items-center gap-2 px-2">
|
<div className="mb-6 flex items-center gap-2 px-2">
|
||||||
<Radio className="size-5 text-[var(--success)]" />
|
<Radio className="size-5 text-[var(--success)]" />
|
||||||
@@ -44,9 +45,41 @@ export function AppShell({ children }: { children: ReactNode }) {
|
|||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<main className="flex-1 overflow-x-hidden">
|
|
||||||
<div className="mx-auto max-w-5xl p-6 sm:p-10">{children}</div>
|
<div className="flex flex-1 flex-col overflow-x-hidden">
|
||||||
</main>
|
{/* Mobile top bar (< sm): brand + language. The sidebar is hidden here. */}
|
||||||
|
<header className="flex items-center gap-2 border-b bg-card/40 px-4 py-3 sm:hidden">
|
||||||
|
<Radio className="size-5 text-[var(--success)]" />
|
||||||
|
<div className="font-semibold leading-tight">{m.app_name()}</div>
|
||||||
|
<div className="ml-auto">
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="flex-1">
|
||||||
|
{/* pb-24 leaves room for the fixed bottom nav on mobile. */}
|
||||||
|
<div className="mx-auto max-w-5xl p-6 pb-24 sm:p-10 sm:pb-10">{children}</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile bottom tab bar (< sm): the primary navigation on phones. */}
|
||||||
|
<nav
|
||||||
|
className="fixed inset-x-0 bottom-0 z-40 flex border-t bg-card/95 backdrop-blur sm:hidden"
|
||||||
|
style={{ paddingBottom: 'env(safe-area-inset-bottom)' }}
|
||||||
|
>
|
||||||
|
{NAV.map(({ to, icon: Icon, label }) => (
|
||||||
|
<Link
|
||||||
|
key={to}
|
||||||
|
to={to}
|
||||||
|
activeOptions={{ exact: to === '/' }}
|
||||||
|
className="flex flex-1 flex-col items-center gap-1 py-2 text-[10px] text-muted-foreground transition-colors"
|
||||||
|
activeProps={{ className: 'text-foreground' }}
|
||||||
|
>
|
||||||
|
<Icon className="size-5" />
|
||||||
|
<span className="leading-none">{label()}</span>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user