Four layouts, one slot contract
Scaffolded apps come in four chromes: a classic sidebar, a top-header layout, a Windows-style desktop with a bottom taskbar and a start menu, and a macOS-style desktop with a top menu bar and a dock. They look nothing alike. They are also, structurally, the same component — because all four consume one interface.
The LayoutSlots contract
Every layout is a function of five slots: brand, nav, workspace, account, and content. The shell fills the slots — the brand mark, the nav items derived from the feature registry, the workspace switcher, the account menu, your page — and the layout decides where they go. SidebarLayout puts nav in a left rail. HeaderLayout puts it in a top bar. WindowsLayout renders it as taskbar tasks behind a start menu. MacosLayout turns each nav item into a dock icon with a label, and puts account and workspace in the menu bar.
export interface LayoutSlots {
brand: ReactNode;
nav: ReactNode; // or nav items, for the desktop metaphors
workspace: ReactNode;
account: ReactNode;
content: ReactNode;
}
export function SidebarLayout({ brand, nav, workspace, account, content }: LayoutSlots) { … }
export function MacosLayout({ brand, nav, workspace, account, content }: LayoutSlots) { … }
Why slots beat per-layout pages
The alternative — each layout owning its own page wiring — is how layout options usually rot. The moment a feature adds a nav entry or the account menu grows an item, four codepaths need the change, three get it, and the fourth ships broken. With slots, features never know which layout they live in. A FeatureModule registers once; the registry produces nav; the active layout places it. Adding a fifth layout is one new function in the LAYOUT_COMPONENTS record, and every existing and future feature appears in it automatically.
It also keeps the exotic layouts honest. The desktop metaphors are real chrome — the taskbar opens a start menu, the dock highlights the active app — but they get zero special treatment from feature code. The records feature's nine views render inside the content slot identically whether that slot sits next to a sidebar or above a dock. And because the chrome is built from the shell's CSS-variable contract, a Windows-style taskbar in the vaporwave design looks exactly as unhinged as you hope.
One flag
pnpm new my-idea --type webapp --layout macos
# layouts: sidebar | header | windows | macos
The layout is picked at scaffold time and validated like every other flag. The quality gates treat it as a first-class axis too: the self-test matrix scaffolds shapes across macos and windows and header, and the smoke test crawls nav whether it finds .lab-navlink entries or .lab-dock-item icons.
Features fill slots; layouts place them. Neither knows the other exists.
That one-way contract is the whole trick — and it is why four wildly different chromes cost the price of one.