# Shell reference

`Layout` is the shell every page renders inside. Your site never
mounts it directly — the renderer entries (`@dreamlake/dockit/server`
and `/client`) wrap each page in it — but understanding its anatomy
explains where everything on screen comes from.

## Layout anatomy

```text
┌─ Topbar ────────────────────────────────────────────────┐
│ brand / subtitle [chips]   breadcrumb   tabs   search ⌘K│
├──────────┬───────────────────────────────┬──────────────┤
│ Sidebar  │  main.doc-content             │  TOC rail    │
│ 280px    │  breadcrumb                   │  280px       │
│ (md+)    │  {page MDX}                   │  (lg+)       │
│          │  DocFooter (prev/next)        │              │
└──────────┴───────────────────────────────┴──────────────┘
             SearchPalette (fixed overlay, ⌘K)
```

The grid is `280px / minmax(0,1fr) / 280px`, max-width 1320px; the
sidebar drops below `md`, the TOC below `lg`. Override `--doc-sidebar-width`
and `--doc-toc-width` independently in your site stylesheet. The content column caps
at 760px at `lg` and up, and carries `data-pagefind-body` so only page
prose enters the search index (the breadcrumb and footer are
`data-pagefind-ignore`d). On `noindex` pages the marker is omitted
entirely, which keeps the whole page out of the index.

## What Layout owns

- **Providers** — `ThemeProvider`, the `MDXProvider` carrying the
  [component map](/reference/exports.md), and the merge context below.
- **Search state** — the open flag and query shared by the
  [Topbar](/reference/components/topbar.md) field and the
  [SearchPalette](/guides/search.md).
- **Merge state** — a rAF-throttled scroll watcher flips a context
  boolean when the page H1's bottom edge passes under the 40px topbar;
  the page-top breadcrumb fades out and the topbar crossfades from
  brand to breadcrumb. Components can read the flag with `useMerge()`.
- **Page-top breadcrumb** — a mono `Section / Title` eyebrow rendered
  above every H1 (including on `/`), hidden again once merged.
- **Head sync** — with Vike's client routing, `<head>` is not
  re-rendered on navigation; Layout syncs `document.title`
  (`{title} — {brand}`), the description meta, and the robots meta
  (for `noindex` pages) from the current page's frontmatter.

## Layout props

`Layout` takes only `{ children }` — the rendered page. Everything
else derives from Vike's page context, the navigation singleton, and
`siteConfig`.

## Fullscreen pages

`fullscreen: true` in frontmatter turns off the content column, TOC,
breadcrumb, and footer, and gives the page the full viewport below the
topbar — for playgrounds and dashboards:

```mdx
---
title: Playground
section: Tools
fullscreen: true
---
```

The sidebar stays (at `md+`), and the main region becomes an
`overflow-auto` panel sized to `calc(100vh - 40px)`.

## DocFooter

The prev/next navigation cards at the bottom of every non-fullscreen
page. You are looking at a live instance right now — scroll to the end
of this page: the cards below the content are ``,
rendered by the Layout automatically.

Adjacency is the **global page order** — every page sorted by its
`order` frontmatter — not the sidebar grouping, so "Next" walks across
section boundaries in reading order. `getAdjacentPages(path)` finds
the current page in that sequence and returns its neighbors:

```ts

const { prev, next } = getAdjacentPages('/reference/components/shell')
```

Rules:

- `hidden: true` pages are skipped, so a reader walking the docs never
  lands on an internal page — unless dev mode (**Cmd+Shift+D**) is on,
  which `DocFooter` honors via `useHiddenToggle` (it passes
  `{ includeHidden: true }` to `getAdjacentPages`).
- If the current page is itself hidden, it still participates, so the
  reader can step back out.
- The footer renders nothing when the page has neither neighbor.

`` takes no props — the current URL comes from Vike's
page context and the sequence from the navigation singleton. Direct
use only matters in a custom layout.

## Escape hatch

Everything Layout composes is exported individually (`Topbar`,
`Sidebar`, `TOC`, `DocFooter`, `SearchPalette`, `mdxComponents`), so a
custom shell can rearrange the pieces — see the
[exports reference](/reference/exports.md).

## Sidebar

The 280px left rail listing every visible page, grouped by section.
It is entirely derived — the [conventions](/guides/authoring.md)
page covers the authoring side; this page covers behavior.

### Sidebar anatomy

```text
│ GETTING STARTED      05 │  ← section header (mono, click to fold)
│   Installation          │
│   Quick start           │  ← active page highlighted
│ APP CHROME         + 04 │  ← folded group (+ affordance, count stays)
│ DEV                  02 │
│   Gallery            🔒 │  ← hidden page, dev mode on
│   Contributing  [DRAFT] │  ← draft chip
```

Each header carries a zero-padded item count on the right and a
fold affordance (`−`/`+`) on hover. The rail is sticky below the 40px
topbar, scrolls independently, and disappears below `md`.

### Sections and order

- Groups come from each page's `section` frontmatter.
- Group order = the `sectionOrder` array passed to `initDocs`; unknown
  sections fall to the end alphabetically (so a new section shows up
  without breaking the nav).
- Page order within a group = `order` frontmatter (missing = 99).

Section headers are collapsible; the collapsed set persists in
localStorage (key `sidebar-collapsed`), so a reader's pruned sidebar
survives reloads. Navigating into a page whose group is folded
auto-unfolds that group.

Active-page matching is trailing-slash tolerant (`/guides/` matches
`/guides`): the sidebar compares against `normalizePath(urlPathname)`,
so prerendered HTML and client routing agree on `aria-current` even
when they disagree on the trailing slash. The same helper backs the
breadcrumbs, prev/next footer, and TOC edit links.

### Tab filtering

With [tabs](/reference/components/topbar.md#tabstrip) declared, the sidebar shows only
groups containing at least one page under the active tab's
`urlPrefix`. The same section label can appear under several tabs —
the filter runs per tab, so each lens shows its own slice. With no
tabs registered, every section shows.

### Hidden pages — Cmd+Shift+D

`hidden: true` pages stay out of the sidebar, search results, and
prev/next by default, while remaining reachable by direct URL — the
convention for internal/dev notes. Press **Cmd+Shift+D** and:

- hidden pages appear in the sidebar (with a lock chip),
- the topbar shows an orange dev-mode dot (click it to turn off),
- search and the [DocFooter](/reference/components/shell.md#docfooter) include
  them.

`draft: true` marks a page as awaiting review — it stays visible but
carries a `DRAFT` chip so reviewers can spot it.

> **Note:** `hidden` is about the reader's chrome. `noindex` is about robots and
> generated artifacts — it emits a noindex meta and excludes the page
> from pagefind and every [LLM surface](/guides/llms.md). Internal pages
> usually want both.

### Sidebar direct use

`` takes no props — groups come from the navigation
singleton (`groupedPages` / `groupedVisiblePages`), the active tab
from the current URL, and the fold state from localStorage. The
[Layout](/reference/components/shell.md) mounts it on every page, fullscreen
included.

## Table of contents

The right rail (visible at `lg` and up — you should see it now, to the
right) lists this page's headings with a scroll-spy: the animated SVG
rail and dot track your position as you read.

### TOC anatomy

```text
│ ON THIS PAGE          │
│ ●  Anatomy            │  ← dot rides the rail to the active heading
│ │  What it indexes    │
│ │   · sub heading     │  ← H3: smaller mono, foldable under its H2
│ │  Rail footer links  │
│ ┊                     │
│ ──────────────────────│
│ View as Markdown      │
│ Edit this page        │
│ Report an issue       │
```

Headings come from the rendered DOM, not frontmatter: after each
navigation the TOC queries `main.doc-content` for anchored headings,
so anything MDX renders as an `h2`/`h3` with an id shows up. H3s fold
under their parent H2; the fold state persists **per page** in
localStorage, and unfolding scrolls the active entry back into view.

### What it indexes

- **H2** headings always; **H3** headings by default, rendered in a
  smaller mono style and foldable under their parent H2.
- Set `tocLevel: 2` in frontmatter on long pages where H3 noise would
  crowd the rail:

```mdx
---
title: Very long reference
tocLevel: 2
---
```

### Rail footer links

Below the headings, the rail carries three derived links:

| Link | Where it goes |
| --- | --- |
| **View as Markdown** | The page's `.md` twin generated by [gen-llms.mjs](/guides/llms.md) — `/reference/components/shell.md` for this page. |
| **Edit this page** | `{docsRepoUrl}/edit/{docsBranch}/{docsPagesPath}/<slug>/+Page.mdx` — jumps straight to the source on GitHub. |
| **Report an issue** | `{docsRepoUrl}/issues/new`. |

### TOC configuration

`docsPagesPath` (default `docs/pages`) is the repo-relative directory
holding `pages/` — set it to match your layout, e.g. `pages` for a
repo-root site or `packages/docs/pages` for a nested package:

```ts file="site.config.ts"
site: {
  docsRepoUrl: 'https://github.com/dreamlake-ai/dockit-workspace',
  docsBranch: 'main',
  docsPagesPath: 'docs/pages',
}
```

Note the split: `docsRepoUrl` is the workspace repo hosting the pages
(edit/issue links); `repoUrl` is the product repo behind the topbar's
GitHub icon. They often differ.

### TOC direct use

`` takes no props; it reads headings from the rendered
`main.doc-content` and page metadata (`tocLevel`, the `.md` path) from
the navigation singleton. The [Layout](/reference/components/shell.md) mounts it
on every non-fullscreen page.
