App shell & layout
The native shell locks the viewport so the document never scrolls, only the content area does. This is what removes the “webpage in a frame” feel: the top bar and bottom nav stay put while the middle scrolls, and the WKWebView rubber-band bounce is suppressed.
Anatomy
┌─────────────────────────────┐
│ safe-area-inset-top │
│ ┌─────────────────────────┐ │ Top bar (sticky)
│ │ [logo] [avatar]│ │ · Velon logo left (larger on native)
│ └─────────────────────────┘ │ · initials avatar right → /app/profile
│ │
│ <main> scrolls │ flex-1, overflow-y-auto, overscroll-contain
│ │
│ ┌─────────────────────────┐ │ Bottom nav (fixed)
│ │ Home History Conta │ │ · reserves 60px + safe-area-inset-bottom
│ └─────────────────────────┘ │
│ safe-area-inset-bottom │
└─────────────────────────────┘Shell rules
- Native shell:
h-[100dvh] w-full flex flex-col overflow-hidden. Usedvh(dynamic viewport), notvh, so the browser chrome / keyboard resize is honored. <main>is the only scroll container:flex-1 min-h-0 overflow-y-auto overscroll-contain.overscroll-containkills the bounce.- Web is untouched: web keeps the original
min-h-screendocument-scroll layout withlg:pl-60for the desktop sidebar. Branch onisNative.
<div className={isNative
? 'relative flex h-[100dvh] w-full flex-col overflow-hidden bg-gray-50 dark:bg-gray-950'
: 'relative w-full min-h-screen bg-gray-50 dark:bg-gray-950'}>
<TopBarAndNav />
<main className={isNative
? 'min-h-0 w-full flex-1 overflow-y-auto overscroll-contain'
: 'lg:pl-60 w-full min-h-screen'}
style={{ paddingBottom: isNative ? 'calc(60px + env(safe-area-inset-bottom, 0px))' : undefined }}>
{/* page */}
</main>
</div>Mac / large-window caveat
The iOS app can run on Apple Silicon Macs and on large windows. The desktop web
layout (hidden lg:flex sidebar, lg: rules) must not activate inside the
native shell regardless of width, otherwise the desktop sidebar and the mobile
bottom nav render at once. Gate the desktop layout on !isNative, not only on the
lg: breakpoint, so native always keeps the app appearance.
Status bar & splash
Configured natively (Capacitor StatusBar / SplashScreen): status bar does not
overlay the WebView (overlaysWebView: false), dark style, background Azul Profundo
velon-deep (#0C1A26). See Keyboard, input & camera for keyboard resize,
which is part of the same shell concern.