<!-- Protocore Design System — Sidebar -->
# Sidebar

- **Category:** Layout (`layout`)
- **Slug:** `layout/sidebar`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Sidebar } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/layout/sidebar

> A left navigation rail of grouped items — mono uppercase group labels over sans links, with an accent rule on the active item.

## When to use it

`Sidebar` is the persistent left rail for **application** wayfinding — a console or dashboard with a fixed set of destinations. It is the primitive; `AppShell` places it (a fixed column on desktop, an off-canvas drawer below 860px), so you rarely position it yourself. Use `TopBar` instead for a marketing or top-level site nav, and `Tabs` for switching views *within* a single page rather than navigating between pages. `Sidebar.Item` renders an `<a>` by default; pass `asChild` to wrap a router `<Link>` so client routing and `active` state stay in sync.

## Props

### Sidebar.Group

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | — |
| `collapsible` | `boolean` | no | — | Make the group collapsible. Its `label` becomes the toggle; children collapse. |
| `defaultOpen` | `boolean` | no | `true` | Uncontrolled initial open state (collapsible only). Default open. |
| `label` | `ReactNode` | no | — | Group heading. Rendered as the trigger when `collapsible`; otherwise use `Sidebar.GroupLabel`. |
| `onOpenChange` | `((open: boolean) => void)` | no | — | Open-state change callback (collapsible only). |
| `open` | `boolean` | no | — | Controlled open state (collapsible only). |
| `style` | `CSSProperties` | no | — | — |

### Sidebar.GroupLabel

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | — |
| `style` | `CSSProperties` | no | — | — |

### Sidebar.Item

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `active` | `boolean` | no | — | Mark as the current item — inks the label and shows the accent left rule. |
| `asChild` | `boolean` | no | — | Render into a passed child (e.g. a router `<Link>`) instead of an `<a>`. |
| `className` | `string` | no | — | — |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

Compose `Sidebar` from `Sidebar.Group`s, each a `Sidebar.GroupLabel` (mono uppercase) over a run of `Sidebar.Item`s. The current item takes `active`, which inks it and grows a 2px accent left rule.

```tsx
import { Sidebar } from "@protocore/pds";

export default function SidebarBasics() {
  return (
    <div style={{ maxWidth: 248 }}>
      <Sidebar.Root aria-label="Console navigation">
        <Sidebar.Group>
          <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
          <Sidebar.Item href="#" active>
            Dashboard
          </Sidebar.Item>
          <Sidebar.Item href="#">Nodes</Sidebar.Item>
          <Sidebar.Item href="#">Peers</Sidebar.Item>
        </Sidebar.Group>
        <Sidebar.Group>
          <Sidebar.GroupLabel>Ledger</Sidebar.GroupLabel>
          <Sidebar.Item href="#">Blocks</Sidebar.Item>
          <Sidebar.Item href="#">Transactions</Sidebar.Item>
          <Sidebar.Item href="#">Validators</Sidebar.Item>
        </Sidebar.Group>
      </Sidebar.Root>
    </div>
  );
}
```

### Collapsible groups

Set `collapsible` on a `Sidebar.Group` and give it a `label` — the label becomes a Radix Collapsible trigger with a chevron, and the items expand and collapse beneath it. Control it with `open`/`defaultOpen`/`onOpenChange`.

```tsx
import { Sidebar } from "@protocore/pds";

export default function SidebarCollapsible() {
  return (
    <div style={{ maxWidth: 248 }}>
      <Sidebar.Root aria-label="Settings navigation">
        <Sidebar.Group collapsible label="Infrastructure" defaultOpen>
          <Sidebar.Item href="#" active>
            Regions
          </Sidebar.Item>
          <Sidebar.Item href="#">Instances</Sidebar.Item>
          <Sidebar.Item href="#">Networking</Sidebar.Item>
        </Sidebar.Group>
        <Sidebar.Group collapsible label="Access" defaultOpen={false}>
          <Sidebar.Item href="#">API keys</Sidebar.Item>
          <Sidebar.Item href="#">Members</Sidebar.Item>
          <Sidebar.Item href="#">Audit log</Sidebar.Item>
        </Sidebar.Group>
      </Sidebar.Root>
    </div>
  );
}
```

## Do & don't

**Do**

- Group related destinations under a GroupLabel so the rail scans quickly.
- Mark exactly one Item active per view to anchor the user.
- Use asChild on Sidebar.Item to wrap your router's Link component.

**Don't**

- Use a Sidebar to switch tabs within one page — that is the Tabs component.
- Leave more than one Item active at a time.
- Nest another scrolling region inside the rail; let AppShell own the layout.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Move focus to the next item or group trigger |
| `Shift + Tab` | Move focus to the previous item or trigger |
| `Enter` | Follow the focused item, or toggle a collapsible group's trigger |
| `Space` | Toggle the focused collapsible group trigger |

**Notes**

- The root renders an `<aside>` — give it an `aria-label` so it is a labelled complementary landmark.
- The active item sets `aria-current="page"`, so the current location is exposed, not just coloured.
- Collapsible groups use Radix Collapsible: the trigger exposes its expanded/collapsed state to assistive tech.

## Related

`app-shell`, `top-bar`, `panel`, `footer`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
