The problem. Today's nested screens (members/index, members/invite) inherit the default Expo Router Stack header: white panel, hairline rule, iOS system font, centered title, blue back link, and — visible in the current build — the literal route segment "index" rendered as the back-button label. Sign-in, OTP, and home all use the Terracotta editorial system; the nested headers look like a different app.
What's below. Four header treatments, each rendered with realistic Members-screen content so the headers are compared in context. The first frame is the current default for reference. Then an iOS ↔ Android comparison of the recommended variant. Tokens (color, type, spacing, radii) match constants/Colors.ts and constants/tokens.ts exactly.
What the user's screenshot shows. iOS native chrome sitting on top of a Terracotta screen. Included for reference only.
No header bar. A floating back chip sits in the safe area; the title renders as a display-size heading at the top of the content. Same language as sign-in / OTP.
A ~56px bar persists at the top: back chip + title (15/20, weight 700) left-aligned + optional right slot. No body title. Title stays visible while scrolling.
Hybrid of A and B. At rest looks like A; once scrolled, the display title smoothly morphs into the bar and a hairline appears. Frame below auto-loops every 6 seconds.
Variant A plus an uppercase accent eyebrow above the display title. Carries breadcrumb-style context (org, parent section) without adding a subtitle slot to chrome.
The ScreenHeader is a custom RN component, not a platform-default header. Same back chip, same display title, same tokens on both platforms. The only differences come from system chrome: iOS has the Dynamic Island and home indicator; Android has a smaller status bar and a gesture pill at the bottom. Both feed into useSafeAreaInsets(), which the component reads to position the back chip correctly.
Dynamic Island sits in the 44px status zone. Back chip starts at insets.top + s2.
28px status bar, no notch. Gesture pill at the bottom. Body content otherwise identical — same tokens, same component.
Why A is the base. Sign-in and OTP already establish the visual language: cream page, display-size title at the top of the body, no header bar. A keeps that contract intact when the user enters a nested screen — no visual jolt, no second chrome system. The back chip uses the same 36px pill shape as the bell button in TopBar, so the chrome vocabulary stays coherent. Implementation is plain layout — no Animated, no scroll handlers, no platform branching.
D is A with two extra props. The eyebrow + meta line are additive on top of A's structure. Screens that need breadcrumb-style context opt in by passing eyebrow= and meta=; screens that don't render as plain A.
C is a future upgrade path. The Large-Title collapse needs a Reanimated scroll handler and a coordinated morph. Build A first, ship the Tabs migration on top of it, add C as a collapsible prop once Catalog/Orders make scroll-heaviness real.
B is rejected for nested screens. Cream-on-cream slim bar with a generic label feels closer to a settings sub-screen than to the editorial direction. Reserve B-style chrome for modal flows (e.g., invite flow in a sheet) where the title anchors a focused task.
surface bg · 1px borderStrong border · ink chevron drawn as two 2px borders (no glyph font). Same shape as TopBar's bell button.+, ⋯, filter, etc. Hidden when no rightSlot prop is passed.display token (28/32, weight 800, tracking −0.5). Ink color. Lives in body, not chrome.eyebrow token (11/14, uppercase, tracking 0.8). Accent color. Above the title.caption token (12/16, weight 500). text2 color. Below the title.space.s2 top, space.s5 sides. Title block: space.s6 top, space.s5 sides.insets.top + space.s2; reads from useSafeAreaInsets() so the same component handles iOS notch and Android status bar.{ title; eyebrow?; meta?; rightSlot?; onBack? } — onBack defaults to router.back(); null hides the back chip (root-of-stack screens).Open in any browser. Give variant C ~6 seconds to see the morph cycle. All tokens match constants/Colors.ts and constants/tokens.ts — what you see is what will ship if a variant is picked.
Reply with the variant name (A / B / C / D), or describe a mix.