# AppShell

> The frame every signed-in screen sits in: a tinted frame holding the sidebar and an inset content card.

```ts
import { AppShell } from "@sdxc/ui/app";
```

Renders the shell. The sidebar is one `<aside popover="auto">`: a grid column from `sheetBelow`,
the icon rail while `collapsed`, and a sheet the top bar's menu button opens below it. Load
`@sdxc/ui/app/app-shell-toggle` in the browser entry for the cookie, ⌘B and Esc-back.

## Examples

```tsx
<AppShell labels={labels} collapsed={readAppShellCookie(request.headers.get("Cookie"))} workspace={<AppShell.Identity … />} nav={<AppShell.Group title="Monitoring"><AppShell.Item href="/monitors" icon={<ActivityIcon />} label="Monitors" current /></AppShell.Group>}>
	<PageHeader title="Monitors" />
</AppShell>
```

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `labels` | `Labels` |  |
| `collapsed?` | `boolean` | Draws the sidebar as the icon rail; read it with `readAppShellCookie`. @default false |
| `workspace?` | `RemixNode` | The workspace switcher at the top of the sidebar, usually an `AppShell.Identity`. |
| `nav` | `RemixNode` | The sidebar's links: `AppShell.Group`s of `AppShell.Item`s, or a swapped panel. |
| `footer?` | `RemixNode` | The foot of the sidebar, usually the user menu's `AppShell.Identity`. |
| `topbar?` | `RemixNode` | The top bar's content: a breadcrumb, a search field, the one global action. |
| `banner?` | `RemixNode` | A notice above the whole frame, such as a sandbox banner; it pushes the frame down. |
| `children` | `RemixNode` | The screen, rendered inside `<main>`. |
| `peek?` | `boolean` | Opens the rail over the content while the pointer rests on it. @default true |
| `indicator?` | `"fill" \| "pill"` |  |
| `id?` | `string` | Prefix for the sidebar's and main's ids (`<id>-sidebar`, `<id>-main`). |
| `cookieName?` | `string` | The collapse cookie's name, for an app with two shells. @default APP_SHELL_COOKIE |
| `sheetBelow?` | `SheetBelow` | The width under which the sidebar is a sheet, for a screen whose own panes need the room, such as a three-pane split view at 1024px. @default "48rem" |


### AppShell.Group

Renders a titled group of rows.

| Prop | Type | Description |
| --- | --- | --- |
| `title?` | `string` | A plain sentence-case title; it turns into a hairline in the rail. |
| `children?` | `RemixNode` |  |

Also accepts everything in `Omit<TagProps<"div">, "title">`.


### AppShell.Item

Renders one row: a 16px icon at reduced ink, the label, and an optional count pill.

Takes no props of its own.

### AppShell.Back

Renders the back row of a swapped panel ("‹ Settings"), drawn by the server in place of the app's nav.

Takes no props of its own.

### AppShell.Identity

Renders a switcher or account row: a 24px avatar, a name and an optional detail line.

| Prop | Type | Description |
| --- | --- | --- |
| `avatar` | `RemixNode` | An avatar or logo, drawn whole at 24px whatever its own size: a root `Avatar` at any `size` fills the slot and an `<img>` covers it. It stays visible in the rail. |
| `name` | `string` |  |
| `detail?` | `string` | A second line, such as a plan or an email. |
| `chevron?` | `boolean` | Draws the up-down chevron on a row that opens a menu. @default true |

Also accepts everything in `Omit<TagProps<"button">, "children">`.


## Types

| Name | Values | What it decides |
| --- | --- | --- |
| `Indicator` | `"fill" \| "pill"` | How the current row is marked: a soft fill, or a 2px pill on its leading edge. |
| `CountTone` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger"` | The tone of a row's count; a solid tone reads as "needs you", `neutral` as a tally. |
