PR 11010 · Route loading shells, part 1

Each pair is the same click-in on a local dev server (stage data, smoke user, 1440×900): left, the route shell captured ~1.2 s after router.push while the page’s server render was held 6 s by a temporary, uncommitted delay wrapper; right, the loaded page. The time is when [role=status][aria-label=Loading] appeared in <main>. Dev never prefetches, so dev times include the section layout’s own awaits; production measured 38–87 ms for prefetched shells.

/conversations

shell visible after 126 ms
/conversations route shell
Shell
/conversations loaded
Loaded

Matches: Heading at the same y (102px) and the table card at the same y (134px) in both frames. The shell is the real DataTable in its cold state: same toolbar band height, the log’s six default column headers, 15 ghost rows (the log’s own ghostRows), placeholder footer.

Known differences: Filter pills arrive with the page (their specs are built from live data). Loaded rows are two-line, so the table grows downward when data lands — that is the DataTable’s own cold ghost too, not new. Loaded frame cropped to the chrome: rows carry resident names.

/leasing

shell visible after 1,307 ms (dev; layout awaits the module gate first)
/leasing route shell
Shell
/leasing loaded
Loaded

Matches: Header, subtitle and window-picker ghost line up (picker ghost re-measured after round 1: 128×28, 6px radius — round 1 used a 120×30 pill that jumped when the real control landed). Both pipeline cards are the real LeasingSection / RenewalsSection fed null stats, in the same 2-column grid; nav cards are real.

Known differences: Null-stat sections print ghost-zero values — pre-existing behaviour of these components on the hub’s own cold frame and on dashboard/loading.tsx; not changed here. Cards grow with data (stage rows).

/maintenance

shell visible after 795 ms (dev)
/maintenance route shell
Shell
/maintenance loaded
Loaded

Matches: Maintenance card now renders from the first frame (it used to pop in above Turnovers when stats landed). Turnovers ghosts its count instead of printing a stand-in 0.

Known differences: Maintenance card grows by its “Top categories” block when data lands — data-dependent.

/review

shell visible after 2,331 ms (dev; review layout awaits session + module gate)
/review route shell
Shell
/review loaded
Loaded

Matches: Real PageHeader “Review” at the same y; real DataTable cold frame with the page’s column headers and its 10 default ghost rows.

Known differences: The loaded page here shows a “Pick a company” banner (smoke user spans every company), which pushes the table down — state-dependent, not something a shell can know. Loaded frame cropped to the chrome.

PropFlow Docs