Skip to content
Cookbookv0.28.0
Color scheme
Contrast
Color scheme
Contrast

Configuration reference

Use defineDocsConfig() for type inference and immediate validation of unknown keys:

import { defineDocsConfig } from "@tenphi/cookbook/config";

export default defineDocsConfig({
  site: { title: "Example" },
  content: { sources: [{ file: "README.md", route: "/" }] },
});

Cookbook validates the configuration when it loads. Unknown top-level and section keys are errors.

Start with a complete configuration recipe for common tasks. This page documents the available options; working examples covers repository layouts and shared presets.

For a focused explanation, use content sources, theme basics, fonts and typography, component styles, or publishing metadata. The sections below are the complete configuration reference.

Project resolution and presets

cookbook() and CLI commands discover the same docs.config.ts. Set root to a directory relative to this file to change where content, local assets, and the lock live. --root chooses the CLI’s configuration search directory; --config chooses a config file. An inline integration config object skips discovery and resolves its root relative to the integration root.

Use mergeDocsConfig(preset, overrides) to compose shared configuration. Objects merge recursively and arrays replace. Each theme.fonts role replaces its previous definition, so switching a role from Google to local files takes one override. Tasty styles preserve default values when adding conditional overrides. Neither input is mutated. See working examples.

Redirects

Map old routes to collected document routes:

redirects: { "/old-guide": "/getting-started" }

Pages can also declare aliases: ["/old-guide"] in frontmatter. Page aliases are relative to the source mount. Cookbook rejects collisions, cycles, and missing targets, collapses chains, and lets Astro emit static redirects. Internal links using an alias are rewritten to the canonical route.

Site

site: {
  title: "Example Project",
  version: "1.2.3",
  description: "Documentation for Example Project",
  url: "https://docs.example.com",
  repository: "https://github.com/example/project",
  favicon: "./assets/brand.svg"
}

version is the documented package version shown beside the site title. A site with one locked npm package source infers its version automatically; set this field for local or multi-package documentation. Read it from the package manifest when possible so it remains current. url configures Astro’s canonical site origin and sitemap metadata. If Astro also declares site, the values must match. It also lets Cookbook put absolute page URLs in llms.txt and a sitemap pointer in the origin-root robots.txt. repository adds a source link to the header.

For multiple documentation versions, set site.versions and mount content at each version root. The header then shows a version selector. It links to the same route in another version when that page exists and falls back to the other version’s root when it does not:

site: {
  versions: [
    { label: "Latest", routeBase: "/" },
    { label: "v1", routeBase: "/v1" },
  ],
},
content: {
  sources: [
    { file: "README.md", route: "/" },
    { glob: "docs/**/*.md", base: "docs" },
    { file: "versions/v1/README.md", routeBase: "/v1", route: "/" },
    { glob: "versions/v1/docs/**/*.md", base: "versions/v1/docs", routeBase: "/v1" },
  ],
},

Each routeBase must have a home page (a localized home also qualifies), and version roots must be distinct. A single-version site can continue using site.version for a simple label.

site.logo accepts a local image path, false to hide the mark, or an object with src (one image) or light and dark (appearance variants). Optional alt, decorative, href, width, and height control accessible text, destination, and intrinsic sizing. It applies to the header and mobile drawer. See the shared logo guide. The logo and favicon are independent.

Site icons

Cookbook ships its book mark as the default favicon. Set site.favicon to a local SVG, PNG, JPEG, WebP, AVIF, or GIF when the documentation should use the project’s own artwork:

site: {
  title: "Example Project",
  favicon: {
    source: "./assets/brand.svg",
    background: "#315efb"
  }
}

Paths are resolved from the project root. The string shorthand sets only the source, as in favicon: "./assets/brand.svg". background controls the opaque canvas used for the Apple touch icon and maskable application icons; it defaults to Cookbook blue.

At development and build time, Cookbook creates a scalable favicon when the source is SVG, a 32×32 PNG fallback, a 180×180 Apple touch icon, 192×192 and 512×512 application icons, safe-zone maskable variants, and a web app manifest. It also emits modern rel="icon", rel="apple-touch-icon", and rel="manifest" links plus light and dark theme-color metadata. Generated files live under /_cookbook/icons/, respect Astro’s base, and do not modify the project’s public/ directory. The maskable variants render the source artwork at 80% of the canvas on the configured opaque background.

These outputs follow current browser guidance for multiple icon formats and sizes, manifest icon purposes, and the 180×180 Apple touch icon.

Add custom elements to every page’s <head> with the head array. This is the preferred configuration for analytics scripts and other remote resources:

head: [
  {
    tag: "script",
    attrs: {
      defer: true,
      src: "https://analytics.example.com/script.js",
      "data-website-id": "YOUR-WEBSITE-ID",
    },
  },
],

Each entry accepts a tag, optional string or boolean attrs, and optional string content. Cookbook renders these entries directly as HTML elements; they do not pass through Astro’s script or style processing. Use a Head component override when a local asset needs Astro processing.

Page metadata

Enable source-aware edit links and Git timestamps for the page footer:

editLink: {
  baseUrl: "https://github.com/example/project/edit/main/"
},
lastUpdated: true,

Cookbook joins editLink.baseUrl with each original repository-relative source path, not a generated content-collection path. Set editUrl: false or lastUpdated: false in a page’s frontmatter to opt that page out; a string editUrl or date-valued lastUpdated overrides the generated value. Package sources show a timestamp only when their materialized file has Git history or the page supplies one explicitly.

These settings use Cookbook’s original source paths when constructing links and timestamps.

Cookbook also accepts page presentation frontmatter:

tableOfContents:
  minHeadingLevel: 2
  maxHeadingLevel: 4
pagefind: true
banner:
  content: This API is experimental.
hero:
  image:
    file: ./assets/product.svg
sidebar:
  label: Quick start
  order: 1
  group: Guides
  badge: NEW

template: splash uses a wider layout without sidebars by default. Set tableOfContents in that page’s frontmatter to show its contents list.

Set sidebar: false to omit a page from generated sidebars. label, order, group, and badge affect both the default sidebar and autogenerated navigation directories. A badge is plain text and also appears when a navigation item links to the page, unless that item overrides it. Relative hero images are validated, content-hashed, and copied with the page’s other local assets. Unrecognized frontmatter is preserved as graph entry metadata, outside the renderer frontmatter. Use content.frontmatter: "reject" to reject unrelated fields. Other supported fields include aliases, template, hero, editUrl, lastUpdated, prev, next, head, draft, and slug.

Set pagefind: false to omit an individual published page from search while keeping its normal route and navigation. A draft is excluded from search regardless of pagefind.

Draft pages

Set draft: true to publish a page for review at its normal URL. Drafts remain available in development and production, and authored links to them are valid. They are excluded from automatic navigation, pagination, search, sitemaps, and llms.txt, and receive noindex metadata. Removing draft makes the page discoverable on the next build. A draft URL is public; this is not access control.

Languages

Configure Cookbook’s multilingual routing and language picker:

locales: {
  root: { label: "English", lang: "en" },
  fr: { label: "Français", lang: "fr" },
  ar: { label: "العربية", lang: "ar", dir: "rtl" }
},
defaultLocale: "root",

Locale keys other than root are URL prefixes. A source routed to /fr/guide is the French counterpart of /guide. Navigation and pagination include the current locale’s published routes. Locale prefixes are added once, after the site’s deployment base.

Cookbook does not create duplicate fallback pages. When the language picker cannot find a translation, it links to the equivalent page in defaultLocale, then an existing locale home or default-language home. The picker labels cross-language fallbacks. Locales with no usable target are omitted. Header home links use the same policy, so a missing /fr/ never creates a broken home link. Alternate-language metadata lists only existing published translations. Drafts remain directly accessible but are excluded from locale discovery.

Content

content: {
  sources: [
    { file: "README.md", route: "/" },
    { glob: "docs/**/*.{md,mdx}", base: "docs" }
  ],
  allowOutsideRoot: false,
  localizeRepositoryLinks: false,
  frontmatter: "preserve"
}

See Content sources for every declaration and its route rules. Enable localizeRepositoryLinks to turn absolute repository blob, tree, raw, or Bitbucket src links back into local Cookbook routes when a matching page was collected. Cookbook uses site.repository for local sources and each package manifest’s repository metadata for package sources.

Navigation can mix direct routes, nested groups, autogenerated directories, and external links. Groups are recursive and can be nested to any practical depth. Use the object form to add an optional primary tab row above the documentation shell:

navigation: {
  tabs: [
    {
      label: "Guides",
      link: "/",
      items: [
        "/",
        {
          label: "Build",
          items: [
            {
              label: "Frontend",
              items: [
                {
                  label: "Frameworks",
                  items: ["/react", "/vue"]
                }
              ]
            }
          ]
        }
      ]
    },
    {
      label: "API",
      link: "/reference",
      items: [
        {
          label: "Reference",
          autogenerate: { directory: "/reference" }
        }
      ]
    },
    { label: "Playground", link: "https://example.com/playground" }
  ],
  items: [
    "/",
    {
      label: "Start here",
      items: ["/getting-started", "/configuration"]
    },
    {
      label: "Reference",
      autogenerate: { directory: "/reference" }
    }
  ]
}

Give a group an optional link to use its header as a parent page:

{
  label: "Charts",
  link: "/charts",
  items: [
    {
      label: "Chart types",
      link: "/charts/types",
      autogenerate: { directory: "/charts/types" }
    }
  ]
}

Group links must name existing root-relative page routes, without the deployment base, a query, or a fragment. The parent page is omitted from its children if it also appears in the manual list or autogenerated directory. It remains in previous/next page navigation. A group with no remaining children becomes a regular page link.

Every navigation object, including manual and autogenerated groups, accepts a plain-text badge, such as { label: "Offline support", link: "/offline-support", badge: "NEW" }. Links inherit sidebar.badge from their page’s frontmatter; an explicit badge overrides it, and badge: "" hides it. Badges use brand-derived accent colors and support partial customization through theme.styles.Sidebar.Badge. See Sidebar badges for examples.

Top-level sections stay flat; their linked headings navigate to their pages. For nested groups, clicking an unselected header or its chevron selects the parent page and opens its children, keeping them open if already expanded. Clicking the selected parent toggles its children without reloading. Selecting and collapsing an open, unselected group therefore takes two clicks. Enter and Space work too, while modified clicks and opening a link in a new tab retain normal link behavior. Groups without a link only toggle. Only the selected page is highlighted; its ancestors keep their normal styling.

Tabs are omitted when tabs is not set. Give a tab items to replace the sidebar for that section. Those items also establish section membership, so a tab can own routes that do not share its URL prefix. A tab stays active for pages nested below its link; / matches only the home page. When multiple tabs could match, an exact tab link wins, followed by the longest link prefix and then sidebar membership. Tabs without items use the top-level items fallback. Every internal route named anywhere in navigation must exist.

Theme

theme: {
  brand: {
    from: "#2f5bff",
    contrast: { apca: [45, 60] }
  },
  palette: {
    surface: { tone: 98, saturation: 0.05 },
    text: {
      base: "surface",
      tone: 0,
      saturation: 0,
      contrast: { wcag: [7, 10] }
    },
    heading: {
      base: "surface",
      tone: [4, 0],
      saturation: 0,
      contrast: { wcag: [7, 10] }
    },
    "text-soft": {
      base: "surface",
      tone: [25, 10],
      saturation: 0.05,
      contrast: { wcag: [4.5, 7] }
    }
  },
  fonts: {
    body: "Inter",
    heading: { google: "Newsreader", weights: [400, 700] },
    code: {
      family: "Project Mono",
      files: [{ src: "/fonts/project-mono.woff2", weight: "100 900" }]
    }
  },
  tokens: {
    "$radius": "8px",
    "$card-radius": "16px",
    "$border-width": "1px",
    "$layout-width": "87.5rem",
    "$content-width": "58rem",
    "$sidebar-width": "17.5rem"
  },
  states: {},
  presets: {
    body: { boldFontWeight: 680 },
    heading: {
      fontWeight: 650,
      boldFontWeight: 740
    }
  },
  styles: {
    ThemeSelect: {
      Trigger: { border: "#border-strong" },
      Panel: { shadow: "0 1rem 3rem #shadow" }
    }
  },
  contrastLevel: "auto"
}

The default brand is okhsl(266 68% 48%), a blue with 68% saturation. Controls use an 8px radius and cards use 16px. Onest Variable is the default body and heading family; JetBrains Mono Variable is used for code. theme.fonts loads Google families by name or local files from public/. See Fonts and typography for both flows.

The default layout is capped at 87.5rem (1400px), with a 58rem reading column and a 17.5rem sidebar. brand.from supplies the color seed. theme.palette supplies semantic Glaze declarations that inherit its hue and saturation unless a role has its own from. Reading text uses absolute tones: body text starts at 0 and headings at 4. Glaze applies normal-mode tone boundaries and adapts the values for dark mode; base and contrast supply minimum contrast safeguards. palette.heading follows an explicit palette.text declaration unless configured separately.

Glaze resolves light, dark, and both high-contrast modes independently. Literal brand color shorthand and structured hue/saturation/tone declarations remain supported. An APCA floor below 45 requires unsafeContrast: true. See Theme and components for the color model and examples.

theme.styles is keyed by Cookbook UI surface name. A plain Tasty style object contains only the properties to override; Cookbook deep-merges it into that surface’s complete base object internally. Unknown built-in names and sub-elements are rejected. Register custom component names and their overrides in theme.customStyles. Structural Astro overrides under components.overrides remain available when styling alone is insufficient. theme.states registers additional Tasty state shorthands or replaces a built-in alias; see the responsive state reference for all nine defaults. contrastLevel is forwarded to Glaze’s palette resolution. The built-in documentation navigation surfaces are available as Sidebar, TableOfContents, MobileMenuToggle, and MobileNavigationTabs; see Component styles for their complete sub-element lists.

Markdown

markdown: {
  stripLeadingBadges: true,
  rawHtml: "sanitize",
}

stripLeadingBadges removes badge-only paragraphs at the start of a page and defaults to true. rawHtml accepts "allow", "sanitize", "strip", or "reject". The default sanitize policy preserves safe elements, including details, summary, tables, and inline emphasis, while removing executable markup, event handlers, and presentational attributes. Links and images in sanitized HTML pass through the content graph. strip removes HTML with a warning, allow preserves trusted HTML, and reject reports a graph error. Package Markdown in safe mode is always sanitized even when the global policy is "allow", and package MDX requires an explicit trust: "mdx" source declaration.

Configure renderer-level Markdown options such as custom remark or rehype plugins and Shiki languages through Astro’s top-level markdown configuration. Cookbook preserves those settings while adding its own build-time transforms.

GFM column alignment (:--, :-:, --:) becomes a data-align modifier on table cells. Restyle it with @own(align=right) and similar states in theme.styles.MarkdownTable.Cell.

Cookbook preserves custom metadata and supports custom schemas. See extending Cookbook for Astro Markdown plugins, native GitHub alerts, the content query API, and styling limits.

Cookbook renders fenced mermaid blocks as responsive, theme-aware SVG during the static build. Flowcharts, state, sequence, class, and entity-relationship diagrams are supported. Invalid or unsupported diagrams remain visible as source code.

```mermaid
flowchart TD
  accTitle: Proposal workflow
  accDescr: Evidence is inspected before proposals are decided and applied.
  inspect[Inspect evidence] --> seal[Seal proposals]
  seal --> decide[Decide eligible items]
  decide --> apply[Apply accepted items]
```
Proposal workflowEvidence is inspected before proposals are decided and applied. Inspect evidence Seal proposals Decide eligible items Apply accepted items
search: {
  enabled: true,
},

Search is generated locally with Pagefind during a static build. Disable it for hosts or fixtures that do not need an index. Readers can open the search dialog with Ctrl+K (⌘+K on macOS), type a query, and close it with Escape. Search loads its index when opened, so a development server shows a notice instead of results. Build and preview the site to test actual results. See Search and navigation for the reader experience.

Components

components: {
  overrides: {
    Header: "./src/components/Header.astro",
    Footer: "./src/components/Footer.astro"
  }
}

Component replacement is the advanced escape hatch. Prefer theme tokens and named styles for visual changes that do not need new structure.

Cookbook uses these Astro component slots. A relative override path resolves from the project root; a package specifier resolves from the app’s dependencies:

Area Override names
Page shell Head, PageFrame, Header, Footer, Sidebar, Hero, SiteTitle
Navigation and controls Search, LanguageSelect, ThemeSelect, SocialIcons, MobileMenuToggle, MobileMenuFooter, MobileTableOfContents, TableOfContents, Pagination
Content and metadata MarkdownContent, DraftContentNotice, EditLink, LastUpdated

An override owns its markup and interaction behavior. Page overrides can read the validated route and navigation data from Astro.locals.cookbookRoute; see Extending Cookbook. Use Custom components for a complete site title example.

Cookbook uses its built-in footer by default, including the “Generated with Cookbook” credit. Replace the complete footer with the Footer component path shown above, or remove it with:

components: {
  overrides: {
    Footer: false,
  },
},

Disabling the complete footer also removes its edit link, last-updated metadata, and previous/next-page navigation.

Build

build: {
  strict: true,
  ci: process.env.CI === "true",
  cacheDir: "",
  maxArtifactBytes: 25 * 1024 * 1024,
  maxUnpackedBytes: 100 * 1024 * 1024,
  maxFiles: 10_000,
  maxPathDepth: 24,
  maxAssetBytes: 20 * 1024 * 1024
}

strict makes missing internal links errors instead of warnings. ci makes missing heading fragments errors and defaults to true when CI=true. Cookbook derives its public base path from Astro’s top-level base setting so routes and assets cannot drift between two configurations. Page URLs likewise follow Astro’s trailingSlash and build.format; see Deployment. An empty cacheDir uses ~/.cache/cookbook; set an explicit path to relocate downloaded package artifacts. Package limits protect builds from unexpected registry artifacts; raise them deliberately for a reviewed package.

Header buttons

Configure the top bar with site.headerLinks. Links appear beside search on larger screens and in a More popover below 50rem. The desktop header supports primary buttons; More uses a compact, left-aligned list of uniform navigation links. Omit the array (or use []) to hide More.

export default defineDocsConfig({
  site: {
    headerLinks: [
      {
        label: "Changelog",
        link: "https://example.com/changelog",
        newTab: true,
      },
      { label: "Get started", link: "/getting-started", variant: "primary" },
    ],
  },
});

Each entry requires a non-empty label and a link (an HTTP(S) URL, root-relative route, or fragment). Cookbook prefixes root-relative links with the Astro base. variant is default or primary and applies on desktop; newTab defaults to false and adds safe new-window attributes when enabled. Customize the complete button and popover style tree through theme.styles.HeaderLinks.

theme.palette.header sets the translucent header’s color declaration. It defaults to theme.palette.surface; Glaze resolves it for light, dark, and both high contrast modes. Adjust blur and other header styling via theme.styles.HeaderFrame. theme.palette.overlay controls the fixed underlay color, defaulting to black at 50% opacity in every scheme. The mobile drawer uses the Glaze shadow token and a 120ms slide transition; customize these through theme.styles.Sidebar.

Translate Cookbook controls

Cookbook includes English and French interface messages. Other languages use English for missing messages. Override messages by language code; regional codes fall back to the base language and then English:

translations: {
  de: {
    appearance: "Darstellung",
    closeNavigation: "Navigation schließen",
    "navigation.Guide": "Anleitung",
  },
},

navigation.<configured label> translates named tabs, navigation groups, and explicitly configured sidebar badges. Automatically generated links use the title from the translated page. The supported keys below keep independent overrides, including similar labels: auto controls contrast, while themeSelect.auto controls color scheme; search.label labels the search control, while pagefind.search supplies the search input prompt. No migration or key renaming is needed.

Keys Controls or feedback
copyPage, viewMarkdown, pageCopied, pageCopyError Page Markdown actions and copy status.
draftTitle, draftNotice Draft-page notice.
searchError Search loading failure.
appearance, colorScheme, contrast, normalContrast, highContrast, auto Appearance and contrast controls.
closeNavigation, primary, sections, chooseSection Primary and mobile navigation.
headerLinks, more, closeMore, moreLinks Header links and overflow menu.
versions, documentationVersion, documentationVersions, version Version selection and package labels.
home, generatedWith, viewSource Home, footer credit and preview source.
copyCode, codeCopied, codeCopyError Code copy action and status.
copyHeading, linkCopied, linkCopyError Heading link action and status. Preserve {heading} in copyHeading.
notFound, notFoundDescription, fallback Missing-page and language fallback messages.
sidebarNav.accessibleLabel, menuButton.accessibleLabel Sidebar navigation and menu accessible names.
tableOfContents.onThisPage, languageSelect.accessibleLabel Contents heading and language selector.
themeSelect.light, themeSelect.dark, themeSelect.auto Color-scheme options.
search.label, search.ctrlKey, search.cancelLabel, search.devWarning Search control, shortcut, close action and development notice.
page.nextLink, page.previousLink, page.editLink, page.lastUpdated, page.skipToContent Pagination, edit/update labels and skip link.
pagefind.search, pagefind.clear_search, pagefind.zero_results, pagefind.many_results, pagefind.one_result Search input, clearing and result counts. Preserve [COUNT] in count messages.

Custom component message keys are also preserved. Missing custom keys passed to Astro.locals.t(key) return the key itself. Changing an override does not alter other message keys, even when their default text is the same.