Canvas Design System
Main Site Tokens

App Shell Layouts

The layout system behind /app/. Four page archetypes and four content grids, measured at 1440.

info
This is not Page Templates. That page covers the main site, where a page is 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.

RegionClassSizeNotes
Global header.wsh-header1440 × 60ws_header($section), 17 section keys
Rail.ws-sidenav100 wideInk #2C2416; the shell inverts the component's light surface
Main region.app-shell__main1340 wideScrolls on its own; drafting paper, 24px grid
Document bar.ws-app-bar1340 × 60--app-bar-height; four states. Renders only for plan, facilitate, synthesize and prove: home, settings, stuff and library have none.
Footer.ws-app-footer1440 × 48Absent 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.

ArchetypeDecided byContent slotRoutes
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
lightbulb
A dashboard is not prose, and neither is a form. The six-column main (928) is the measure that keeps both off a 1400px line, and a document inside it takes five columns (770, .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.

ClassColumnsGapStatus
.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>
Do open .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.
Don't put one card per column in .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.
info
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.

WhatWas drawn asActually 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
check_circle
The Figma templates and this page now carry the same numbers as the app. Confirmed in the browser: header 60, rail 100, main 1340, bar 60, wide column 1244, reading measure 1131, footer 48.

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”.

ComponentContent slot
App / Template / 1 Full bleedx 0, w 1340
App / Template / 2 Wide columnx 48, w 1244
App / Template / 3 Reading measurex 48, w 1131
App / Template / 4 Handoff sheetx 48, w 1131, y 24, no bar
App / Template / 5 Four columns296 ×4
App / Template / 6 Small left, large right296 + 928 — rail two columns, then main six; .app-page--rail-left
App / Template / 7 Large left, small right928 + 296 — main six columns, then rail two; .app-page
App / Template / 7 Large left, small right / no barthe 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 columns138 ×8 at 20 — the grid 6 and 7 are spans of; all three slots carry it as a layout grid
info
Every frame is 1440 × 1024 with the chrome instanced, so the only thing that varies between templates is the content slot. The rail is cloned from a live capture and its active row reads Strategize on all eight; set it per screen.