# Sidebar

> A vertical navigation aside, sticky and full-height on desktop, collapsible behind a toggle on mobile.

URL: https://apogee.avaui.dev/docs/sidebar
Last updated: 2026-10-16

A vertical navigation aside, sticky and full-height on desktop, collapsible behind a toggle on mobile.

Last updated October 16, 2026

Uses JavaScript

A small island toggles `data-open` on the nav and `aria-expanded` on the button when the mobile toggle is clicked, showing or hiding the nav below the header row; a sibling CSS rule also reveals the footer once the nav is open. With `overlay` the same island moves the nav into a `<dialog>` drawer and back on close.

**Without JS:** The toggle is hidden (\`no-js:hidden\`) and the nav stays expanded (\`no-js:flex\`), and the footer stays expanded too (\`no-js:block\`) — so below the \`md:\` breakpoint everything is reachable without a working control to click. Desktop is unaffected: the nav is always visible above the \`md:\` breakpoint, shown by CSS alone.

```astro
<Sidebar>
<a slot="brand" href="/" class="font-semibold">Larkspur</a>
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/settings">Settings</a>
<div slot="footer">v1.2.0</div>
</Sidebar>
```

Resize below `md` (768px) to see the mobile header + toggle; the nav and footer behave differently at that width (see Anatomy).

## Add

```sh
npx @ava-ui/apogee add sidebar
```

Ships `@lucide/astro` (for the toggle’s `Menu` icon). No Apogee component dependencies.

## Usage

```astro
---
import Sidebar from '@/components/apogee/Sidebar/Sidebar.astro'
---
<Sidebar>
  <a slot="brand" href="/">Larkspur</a>
  <a href="/dashboard">Dashboard</a>
  <a href="/settings">Settings</a>
</Sidebar>
```

## Anatomy

-   **Header row** — always visible; holds `brand` and the mobile toggle. On desktop the toggle is hidden (`md:hidden`) and the row loses its bottom border.
-   **`brand`** (`slot="brand"`) — logo or product name, left-aligned in the header row.
-   **Nav** (default slot) — links or `NavLink`\-like items, stacked with a 2px gap. Collapsed by default on mobile; always shown on desktop.
-   **`footer`** (`slot="footer"`) — pinned to the bottom on desktop (`mt-auto`). On mobile with JS, hidden until the nav is opened, then revealed via a sibling CSS rule. Without JS (`no-js:block`) it’s always shown.
-   **Toggle button** — auto-rendered, `md:hidden`. Controls the nav via `aria-controls` (a generated id) and `aria-expanded`.

## API

`@composable` — exposed to Content Editors in AVA. Any other `<aside>` attribute passes through.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `'default' \| 'slim'` | `'default'` | `slim` is a 4rem icon rail from `md`: NavLink labels become screen-reader only, links centre. Below `md` it behaves like the default. |
| `overlay` | `boolean` | `false` | On mobile, the nav opens as a start-edge drawer over the page (a native `<dialog>`) instead of pushing content down. Without JS the inline panel remains. |
| `toggleLabel` | `string` | `'Toggle navigation'` | Name of the phone toggle button. |
| `overlayLabel` | `string` | `'Navigation'` | Name of the `overlay` drawer. |
| `class` | `string` | — | Extra classes on the root `<aside>`, merged last. |

## States

-   **Mobile, closed (default)** — nav hidden (`display: none`), toggle `aria-expanded="false"`.
-   **Mobile, open** — clicking the toggle sets the nav’s `data-open="true"` and the toggle’s `aria-expanded="true"`; the nav becomes a flex column below the header.
-   **Desktop (`md:` and up)** — nav always visible (`md:flex`); toggle hidden.
-   **Overlay open** (mobile, `overlay`) — the nav and footer live inside an open `<dialog>`; Escape, the close button or the backdrop close it and move them back. Resizing to desktop while open closes it.

## Examples

Nav links with an icon, and a footer used for a persistent action:

```astro
<Sidebar>
<a slot="brand" href="/" class="font-semibold">Larkspur</a>
<a href="/dashboard">Dashboard</a>
<a href="/settings" class="flex items-center gap-2"><Settings class="size-4" /> Settings</a>
<div slot="footer" class="text-sm text-neutral-faded">Signed in as jane@example.com</div>
</Sidebar>
```

Groups, section headings and a featured card in the footer. `NavGroup` is a native `<details>` that opens when it contains the current page; headings are plain text; a Divider separates sections.

```astro
<Sidebar>
<a slot="brand" href="/">Larkspur</a>
<NavLink href="/dashboard" match="prefix"><Home slot="start" />Dashboard</NavLink>
<NavGroup label="Projects">
  <FolderKanban slot="start" />
  <NavLink href="/projects/spring">Spring launch</NavLink>
  <NavLink href="/projects/site">Website</NavLink>
</NavGroup>
<Divider inset={2} />
<p class="px-3 pb-1 text-xs font-semibold uppercase tracking-wide text-neutral-faded">Team</p>
<NavLink href="/members"><Users slot="start" />Members <Badge slot="end" size="small">4</Badge></NavLink>
<NavLink href="https://help.example.com"><LifeBuoy slot="start" />Help centre</NavLink>
<Dismissible slot="footer" label="Dismiss">
  <Card padding="small">…<Progress value={60} label="Storage" />…</Card>
</Dismissible>
</Sidebar>
```

## Slim rail

An icon-only rail from `md`. Give every link a `start` icon; wrap it in a Tooltip for a visible name on hover and focus (the label stays in the accessibility tree). A Popover on a rail item is the flyout for a group. Its trigger takes the NavLink classes (`navLinkClass`, `navLinkIdle`) and an `apogee-navlink-label`, so it shows its label on phones and goes icon-only on the rail like the links:

```astro
<Sidebar variant="slim">
<a slot="brand" href="/" aria-label="Larkspur">A</a>
<Tooltip text="Dashboard" placement="end"><NavLink href="/dashboard"><Home slot="start" />Dashboard</NavLink></Tooltip>
<Tooltip text="Inbox" placement="end"><NavLink href="/inbox"><Inbox slot="start" />Inbox</NavLink></Tooltip>
<Popover id="rail-projects" placement="end">
  <button slot="anchor" popovertarget="rail-projects" class={`${navLinkClass} ${navLinkIdle}`}><FolderKanban class="size-4" /><span class="apogee-navlink-label">Projects</span></button>
  <NavLink href="/projects/spring">Spring launch</NavLink>
  <NavLink href="/projects/site">Website</NavLink>
</Popover>
</Sidebar>
```

## Overlay

On a phone the nav opens as a drawer over the page. Resize below `md` and press the toggle:

```astro
<Sidebar overlay>
<a slot="brand" href="/">Larkspur</a>
<NavLink href="/dashboard"><Home slot="start" />Dashboard</NavLink>
…
</Sidebar>
```

## Accessibility

| Key | Action |
| --- | --- |
| `Tab` | Moves through the toggle and the nav links in order. |
| `Enter` `Space` | On the toggle, open or close the nav. |
| `Esc` | With `overlay`, closes the drawer and returns focus to the toggle. |

-   Toggle is a real `<button>`. It carries `aria-controls` (pointing at the nav’s id), `aria-expanded`, and `aria-label="Toggle navigation"`.
-   Nav links are plain `<a>` elements in normal tab order.
-   Visible `focus-visible` ring on the toggle.
-   **No JS:** the toggle is `no-js:hidden`, and the nav (`no-js:flex`) and footer (`no-js:block`) both stay expanded — so on a small viewport without JavaScript, the nav and every link in it, plus the footer, are reachable without a working control. Desktop viewports are unaffected, since the nav is always shown there by CSS alone.
-   With `overlay`, the drawer is a native modal `<dialog>` (focus trapped, Escape closes, focus returns to the toggle) labelled “Navigation”. The nav is moved, not copied, so there is never a duplicate landmark.

## Tokens

| Token | Used for |
| --- | --- |
| `--border-neutral` | Aside end border (desktop), header bottom border (mobile), footer top border |
| `--background-page` | Aside background |
| `--foreground-neutral` | Toggle icon colour |
| `--background-neutral-faded-highlighted` | Toggle hover background |
| `--outline-color-focus` | Toggle focus ring |

## Do / Don’t

-   **Do** put the most-used links first — they’re always visible on desktop, and stay expanded on mobile without JS too.
-   **Do** use `brand` for a logo or product name only, not another nav item.
-   **Don’t** worry about the mobile toggle as a hard dependency — without JS it’s hidden and the nav/footer fall back to expanded, so content stays reachable either way.
-   **Don’t** put page content in `footer` — it’s for persistent bits (account switcher, version, sign out); on mobile with JS it only shows once the nav is opened.

## Content

-   Nav labels: nouns, sentence case, one to three words (“Dashboard”, “Team settings”).
-   Footer content stays short — a name, plan, or single action; it’s cramped in the rail.

## Related

[

NavbarA responsive site header that collapses its links into a mobile panel below the md breakpoint.

](https://apogee.avaui.dev/docs/navbar)[

NavLinkA navigation link that auto-detects and styles the current route, with prefix matching, external links and a collapsible NavGroup.

](https://apogee.avaui.dev/docs/nav-link)[

FooterA site footer shell for link columns and a bottom copyright row.

](https://apogee.avaui.dev/docs/footer)
