Skip to main content

Double-Sided

App Shells

Two navigation levels plus a summoned third surface: a permanent 56px icon rail picks the area, a collapsible second panel lists that area's sections, and the floating inset carries the settings screen. A resizable right-hand SideDock holds the change history — a column above 1100px, an overlay below it. The icon rail is deliberately not a Sidebar, so it survives every breakpoint while the section panel becomes a sheet.

npx shadcn@latest add https://elabs-ai.com/r/sidebar-05.json

Skipping the @source line is the most common first failure: Tailwind does not scan node_modules, so every component renders unstyled. The one-time wiring

Prompt for your coding agent

Paste 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 "Double-Sided" (sidebar-05) 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 sidebar-05`.
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/sidebar-05.json`; if that url does not answer, copy its files from https://github.com/mreimitz/elabs-components/tree/main/registry/blocks/sidebar-05 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

  • @elabs-ai/components-icons
  • @elabs-ai/components-tokens
  • @elabs-ai/components-ui
  • lucide-react

Examples

13 live examples

Panel Collapsed

The panel put away. The rail is the half that never goes anywhere, so the claim is not "the panel is hidden" but "the panel is hidden AND the rail is still the thing at x=28" — the failure `group-data-[collapsible=offcanvas]:ms-0` prevents, where 56px of a slid-away panel keeps covering the rail.

Area Switching

Switching AREAS from the icon rail, driven the way a person drives it. The rail is a switcher, not four links: a different area re-points the second panel WITHOUT navigating, and re-clicking the area already showing closes the panel. Both halves live in one handler in the shell, and neither is observable from any prop this story passes — the only way to see them is to click the rail and read what the panel became.

Section Selection

Choosing a SECTION from the second panel. The row is a plain `<a href>` whose click is intercepted, so "it navigated" cannot be read from the URL — it has to be read from the content pane. The assertion is therefore what the detail pane now says, not which prop the story passed in.

Dock Open

The dock, summoned — the reason this shell exists. Three claims, and none is a presence check: the dock reaches assistive tech as a NAMED `complementary` landmark, its resize handle is genuinely operable from the keyboard, and the live stream and the end-of-gesture commit are two separate seams. "Operable" is measured by pressing a key and watching `aria-valuenow` move, because a handle that is focusable and inert passes every attribute assertion anyone would think to write.

Dock Dismissed

The same dock, dismissed — the second half of the disclosure round trip. It is a story of its own rather than the tail of `DockOpen` so that each one's FINAL PAINT matches its own name. A play function that opens a thing, asserts it, then closes it again documents the CLOSED state under an OPEN name — every screenshot, docs frame and visual diff taken from it shows the state the story says it is not showing.

No Selection

An area open with nothing selected in it — the route a rail click produces before a section is picked. The content pane must say so rather than render an empty frame.

Loading

Nothing has arrived yet. Both navigation and content render layout-shaped skeletons, and the whole not-ready screen announces ONCE — one live region, not one per box.

Empty Area

An area with no sections at all — the panel says so in CHROME ink, on chrome ground.

Narrow

Below the `md` boundary — 414px, a phone. The claim that makes this shell what it is: the icon rail is STILL on screen. A `Sidebar` becomes a closed `Sheet` here, and a closed sheet cannot be the thing you reopen the app from — which is why the rail is a plain column. The section panel, which you genuinely can put away, is the sheet.

Narrow Dock Open

The narrow shell with the change-history dock summoned. Split from `Narrow` so each story's FINAL PAINT is the one its name promises. The claim is the branch itself: below `SideDock`'s own breakpoint there is no resizable column anywhere in the document — what opens is a modal dialog over the shell, and it is dismissible from the keyboard like any other.

Just Above The Breakpoint

Just above the `md` boundary — the width band nothing else in this file sees. Every other desktop story renders at 1280px and `Narrow` at 414px: both far from 768px, on opposite sides of it. Sliding the shell's layout breakpoint one step, `md:` to `lg:`, would switch every reader between 768px and 1023px to the phone layout and leave this file green. It also reads the OTHER breakpoint in this shell, which is not the same number: `SideDock` swaps to an overlay below 1100px, so at 800px the section panel is a column and the dock is not.

Overflowing Content

Very long labels and summaries everywhere at once. A surface is designed for short, average AND overflowing content; this is the third case, asserted rather than eyeballed.