# Configuration

Everything configurable in dockit flows through one call — `initDocs`
— made from your `site.config.ts` before the shell renders.

## initDocs(options)

```ts

initDocs({
  site: { /* Partial */ },
  pageMetadata: import.meta.glob('./pages/**/+Page.mdx', { eager: true, query: '?frontmatter', import: 'default' }),
  rawPages: import.meta.env.DEV ? import.meta.glob<string>('./pages/**/+Page.mdx', {
    eager: true, query: '?raw', import: 'default',
  }) : undefined,
  sectionOrder: ['Getting started', 'Reference'],
  tabs: [],
})
```

| Option | Type | Required | Purpose |
| --- | --- | --- | --- |
| `site` | `Partial` | yes | Branding, URLs, chips — merged over neutral defaults. |
| `pageMetadata` | eager `?frontmatter` glob | one of these | Metadata-only loader keeps page bodies out of startup JavaScript. |
| `pages` | eager glob of `+Page.mdx` | one of these | Legacy module map; use either `pages` or `pageMetadata`. |
| `rawPages` | eager `?raw` glob | no | Feeds fallback full-text search. Prefer dev-only; production Pagefind searches built HTML. |
| `sectionOrder` | `string[]` | no | Sidebar section order; unknown sections fall to the end. |
| `tabs` | `TabEntry[]` | no | Topbar tab lenses and external links; omit for a tabless site. |
| `search` | `SearchConfig` | no | Runtime search options; overrides `site.search` when both are provided. |

## SiteConfig

| Field | Type | Effect |
| --- | --- | --- |
| `brand` | `string` | Top-left brand; llms.txt heading. |
| `subtitle` | `string` | Muted label after the brand. |
| `repoUrl` | `string` | Topbar GitHub icon (empty hides it). |
| `docsRepoUrl` | `string` | Base for Edit/issue links in the TOC rail. |
| `docsBranch` | `string` | Branch in the edit link (default `main`). |
| `breadcrumbRoot` | `string` | Leftmost crumb (empty omits it). |
| `url` | `string` | Public origin — absolute URLs in LLM artifacts. |
| `skill` | `string` | Agent-skill name (`skills/<skill>/`, `<skill>.zip`). |
| `summary` | `string` | One-liner: llms.txt blockquote + skill description. |
| `skillTriggers` | `string[]?` | Curated task phrases for the generated skill's "Use when:" trigger list; unset falls back to the site's section names. |
| `versionChips` | `VersionChip[]?` | Topbar chips (see below). Individual tabs can override this via [`TabDef.versionChips`](#tabdef). |
| `gitHash` | `string?` | Git-hash chip (empty hides it). |
| `docsPagesPath` | `string?` | Repo-relative pages dir for edit links (default `docs/pages`). |
| `themeToggle` | `'cycle' \| 'segmented'?` | Topbar theme control: single cycling button (default) or the three-button slider — see [Theming](/guides/theming.md#themetoggle). |
| `mdxComponents` | `Record<string, Component>?` | Site-specific MDX tags merged over the library's provider map — pages use them without per-page imports. |
| `headScripts` | `string[]?` | External script URLs injected as `<script src defer>` into every page head (e.g. the Studio design-integration bridge). URLs only; non-URL entries are dropped. |
| `search` | `SearchConfig?` | Runtime search options; see [Search](/guides/search.md) for multisite indexing. |
| `indexable` | `boolean?` | Search-engine visibility. **Default `false`** (org policy): every page carries `noindex, nofollow` unless the site opts in with `indexable: true`. In-site search, llms.txt, and skills are unaffected. |

`skillTriggers` phrases should read as task descriptions — "theming
the docs shell", "wiring Netlify deploys" — since an agent matches
them against what it is currently doing. See
[LLM-readable docs](/guides/llms.md) for where they land.

## VersionChip

```ts
interface VersionChip {
  label: string      // left segment, e.g. 'dockit'
  version: string    // right segment, rendered as v<version>
  dropdown?: boolean // true → version switcher
  prefix?: string    // label prefix; defaults to 'v', '' for revisions
  manifestUrl?: string // defaults to '/versions.json'; path or absolute URL
}
```

Each chip renders as a `` — also exported from the
library, so a site can place one outside the topbar:

```tsx

```

`prefix` controls display only. Use `prefix: ''` for a revision such as
`main · abc1234`; it does not create or imply a package release.

With `dropdown`, `manifestUrl` selects the version list. A relative path
is fetched from the production origin (`siteConfig.url`) first, then
from the current origin; local development tries its own origin first.
An absolute HTTP(S) URL is fetched directly. Cross-origin manifests
need a CORS header. Without `dropdown`, the badge displays the supplied
version and does not fetch a manifest.

Use a separate manifest for each product. This site selects
`/dockit-versions.json` for Dockit's `0.2.14` badge; its historical
docs-build manifest `/versions.json` has a separate version sequence.
The Python tab shows a `main` revision without a dropdown. See
[Releases & versioning](/guides/releases.md#two-independent-version-axes).

## TabDef

```ts
interface TabDef {
  id: string        // stable key
  label: string     // strip label
  numeral: string   // editorial numeral ('I', 'II', …)
  landing: string   // click target
  urlPrefix: string // scoping prefix for the sidebar filter
  versionChips?: VersionChip[] // per-tab override of site.versionChips
}
```

### Per-tab version chips

`versionChips` on a tab **overrides** the global
[`site.versionChips`](#siteconfig) whenever that tab is active:

- **Omit** the field → the tab inherits the global chips (the default).
- **`[]`** → no chips render on that tab.
- **A non-empty array** → exactly those chips render, replacing the
  global list for that tab only.

The active tab is derived from the URL's first segment (same rule the
sidebar filter uses), so the chips switch as the reader moves between
tabs. A CLI-focused site can scope each surface to its own version:

```ts
tabs: [
  // Inherits site.versionChips
  { id: 'overview', label: 'Get Started', numeral: 'I',
    landing: '/', urlPrefix: '/get-started' },
  // cli-only on the CLI tab
  { id: 'cli', label: 'CLI', numeral: 'II',
    landing: '/cli', urlPrefix: '/cli',
    versionChips: [{ label: 'cli', version: '1.4.0' }] },
  // py-only on the Python tab
  { id: 'python', label: 'Python', numeral: 'III',
    landing: '/python', urlPrefix: '/python',
    versionChips: [{ label: 'py', version: '0.9.2' }] },
  // no chips on the design tab
  { id: 'python-design', label: 'Design', numeral: 'IV',
    landing: '/design', urlPrefix: '/design',
    versionChips: [] },
],
```

### External tab links

`TabEntry` is `TabDef | ExternalTabDef`. External entries share the strip
but link to another site, never become active, and scope no local pages:

```ts
interface ExternalTabDef {
  label: string
  href: string // absolute destination URL
  external: true
}
```

`internalTabs()` returns only page-scoping tabs; `isExternalTab(entry)`
is the exported type guard.

## SearchConfig

```ts
interface SearchConfig {
  mergeIndexes?: MergeIndexDef[]
}

interface MergeIndexDef {
  url: string   // remote Pagefind bundle directory
  label: string // source badge shown on remote results
}
```

Remote indexes require compatible Pagefind formats and CORS. A failed
remote index leaves local search available. See [Search](/guides/search.md)
for the complete indexing and palette workflow.

## PageMeta

The parsed frontmatter shape — field-by-field effects are documented
in [Authoring](/guides/authoring.md):

```ts
interface PageMeta {
  path: string
  title: string
  section: string
  order: number
  description?: string
  draft?: boolean
  hidden?: boolean
  noindex?: boolean
  tocLevel?: 2 | 3
  fullscreen?: boolean
}
```

The `dev: true` frontmatter shorthand folds into `hidden` + `noindex`
at parse time — `PageMeta` carries the resolved flags. Path matching
against `PageMeta.path` should go through `normalizePath` /
`findPage` (both exported), which tolerate a trailing slash.

## The init functions

`initDocs` is sugar over four setters, each also exported for
piecemeal setups. All mutate module-level singletons in place, so
components holding an imported reference always see current data.

```ts
function initDocs(opts: InitDocsOptions): void

function defineSiteConfig(cfg: Partial): void

function initNavigation(
  modules: Record<string, { frontmatter?: PageFrontmatter }>,
  sectionOrder?: string[],
): void

function initSearchIndex(rawSources: Record<string, string>): void

function initTabs(tabs: TabEntry[]): void
```

| Function | Populates | From |
| --- | --- | --- |
| `defineSiteConfig` | `siteConfig` | your `site` overrides, merged over defaults. |
| `initNavigation` | `pages`, `groupedPages`, `groupedVisiblePages` | the eager page glob's frontmatter. |
| `initSearchIndex` | `searchIndex`, `rawMarkdown` | the `?raw` glob (dev search fallback). |
| `initTabs` | `TABS` | your `TabEntry[]`. |

## Navigation rail widths

Both rails default to **280px**: the left sidebar appears at 768px and wider,
and the right-hand TOC at 1024px and wider. Override
`--doc-sidebar-width` and `--doc-toc-width` in your site stylesheet (after importing Dockit styles):

```css
:root {
  --doc-sidebar-width: 300px;
  --doc-toc-width: 240px;
}
```

Use `240px` for either rail to restore its previous width. The sidebar setting
also applies to fullscreen layouts; those layouts omit the TOC. Mobile navigation
is unchanged.

The sidebar uses a thin scrollbar with a stable gutter, preventing overflow changes
from shifting section headings. Section labels stay on one line (with an ellipsis
and full-label tooltip if needed); page titles can wrap normally.
