Agent docs

9 · Page composition

Page widths, padding, section rhythm, responsiveness, and the two page archetypes.

Page widths, padding, section rhythm, responsiveness, and the two page archetypes. Evidence: Speedway shell inventory §3-4 (docs/evidence/20260807-0230-speedway-design-language/shell-nav-styles-inventory.md).

Audience: an agent (or person) composing or restructuring a page.

§A Principles and mental models#

1. Two page archetypes: reading page and work surface. A reading page (dashboard sections, job detail, settings) caps its content width and scrolls the whole page. A work surface (the review queue, dense tables) goes full width, drops the activity rail, and bounds itself to the viewport so the table scrolls inside its own card. The rationale is quoted in the CSS: "a work surface, not a reading page: no activity rail, no reading-width cap" (speedway app.css:1300-1305). Deciding which archetype a page is comes before any layout work.

2. The width ladder.

  • Default reading container: panel-inner, max-width 1200px, padding 22px 32px 60px (app.css:347-351).
  • Work surfaces: panel-inner wide, max-width none. Prose inside still caps: .statement { max-width: 68ch } so text never stretches even on a wide panel (app.css:1306-1311).
  • Centered narrow pages (org selection era): 860px centered (app.css:578-583). The model: containers widen, prose never does. 68ch is the one prose measure, reused identically in at least three contexts.

3. Viewport bounding is opt-in and named. A page declares layout: "list" in its shell handle. The shell adds fit-viewport, which gives the grid height: calc(100vh - topbar) and overflow: hidden on main, letting the inner table own the scroll. The why is documented: without a definite height on the chain, "an inner height:100% chain has nothing to resolve against" (app.css:279-289). Never hand-build viewport bounding per page.

4. Composition order is fixed. Inside the main column: breadcrumbs (only when 2+ levels deep) → route outlet → panel-inner[.wide]PageTitle (pinned shrink-0 inside scrolling flex columns) → stat tiles → cards, sections, tables. Pages differ in content, not in ordering.

5. One kicker idiom, five names. The 11px, weight-600, 0.08em-tracked, uppercase, muted label appears as .section-label, .panel-kicker, .rail-label, .field-label, .orch-k. It is the house section-opener. Reuse it rather than inventing a sixth variant. Section rhythm: margin-top: 30px; padding-top: 26px; border-top: 1px solid hairline (app.css:386-398).

6. Breakpoints are few and structural. 1200px: workspace grid narrows, activity panel hides. 1024px (kit lg): sidebar becomes icon rail, identity text hides. 900px: workspace collapses to one column, left rail's border moves from right to bottom. Nothing else is responsive. Content-level responsiveness comes from flex-wrap and minmax(0,1fr) grid tracks, not media queries.

7. Overscroll never flashes. html, body both carry the paper background so macOS rubber-banding shows paper, not white (app.css:88-96). Small, cheap, and part of the house feel.

§B Presets#

PresetRecipe
Reading pageshell handle default → panel-inner → PageTitle → sections with kicker labels, 30/26px rhythm
Work surfacehandle layout:"list" + .wide + no-rail; table inside a card owns scrolling; toolbar pinned above table
Dashboard hybrid.wide reading page: stat tiles row, then two-column card grid, statement copy capped at 68ch
Detail page with backPageTitle back slot (own row); crumbs give the full trail; content in default panel
Sticky topbar chaintopbar 56px sticky z-20; page content scrolls under it; sweep bar z-200 above everything

§C Use cases through Speedway#

PageArchetypeNotesEvidence
Dashboard (workspace.tsx)wide readingpanel-inner wide at :291; PageTitle shrink-0; card follows tile ruling (2026-08-05)workspace.tsx:153-155, 291-296
Review queue (review.tsx)work surfaceno-rail, full-width, viewport-bound; the archetype's namesakeapp.css:1300-1305
Jobs list (jobs.tsx)work surfacelayout:"list", table scrolls in card, PageTitle + NewJobMenu actionsjobs.tsx:187-193
Job detail (job.tsx)readingJSX InlineEdit title; log sectionsjob.tsx:256-278 (the InlineEdit itself at :261-270)
Parts list (parts.tsx)work surfaceback-link title, shrink-0parts.tsx:130-139
Accountsreadingtabbed; crumb appends active tabinventory §2.2-2.3
Admin teamreadingback to All teams; org-name titleteam.tsx:288-297

Portable checklist (for walmart-mvp)#

  1. Classify every page: reading page or work surface. Mixed pages are dashboards, which are wide reading pages.
  2. One default reading width (Speedway: 1200px), one prose cap (68ch), applied by container class, never per-element.
  3. Work surfaces: viewport-bound by a declared layout mode. Tables own their scroll. No page-level scrollbar fighting the table's.
  4. Fixed composition order. PageTitle always present and pinned (shrink-0) on scrolling pages.
  5. One kicker idiom for section labels. One section-rhythm rule.
  6. Three structural breakpoints maximum. Everything else flexes.
@versable-git/ui · reference, canon, and method, read in place