Layout
PURPOSE
Metagame shell that wraps every React screen. Provides the fullscreen flex column, the fixed FX overlay stack used by the reward pipeline, and route-aware overflow handling. Every screen renders fullscreen — no legacy global PLAY / SHOP / COLLECTION bar. Screens that want chrome render it themselves (e.g. V32Shell for Hub/Shop, per-screen bottom bars for Ships).
OWNS
- The outer
divshell —height: 100%,flex column,overflow: hidden. - The
<main>element that hostschildren—flex: 1, with route-conditional overflow. - The
FxLayerscomponent — five stacked fixed-position overlays for the reward pipeline:#fx-dim(zIndex 9998) — dimmer / vignette.#fx-flash(zIndex 9999) — full-screen tinted flash.#fx-particles(zIndex 10000) — canvas for burst / inflow / sparkle particles.#fx-floating(zIndex 10001) — cloned cards / icons in flight.#fx-chips(zIndex 10002) — milestone chips and small labels.
- Mounting of
DevKeys,RewardOrchestrator, andRewardPopuponce at shell level.
READS FROM
react-router-domuseLocation()— readslocation.pathnameto decide overflow mode.ReactNodechildrenprop — the routed screen content rendered inside<main>.
PUSHES TO
- The DOM. The five FX layer elements are created here so the
RewardOrchestratorand downstream effect code can target them byid. - Nothing in stores; the Layout is presentational shell only.
DOES NOT
- Render any navigation chrome, header, footer, or global bar.
- Apply any post-FX CSS filter to
<main>. The cartoony post-FX moved out of a global SVGfilter: url()into a GLSL post-process pass inside the nebula shader (FS1/FS2) because CSS filters don’t reliably apply to WebGL-textured layers. - Manage safe-area insets via CSS env() padding. The shell is
height: 100%only — safe-area handling is delegated to screens / chrome components rather than applied at the Layout level. - Handle pointer events on FX layers. All FX layers are
pointer-events: noneby default; the orchestrator enables pointer events on specific layers when active. - Scroll. The outer container is
overflow: hidden; only<main>scrolls, and only on dev routes.
Signals
- Route prefix
/dev— whenlocation.pathname.startsWith('/dev'),<main>switches tooverflow: autoso dev dashboards can scroll. All other routes keepoverflow: hiddento lock the game / metagame to a fixed fullscreen viewport.
Entry points
Layout({ children })— default export pattern used by the React Router tree to wrap every routed screen.FxLayers()— internal component, not exported. Renders the five overlay layers as a fragment.- DOM IDs
fx-dim,fx-flash,fx-particles,fx-floating,fx-chips— public-by-convention selectors thatRewardOrchestratorand effect helpers query.
Pattern notes
- The fullscreen-no-bar policy is intentional. Earlier shells rendered a global tab bar; the current model lets each screen own its own chrome so transitions can be coordinated per screen.
- FX layer z-indexes are sequential from 9998 upward, leaving headroom below for any modal layer and above for native browser UI.
- The route-conditional overflow is a single boolean (
isDevRoute) rather than a route-table lookup — kept inline because dev routes are the only exception. - Post-FX history is preserved as an inline comment so future contributors don’t re-attempt the SVG
filter: url()approach on<main>. The shader-tail path inengine/rendering/nebula-engine.ts(FS1/FS2) is the canonical home for cartoony post-FX. DevKeys,RewardOrchestrator, andRewardPopupmount once at shell level so they survive route changes and remain available across every screen.