Pin bottom nav to viewport and reserve scroll clearance.

Track visual viewport for keyboard offset, add bottom spacer, use solid
nav background with safe-area padding, and avoid zero nav offset fallback.
This commit is contained in:
2026-05-24 04:34:34 -06:00
parent 68b3da1d13
commit 831170573b
8 changed files with 97 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
import { Suspense } from "react";
import { AppAuthGate } from "@/components/layout/app-auth-gate";
import { AppBottomSpacer } from "@/components/layout/app-bottom-spacer";
import { DailyReminderProvider } from "@/components/reminders/daily-reminder-provider";
import { InAppReminderBanner } from "@/components/reminders/in-app-reminder-banner";
@@ -14,7 +15,10 @@ export default function AppLayout({
<AppAuthGate>
<DailyReminderProvider />
<InAppReminderBanner />
<div className="pb-nav">{children}</div>
<div>
{children}
<AppBottomSpacer />
</div>
</AppAuthGate>
</Suspense>
);

View File

@@ -5,7 +5,8 @@
@layer base {
:root {
--app-topbar-height: 3.5rem;
--app-nav-offset: var(--bottom-nav-height);
--app-viewport-bottom-offset: 0px;
--app-nav-offset: 6rem;
--bottom-nav-height: 6rem;
--background: 252 38% 10%;
--foreground: 52 100% 88%;
@@ -68,6 +69,7 @@
html {
overflow-x: clip;
scroll-padding-bottom: var(--app-nav-offset, var(--bottom-nav-height));
}
body {
@@ -170,7 +172,14 @@
@layer utilities {
.pb-nav {
padding-bottom: var(--app-nav-offset);
padding-bottom: var(--app-nav-offset, var(--bottom-nav-height));
}
.pb-nav-safe {
padding-bottom: calc(
var(--app-nav-offset, var(--bottom-nav-height)) +
var(--app-viewport-bottom-offset, 0px)
);
}
.pt-topbar {

View File

@@ -1,4 +1,4 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { Press_Start_2P } from "next/font/google";
import { ThemeProvider } from "next-themes";
@@ -16,6 +16,13 @@ export const metadata: Metadata = {
description: "Build habits, care for your pixel pet, and track wellness every day.",
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
viewportFit: "cover",
interactiveWidget: "resizes-content",
};
const pixelFont = Press_Start_2P({
weight: "400",
subsets: ["latin"],