npx shadcn@latest add https://elabs-ai.com/r/app-shell.jsonPaste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.
Add the brand-ui registry block "Flagship" (app-shell) to this project. A block is copied into the app and becomes my code. 1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`. 2. Find it and what it is built from: `npx -y @elabs-ai/components-cli search app-shell`. 3. Make sure the base is in place: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui`), plus the packages the block imports. If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`. 4. Copy the block in with `npx shadcn@latest add https://elabs-ai.com/r/app-shell.json`; if that url does not answer, copy its files from https://github.com/mreimitz/elabs-components/tree/main/registry/blocks/app-shell instead. Then adapt it here: [the screen or file, and the real data it should show] 5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop. 6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.
Dependencies
Examples
Brand Mark
The library's own brand mark in the rail header — `AppIcon`, not a stock glyph. This is the lock for that: every shell in this family renders the repo's brand component, so a re-brand is a token change and never an edit to four blocks.
Active Path Matching
A nested route lights its parent nav entry; an unrelated one stays dark.
Frame
Chrome only: every zone present, the content slot empty. The lock is the FRAME — top bar and nav rail mounted, the single `<main>` landmark reachable from the skip link — so a later refactor that quietly drops one fails here rather than in review.
Collapsed
The icon rail: labels gone, tooltips and accessible names intact.
Dock Open
The assistant dock summoned. It takes real width from the content column and gives it all back when dismissed — the mechanic that makes it a dock rather than permanent furniture.
Narrow
A phone: the nav rail becomes a drawer and the dock hands its body to an overlay instead of a column, so the content column never has to share a 320px viewport with either.
Loading
Nothing has arrived yet. Every region renders its own layout-shaped skeleton at the size the real content will occupy, so the screen never collapses and then expands under the reader.
Empty
A first run, or a filter that matched nothing: each region answers for itself with a real empty state — a title, one sentence, and no broken chrome.
Just Above The Breakpoint
Just above the `md` boundary. Every other story in this file sits at 320px or 1200px — both far from 768px, and on opposite sides of it — so a breakpoint slid by one step would fold a zone away for every reader between 768px and 1023px with nothing here noticing. 800px is also the width the OLD four-zone flagship could not serve: nav rail (256px) + list column (280px) + rail strip (48px) left the content pane about 230px, and KPI labels clipped mid-word. Two zones is the repair; this story is what proves the pane keeps its room.
Top Bar Help Wired
The top bar in isolation with `onHelp` supplied. The Help control is opt-in on purpose: it renders only when a consumer wires it, so the shipped block can never hand a copier a button that takes focus and does nothing. This story is what keeps the prop from becoming an unexercised API — the other half of the contract `Default` locks by asserting the button's absence.