App Shell Layouts
The layout system behind /app/. Four page archetypes and four content grids, measured at 1440.
includes/header.php plus a .container. The app shell is a different frame:
a fixed rail, a document bar, a main region that scrolls on its own, and drafting paper behind it.
Nothing on that page applies here.
The frame
Constant on every route. Measured from the live DOM at a 1440 viewport, and matching app/css/app-shell.css.
| Region | Class | Size | Notes |
|---|---|---|---|
| Global header | .wsh-header | 1440 × 60 | ws_header($section), 17 section keys |
| Rail | .ws-sidenav | 100 wide | Ink #2C2416; the shell inverts the component's light surface |
| Main region | .app-shell__main | 1340 wide | Scrolls on its own; drafting paper, 24px grid |
| Document bar | .ws-app-bar | 1340 × 60 | --app-bar-height; four states. Renders only for plan, facilitate, synthesize and prove: home, settings, stuff and library have none. |
| Footer | .ws-app-footer | 1440 × 48 | Absent on full-bleed routes |
Four page archetypes
The shell is config-driven. Every view declares label, icon, header, eyebrow, title and lede, and two flags in app/index.php pick the frame. There are four shapes, not twenty-six.
| Archetype | Decided by | Content slot | Routes |
|---|---|---|---|
| Full bleed | $fullBleed |
x 0, w 1340. No column, no gutters, no footer. | plan/build (iframe), plan/check, facilitate/run, synthesize/make, synthesize/recap |
| Page grid | not full bleed | x 48, w 1244. .app-shell__content (max-width: calc(var(--app-content-wide) + 48px * 2)) holding .app-page: a six-column main (928) beside the two-column rail (296). The reading measure (1131) and $wideContent retired in September 2026. |
every other route: home, plan/home, plan/saved, strategize ×5, facilitate/home, synthesize/home, prove/home, prove/report, settings, stuff ×6, library, sign-in |
| Handoff sheet | the else branch |
The page grid, holding .app-shell__sheet in the main: eyebrow, title, lede, one primary button, with the shared rail beside it (Tips alone for a stranger). No document bar, so content starts 24px below the region top rather than 84px. |
library |
.prv-report) or four (612). That replaced the old choice between a 1131 reading measure and a 1244 wide column.Content grids
What goes inside the wide column. All four are measured against its 1244, where the arithmetic closes exactly. Defined in app/css/app-shell.css.
One page grid, since September 2026: eight columns of --app-grid-col (138px) with
--app-grid-gutter (20px) between them, 8 × 138 + 7 × 20 =
--app-content-wide (1244), the content column at 1440. A rail is two columns
(--app-rail, 296) and the main beside it is six (928). .app-page is the
page-level grid with the rail and main placed; .app-grid-8 is the same tracks as a bare
primitive for layouts inside a column. The old .app-cols split (892 + 320 at a 32px gap)
was deleted once its last view had moved.
| Class | Columns | Gap | Status |
|---|---|---|---|
.app-page |
138 ×8 at 1244 — .app-page__main spans 6 (928), .app-rail spans 2 (296); .app-page--rail-left puts the rail first |
--app-grid-gutter 20px |
shipped |
.app-grid-8 |
138 ×8 at 1244 — the bare primitive; span with .app-span-2/-3/-4/-6/-full |
--app-grid-gutter 20px |
shipped |
.app-grid-6 |
138 ×6 inside a 928 main | --app-grid-gutter 20px |
shipped |
.app-grid-4 |
296 ×4 at 1244 — inside the full width only (217 inside a main is off-grid) | --app-grid-gutter 20px |
shipped |
.app-grid-3 |
296 ×3 inside a 928 main — where .wl-grid lands by itself |
--app-grid-gutter 20px |
shipped |
.app-grid-2 |
454 ×2 inside a main, 612 ×2 at 1244 | --app-grid-gutter 20px |
shipped |
<div class="app-shell__content">
<div class="app-page">
<div class="app-page__main"><!-- six columns, 928 --></div>
<aside class="app-rail"><!-- two columns, 296, sticky --></aside>
</div>
</div>.app-page on every non-bleed view, keep the rail as the
<aside class="app-rail"> that appShellRail() renders, and lay the main
column out with .app-grid-6, -3 or -2, which reproduce the page
columns exactly. Span two or three of .app-grid-8 and put a card in that.
.app-grid-8. At 138px a card is
barely half the 260px floor .wl-grid enforces, and that floor was written after an
uncapped three-column grid drew 214px cards. Don't use .app-grid-4 inside a main
(217 is off-grid) or .app-grid-3 at the full width. Don't reintroduce a local
split or a local cap: the grid owns width.
tests/E2E/app-grid.spec.js reads the used track sizes back from the browser: eight
tracks of 138 at 1440, main 928, rail 296, on every non-bleed route.
Under 1100px the grid halves and the rail drops beneath the main column at full width.
Measured against the running app
Every number on this page was taken from /app/ in a browser at 1440 on 17 September 2026, not from the CSS alone. Three things the design files had wrong were corrected to match.
| What | Was drawn as | Actually is |
|---|---|---|
| Document bar height | 72 (the ws-app-bar Figma component) |
60 (--app-bar-height), which puts the content slot at y 84, not y 96 |
| Handoff sheet | with a document bar, content at y 84 | no bar at all; /app/?view=library renders none, so content starts at y 24 |
| Reading measure | 1244, widened to match the other templates | 1131; the templates were reverted rather than the app widened |
In Figma
Eight components, one per archetype and one per grid, on the templates page of the synthesize file, in the section “App — Layout Templates”.
| Component | Content slot |
|---|---|
| App / Template / 1 Full bleed | x 0, w 1340 |
| App / Template / 2 Wide column | x 48, w 1244 |
| App / Template / 3 Reading measure | x 48, w 1131 |
| App / Template / 4 Handoff sheet | x 48, w 1131, y 24, no bar |
| App / Template / 5 Four columns | 296 ×4 |
| App / Template / 6 Small left, large right | 296 + 928 — rail two columns, then main six; .app-page--rail-left |
| App / Template / 7 Large left, small right | 928 + 296 — main six columns, then rail two; .app-page |
| App / Template / 7 Large left, small right / no bar | the same without the document bar, content from y 24 — home, settings, stuff and library render no bar. The first screen drawn on it is App / home in the section “App — Screens”, a detached copy filled with library instances. |
| App / Template / 8 Eight columns | 138 ×8 at 20 — the grid 6 and 7 are spans of; all three slots carry it as a layout grid |