A full-screen tool — the one page class with no kit yet, because a shell extracted from a single instance is premature. It records what a second instance must follow.
How every full-screen tool — a workbench — is built. Unlike the other page classes there is no kit yet: the class has one full instance, and a shell extracted from a single sample is premature abstraction. This law records the structure that instance proved and the rulings a second instance must follow; extract the kit when it arrives. The living examples are Icon Studio (/icon-studio, the full form) and Showcase (/showcase, the stage-only form). Compositions (/labs/compositions) is the catalogue form: stage, material strip and a library rail, with no inspector. Its route group is (workbench) so its established /labs URL does not change.
01
A page earns chromeless full-viewport treatment when the interaction is canvas-centric — the user manipulates one artifact and wants every pixel for it. Icon Studio (an icon recipe under continuous adjustment) and Showcase (a card framed for screen recording) qualify; a dashboard or a data view never does — density is not the same thing as canvas-centricity.
The cost of leaving the (app) shell is real: no sidebar, no app header, no settings drawer. A workbench therefore must carry its own survival kit — a return path ("← Back to app") and a theme toggle in its own toolbar, because nothing else will provide them.
02
┌──────────────────────── Toolbar ────────────────────────┐
│ identity · view controls …… export · back · theme │
├──────────┬──────────────────────────────┬───────────────┤
│ Library │ Stage │ Inspector │
│ (rail) │ (the artifact) │ (properties) │
└──────────┴──────────────────────────────┴───────────────┘| Zone | Owns | Rule |
|---|---|---|
| Toolbar | View-level state (view mode, backdrop, zoom), export, the return path, the theme toggle | Controls that change how you look at the document, never what it is. |
| Library rail (left) | Assets you load — presets, glyphs, templates | Picking only. Loading an asset patches the document. |
| Stage (center) | The artifact, rendered from the document | Fluid; takes every remaining pixel. Direct manipulation (drag on the artifact) belongs here. |
| Inspector (right) | Properties you tune on what's loaded | Tuning only, composed from the studio kit's row vocabulary (docs-level law: src/components/studio/). |
Pick left, tune right is the load-bearing ruling — the split every drawing app converges on. An asset picker in the inspector, or a property slider in the rail, is the class's central anti-pattern.
The catalogue form (Compositions) pairs a finished layout with a material; geometry and copy stay fixed, so it drops the full inspector and gives the stage the leading left position. The right-hand library chooses type and preset; the strip below the stage chooses material. This is the explicit exception to rail placement, not to responsibilities: the rail loads whole designs. Its explicitly authorized Gradient curve adjustment lives in a separate section for opted-in presets, without creating an editor. It does not yet create a second full three-zone instance, so it does not trigger extracting a shared workbench shell.
The stage-only form (Showcase) drops both rails: toolbar + stage, with the few knobs it has (width preset, surface, replay) living in the toolbar. That is the degenerate case of the same anatomy, not a different class.
03
Recipe) is the document; toolbar, rail and inspector all patch it, and the stage renders it. Zone-local state is allowed only for view concerns (zoom, backdrop) — never for anything the export depends on.min-h-dvh flex-col and locks to lg:h-dvh lg:overflow-hidden; each zone scrolls internally. Below lg the zones stack stage first (the artifact is why the user came), rails after.(app) but the design system still applies in full: theme tokens, elevation, the icon family, toasts on every export/copy.04
(app); give the toolbar the return link and the theme toggle before anything else.patch; wire rail (load) and inspector (tune) through it.src/components/workbench/ and update this law.05
(app) shell, scrolling the whole page.useState is corrupted).