NATIVE
NATIVE

Mobile Platform Proposal

Native Shell

One publish. Web portal and native app from the same builder.

The proof of concept

0

Portal code changes required to ship the native-feeling prototype

The existing publish + /api/app/manifest was already enough

Why this shape

The Thesis

  • The builder remains the single source of truth: pages, widgets, products, links, nav order, theme
  • The Expo app is a render target, not a rebuild — no duplicated screens or product data
  • Admins keep one workflow: edit → publish → web AND app update together
  • Mobile web views are already good; the shell adds the native physics around them

How it works

Architecture

  • Builder publish → portal-tenant BFF GET /api/app/manifest
  • Manifest transform mirrors the web shell's nav rules (top-level, position sort, 5 tabs + More, first slug = home)
  • OKLCH theme payload → native tokens (tab bar, headers, status bar, cards)
  • Published pages render in per-tab WKWebViews with shared session cookies

POC · What we built

Manifest-Driven Chrome

  • Live nav: tabs, labels, icons, badge slots, home tab — all from the publish, re-synced on foreground
  • Theme: OKLCH semantic colors converted natively; isDark flips status bar + blur tints; contrast-safe active-tab tint
  • Brand name, published version surfaced in-app ("Live publish v1852")
  • Publish in the builder → app updates on next launch, no release
Live Nav
Native Theme
Live Version
No Release

POC · What we built

The Invisible WebView

  • Injected CSS hides web header/footer/nav — the page renders only its body, padded for native tab bar + safe areas
  • 3-layer link interception (click capture, history hooks, native shouldStartLoad) — the canvas never navigates itself
  • Internal links open as native bottom sheets with detents; external links leave the app; tab slugs switch tabs
  • One WebView per tab, kept alive — scroll position and state survive tab switches
Chrome Hidden
Links Rerouted
Native Sheets
Kept Alive

POC · What we built

Native Physics

  • Haptics on taps, sheet presents, and events; long-press → native share/copy action sheet
  • Hero-image "flight" from tapped card into the opening sheet (prefetched at touchstart)
  • Inline story/video playback (playsinline injection) instead of the system fullscreen player hijack
  • Fullscreen story overlays detected → tab bar hides, swipe-down-to-dismiss added
Haptics
Hero Flight
Inline Video
Swipe Gestures

The one honest caveat

Heuristics vs. Contract

Today (POC)

CSS selectors hide chrome Add-to-cart guessed from button text Dialogs detected by DOM shape Breaks silently if web shell restyles

Proposed (native_shell=1)

Portal SDK skips chrome by contract Widgets emit real postMessage events (cart:added, story:opened) Safe-area CSS vars passed in Supported, testable, permanent

A small fluid-mono PR deletes the entire fragile layer

From POC to product

The 10x Roadmap

1 native_shell mode Portal SDK PR: chrome off, widget events, safe-area vars
2 Native login Auth against existing endpoints, cookies into WebViews (App Store blocker)
3 Push + deep links expo-notifications; taps deep-link to slugs the shell already routes
4 Builder hints Per-link "sheet / page / external", native icon picker, app preview toggle
5 Real BFF data Messaging, Notifications, Account wired to portal-tenant endpoints
6 EAS + OTA Shell fixes ship over the air; contract versioned for old builds

Scope discipline

What We Won't Do

  • No native widget rebuilds — the builder stays the only authoring surface
  • No second mobile CMS — every admin knob lives in the existing builder
  • No more DOM heuristics — every hour there goes to the render-mode contract instead
  • No forked content pipeline — one publish, one manifest, every platform

What we need

Asks

Review the native_shell render-mode proposal Portal SDK owners
Auth endpoint review for native login flow Platform / Auth
Dev-client build + EAS project setup Mobile
30-min demo of the POC on simulator Everyone

Questions?

Demo available on the simulator — portal-native-test