MobileApp shell & layout

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. Use dvh (dynamic viewport), not vh, 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-contain kills the bounce.
  • Web is untouched: web keeps the original min-h-screen document-scroll layout with lg:pl-60 for the desktop sidebar. Branch on isNative.
<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.