Strata approved layouts
The product app’s macro layout doctrine lives in
apps/web-v2/docs/AGENT_APP_UI_CONVENTIONS.md(chat ↔ canvas ↔ sheets — read it for anything touching the chat surface). This doc is the index of approved page-level shapes; if the shape you need isn’t here, propose a new one — don’t improvise a bespoke layout.
The surface model
The product surface is chat + canvas + sheets. Chat is text and buttons only; everything else renders on the canvas as sheets. The chat column is the bounded resizable element (min 280, max 816); the canvas takes the rest.
Sheet types — pick by contract
| Surface | Component | Use for |
|---|---|---|
| Canvas sheet | CanvasSheet | An artifact rendered full on the canvas. Stacks up to 3 deep with progressive recede; base sheet stays mounted. |
| Inspector panel | WindowSheet | Right-anchored resizable inspector/editor (min 400px). |
| Wall | Dialog / AlertDialog | Auth, paywall, destructive confirm — blocking, no artifact behind it. |
| Fullscreen editor | FullscreenModal | Creative builders and editors. |
Stock shadcn Sheet is never used directly.
CanvasSheet — the law
- Overlays are stacked CanvasSheets. Any overlay/detail view in a product
area is a sibling
CanvasSheetstacked above the base sheet — never a modal, never a nested child.stackedDepthon the sheet below controls the recede. - Headers are the standard sub-components, never hand-rolled JSX:
CanvasSheetChrome— sticky header for tabbed/toolbar sheets (NavTabs+ optionalCanvasSheetToolbar).CanvasSheetHeader— eyebrow + title + actions for chrome-less sheets.- Vertical rhythm between chrome, toolbar, and
CanvasSheetBodyis automatic — never add manualpt-*/pb-*/gap-*between them.
- Which sheet is open lives in URL search params or layout state, not in the page component.
- No close behavior destroys underlying state — dismissing a top sheet lands exactly where the user left the sheet below.
WindowSheet — the composition
Right-anchored resizable inspector on Base UI Dialog (drag the left edge;
defaultWidth/minWidth/maxWidth on WindowSheetContent, max defaults to
90% of viewport).
WindowSheetHeaderchildren are the LEFT side (tabs or title); theactionsprop is the RIGHT side (icon-only buttons). Header and footer are both 64px min-height, content centered.WindowSheetBodyis the scroll region — always use it, never a hand-rolledflex-1 overflow-y-auto.WindowSheetSectionprovides the padding;variant="secondary"tints a region. Forms inside are bare<form>+FieldSet.WindowSheetTitleis required by the a11y contract —className="sr-only"when the header shows tabs instead of a visible title.
Area pages (product app)
-
Area navigation is
NavTabsinsideCanvasSheetChrome; filters are toolbar controls bound to URL search params (shareable state), allsize="sm"(32px). Filter selects are text label + chevron only (no icons insideSelectTrigger); view toggles areTabsList size="sm"with text labels when an icon alone would be ambiguous. -
DataTableis the one table. Every tabular surface uses it (sticky header, mono uppercase<th>, keyset-paginated data underneath — list APIs are cursor-based, never offset). -
Grids use the shared grid classes — never a new one-off grid:
Class Lives in Min column Context content-gridpackages/ui/src/css/canvas-sheet.css240px Generations page, content picker sheet library-gridweb-v2 client/css/influencer-card.css272px Library pages (influencers, media, products) builder-grid/builder-grid-smweb-v2 client/css/creative-builder.css240px / 180px Builder detail views inside FullscreenModal
Sheet footers
At most two actions: the primary (does the thing the sheet is about) and Discuss with Elle (always secondary, right-aligned, mono uppercase subtle style — closes the sheet and drops the artifact into the composer as context).
Marketing pages
Page shells and section building blocks are listed in brand.md — content
pages compose from ContentLayout + the section kit, never bespoke section
scaffolding.