import { SideDock } from "@elabs-ai/components-ui";npm install @elabs-ai/components-tokens @elabs-ai/components-uiPaste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.
Add the brand-ui SideDock component (from @elabs-ai/components-ui) to this project. 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. Look SideDock up before writing code: `npx -y @elabs-ai/components-cli docs SideDock` prints its real props, variants, what it pairs with and what to avoid. 3. If @elabs-ai/components-ui is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui`). 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. Use it here: [the screen or file, and what 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.
Examples
Closed
`open={false}` (the default) — collapsed, `inert`, off-screen; its body content stays mounted for the closing transition.
Left Handed
`side="left"` — the handle sits on the trailing edge and the keyboard direction inverts (`ArrowRight` widens).
Overlay
Below `overlayBreakpoint` the dock renders as an overlay `Sheet` instead of a column, with no resize handle. A very high `overlayBreakpoint` forces this branch deterministically under a headless run, mirroring `ContextRail`'s own `Narrow` story.
In Sidebar
`containerPosition="inset"` (#432): nested inside a `Sidebar` + `SidebarInset` host layout — mirroring the real composition (`DashboardAssetPanel` on the left, `DashboardPropertiesPanel` on the right of a sheet inside an app `Sidebar`) — `side="left"` would otherwise race the Sidebar's own `fixed left-0` container for the SAME viewport edge — the later-mounted dock wins the paint and hides the app's own nav (`wave-4-review-visual.md` P0-1). The play function proves the fix geometrically for BOTH edges: the Sidebar's nav stays visible, and neither dock's rect ever intersects the Sidebar's or the canvas's.
API
SideDock
| Prop | Type | Description |
|---|---|---|
titlerequired | ReactNode | The dock's name. Rendered as its header heading, and as the `SheetTitle` of the overlay presentation. REQUIRED: the overlay cannot be accessible without it, and an optional prop with a generic default would ship a meaningless announced name into real apps. |
description | ReactNode | Optional longer description. Visible at the top of the body in the column presentation — never inside the header band, which is fixed at the app shell's header height; `sr-only` in the overlay presentation (the compact sheet header has no room for it, but it still reaches assistive tech). |
headerActions | ReactNode | Optional controls placed in the header row beside the built-in close button. |
open | boolean | Controlled open state. Closed means zero width (content stays mounted, inert). |
defaultOpen | boolean | Uncontrolled initial open state. Default `false` — a dock is summoned. |
onOpenChange | (open: boolean) => void | Called with the next open state. |
side | "left" | "right" | Which edge it docks to. Default `"right"`. |
containerPosition | CollapsiblePanelContainerPosition | Where the column presentation's fixed/absolute container positions itself. Default `"viewport"` (pins to the viewport edge — byte-identical pre-#432 behavior). `"inset"` pins to the dock's OWN position in the layout instead (`absolute` against a `relative` wrapper this component provides) — use this when the dock is nested inside another element that already pins itself to the same viewport edge (e.g. an app `Sidebar`), so the two never race for the same edge (#432). Has no effect on the overlay `Sheet` presentation below `overlayBreakpoint`. |
width | number | Controlled width in px. |
defaultWidth | number | Uncontrolled initial width in px. Default `400`. |
onWidthChange | (width: number) => void | Fires continuously during a resize — drive layout from this. |
onWidthCommit | (width: number) => void | Fires once when an interaction ends — PERSIST from this. |
minWidth | number | Lower bound in px. Default `320`. |
maxWidth | number | Upper bound in px. Default `640`. |
minContentWidth | number | Content width preserved when clamping against the live viewport, px. Default `480`. |
resizable | boolean | Whether the resize handle renders. Default `true`. |
overlayBreakpoint | number | Viewport width in px below which the dock renders as an overlay `Sheet` instead of a column. Default `1100`. Deliberately ABOVE the library's 768px mobile breakpoint: a 400px dock at 768px leaves ~360px of content. This is the switch point for the column-only sizing props above — the overlay presentation ignores them entirely. |