{"$schema":"https://doc-kit.nodejs.org/schemas/api-doc/1.0.1.json","id":"generators-html","path":"/generators/html","type":"module","module":"generators-html","title":"Website","introducedIn":null,"sourceLink":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"The `html` generator builds your docs into a static website: one\nserver-rendered page per document, with search, a sidebar, light and dark\nthemes, and client-side navigation. This page is the reference;\n[Customizing the site](https://doc-kit.nodejs.org/customization) walks through\nchanging its look.\n\n**Target:** `html` · **Package:** [`@doc-kit/generator-react`](https://doc-kit.nodejs.org/packages/react) · **Reads:** [Page JSX](https://doc-kit.nodejs.org/generators/jsx-ast) · **Writes:** `<page>.html`, `all.html`, `404.html`, `assets/`","summary":"The `html` generator builds your docs into a static website: one server-rendered page per document, with search, a sidebar, light and dark themes, and client-side navigation. This page is the reference; Customizing the site walks through changing its look.","examples":[],"children":[{"kind":"section","id":"usage","name":"Usage","title":"Usage","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"```bash\nnpm install --save-dev @doc-kit/cli @doc-kit/generator-react\nnpx doc-kit generate -t html -t orama-db -i \"docs/**/*.md\" -o out\n```\n\n`orama-db` adds the [search index](https://doc-kit.nodejs.org/generators/orama-db)\nfor the search box. The pages load their scripts as modules, so serve `out/`\nover HTTP (`npx doc-kit preview`) rather than opening it from disk.\n[Publishing your docs](https://doc-kit.nodejs.org/publishing) covers hosting.\n\n| Path                      | Contents                                                                                                                    |\n| ------------------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| `<page>.html`             | One page per input document, at its path from the input glob's base: `docs/api/fs.md` becomes `api/fs.html`.                |\n| `all.html`                | Every module page's content on one page, while `generateAllPage` is on.                                                     |\n| `404.html`                | The not-found page, while the `jsx-ast` option `generateNotFoundPage` is on.                                                |\n| `assets/`                 | The client scripts, stylesheet and fonts. The Vite adapter names each after a hash of its content, so hosts can cache them. |\n| `<module>/<section>.html` | One page per section, when [`section-pages`](https://doc-kit.nodejs.org/generators/section-pages) runs in the same build.   |\n\nIt then copies the paths in `pathsToCopy` into the output (see\n[Shared options](#shared-options)).","summary":"`orama-db` adds the search index for the search box. The pages load their scripts as modules, so serve `out/` over HTTP (`npx doc-kit preview`) rather than opening it from disk. Publishing your docs covers hosting.","examples":[{"language":"bash","displayName":null,"code":"npm install --save-dev @doc-kit/cli @doc-kit/generator-react\nnpx doc-kit generate -t html -t orama-db -i \"docs/**/*.md\" -o out"}],"children":[]},{"kind":"section","id":"configuring","name":"Configuring","title":"Configuring","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Options go in the `html` section of the\n[configuration file](https://doc-kit.nodejs.org/configuration):\n\n```js displayName=\"doc-kit.config.mjs\"\nexport default {\n  global: {\n    project: 'Kittens',\n    repository: 'my-org/kittens',\n  },\n  html: {\n    title: '{project} docs',\n    editURL: 'https://github.com/{repository}/edit/{ref}/docs{path}.md',\n  },\n};\n```\n\n* `title` {string} Template for the site part of each page's `<title>`, which\n  follows the page's own title and a `|`. Placeholders take configuration\n  values, such as `{project}`; `{version}` is the version without a `v`\n  (`22.11.0`). Set it to `''` to title pages with their own title alone.\n  **Default:** `'{project} {version} Documentation'`.\n* `editURL` {string} Template for each page's \"Edit this page\" link. `{path}`\n  is the page's path without an extension (`/api/fs`; on section pages, their\n  module's), `{version}` the version with a `v` (`v22.11.0`), and other\n  placeholders, such as `{repository}` and `{ref}`, take configuration values.\n  **Default:** none, which leaves the link out.\n* `pageURL` {string} Template for a page's URL in another version, which the\n  [version selector](#version-selector) links to. `{version}` is each\n  release's label, such as `v22.x`, and `{path}` the page's path.\n  **Default:** `'{baseURL}{path}.html'`.\n* `useAbsoluteURLs` {boolean} Link pages, and load the client assets, through\n  absolute URLs under `baseURL` instead of relative ones. **Default:** `false`.\n* `showSearchBox` {boolean} Show the search box in the navigation bar. It\n  queries the `orama-db.json` that `orama-db` writes to the site root.\n  **Default:** `true` when `target` includes `orama-db`, `false` otherwise.\n* `navigation` {Object} The sidebar and the navigation bar. See\n  [Navigation](#navigation).\n  * `sidebar` {Object[]} Sidebar groups. **Default:** one \"API Documentation\"\n    group listing every page.\n  * `navbar` {Object[]} Navigation bar links. **Default:** none.\n  * `showCrossLinks` {boolean} End each page with links to the previous and\n    next sidebar items. **Default:** `false`.\n* `remoteConfigUrl` {string} URL of a JSON file the browser fetches for\n  announcement banners and an up-to-date version list. See\n  [Remote configuration](#remote-configuration). **Default:** none.\n* `head` {Object} Tags added to every page's `<head>`. See\n  [Head tags](#head-tags).\n  * `meta` {Object[]} `<meta>` tags, one attribute object each.\n    **Default:** `[]`.\n  * `links` {Object[]} `<link>` tags, one attribute object each.\n    **Default:** `[]`.\n  * `html` {string[]} Raw HTML, added as is. **Default:** `[]`.\n* `stylesheets` {string[]} CSS files bundled after the built-in stylesheet.\n  See [Stylesheets](#stylesheets). **Default:** `[]`.\n* `templatePath` {string} The HTML file every page is rendered into. See\n  [HTML template](#html-template). **Default:** the built-in `template.html`.\n* `imports` {Object} Module aliases, each mapped to a file or a package. The\n  `#theme/*` aliases replace parts of the page. See\n  [Theme slots](#theme-slots). **Default:** the six built-in `#theme/*`\n  components.\n* `components` {Object} Components that MDX pages can use, by tag name. See\n  [Components in MDX](#components-in-mdx). **Default:** `{}`.\n* `virtualImports` {Object} In-memory modules that components can import, from\n  module ID to source code. See [Theme config module](#theme-config-module).\n  **Default:** `{}`.\n* `generateAllPage` {boolean} Write `all.html`: every module page's content on\n  one page, in the default sidebar's order, without section pages and the\n  `index` page. **Default:** `true`.\n* `bundler` {Object} The adapter that bundles the components and the client\n  assets. See [Bundler adapters](#bundler-adapters). **Default:** the Vite\n  adapter.\n\nPaths in `stylesheets`, `templatePath` and `imports` resolve against the\nworking directory. Build them with `join(import.meta.dirname, ...)` to make\nthem work wherever doc-kit runs from.","summary":"Options go in the `html` section of the configuration file:","examples":[{"language":"js","displayName":"doc-kit.config.mjs","code":"export default {\n  global: {\n    project: 'Kittens',\n    repository: 'my-org/kittens',\n  },\n  html: {\n    title: '{project} docs',\n    editURL: 'https://github.com/{repository}/edit/{ref}/docs{path}.md',\n  },\n};"}],"children":[{"kind":"section","id":"shared-options","name":"Shared options","title":"Shared options","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"The generator also reads these\n[global options](https://doc-kit.nodejs.org/configuration#global-options). Set\nthem under `global` to share them with other generators, or under `html` for\nthe site alone.\n\n| Option        | What it does for the site                                                                                                                                                                                                               |\n| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `output`      | The directory the site is written to.                                                                                                                                                                                                   |\n| `project`     | The site's name: the default logo, `{project}` in titles, and the version selector's label.                                                                                                                                             |\n| `repository`  | Adds a GitHub link to the navigation bar, and fills `{repository}` in templates.                                                                                                                                                        |\n| `ref`         | Fills `{ref}` in templates, such as `editURL`.                                                                                                                                                                                          |\n| `baseURL`     | The site's public URL, for `useAbsoluteURLs` and `{baseURL}` in `pageURL`.                                                                                                                                                              |\n| `version`     | `{version}` in titles, and the version selector's current entry. It defaults to the version of Node.js running doc-kit, so set it or drop `{version}` from `title`.                                                                     |\n| `changelog`   | The releases the version selector lists. Without one, there's no selector unless the [remote configuration](#remote-configuration) lists `versions`.                                                                                    |\n| `minify`      | Minifies each page's HTML. The bundler minifies the client assets on its own (see [Vite adapter](#vite-adapter)).                                                                                                                       |\n| `pathsToCopy` | Files and directories copied into the output after the pages: a string keeps the path's name, an object maps sources to output paths, and missing paths are skipped. Setting it replaces the default, `['assets', 'public', 'static']`. |","summary":"The generator also reads these global options. Set them under `global` to share them with other generators, or under `html` for the site alone.","examples":[],"children":[]}]},{"kind":"section","id":"navigation","name":"Navigation","title":"Navigation","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`navigation` configures the sidebar and the navigation bar. Each key is\noptional, and leaving one out keeps that part's default:\n\n```js displayName=\"doc-kit.config.mjs\"\nexport default {\n  html: {\n    navigation: {\n      sidebar: [\n        {\n          groupName: 'Reference',\n          items: [\n            { label: '`kittens`', link: '/api/kittens' },\n            {\n              label: 'Plugins',\n              link: '/api/plugins',\n              items: [\n                { label: 'Plugins', link: '/api/plugins' },\n                { label: 'Writing a plugin', link: '/api/writing-plugins' },\n              ],\n            },\n          ],\n        },\n      ],\n      navbar: [\n        { text: 'Blog', link: 'https://example.com/blog', target: '_blank' },\n      ],\n      showCrossLinks: true,\n    },\n  },\n};\n```","summary":"`navigation` configures the sidebar and the navigation bar. Each key is optional, and leaving one out keeps that part's default:","examples":[{"language":"js","displayName":"doc-kit.config.mjs","code":"export default {\n  html: {\n    navigation: {\n      sidebar: [\n        {\n          groupName: 'Reference',\n          items: [\n            { label: '`kittens`', link: '/api/kittens' },\n            {\n              label: 'Plugins',\n              link: '/api/plugins',\n              items: [\n                { label: 'Plugins', link: '/api/plugins' },\n                { label: 'Writing a plugin', link: '/api/writing-plugins' },\n              ],\n            },\n          ],\n        },\n      ],\n      navbar: [\n        { text: 'Blog', link: 'https://example.com/blog', target: '_blank' },\n      ],\n      showCrossLinks: true,\n    },\n  },\n};"}],"children":[{"kind":"section","id":"sidebar","name":"Sidebar","title":"Sidebar","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`sidebar` is a list of groups, each `{ groupName, items }`. Each item has:\n\n* `label` {string} The item's text. Backticked spans render as code, as in\n  page headings.\n* `link` {string} A page's path without its extension, such as `/api/kittens`.\n  It resolves against the page being rendered, so it follows\n  `useAbsoluteURLs`, and it's highlighted on its own page. Links starting with\n  `http://` or `https://` are used as written.\n* `items` {Object[]} Nested items. An item with `items` renders as a\n  disclosure, open on the pages it lists. Its label opens and closes it and\n  isn't a link, so list the item's own page again as its first child, as\n  `Plugins` does above.\n\nWhen `section-pages` splits a module, the module's sections are nested under\nits item while you're on it, with the module repeated as the first child. An\nitem with `items` of its own keeps those instead.\n\nWithout a `sidebar`, the site has one \"API Documentation\" group listing every\npage, alphabetically by title, with pages named `index`, `synopsis` and\n`documentation` first.","summary":"`sidebar` is a list of groups, each `{ groupName, items }`. Each item has:","examples":[],"children":[]},{"kind":"section","id":"navigation-bar","name":"Navigation bar","title":"Navigation bar","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Each `navbar` item has:\n\n* `text` {string} The link's text.\n* `link` {string} The URL, used as written: usually a site-absolute path or\n  another site.\n* `target` {string} The link's target. `'_blank'` opens a new tab and adds an\n  external-link icon.\n* `accent` {boolean} Highlights the link.\n\nA site-absolute link shows as the current section on its page and on the pages\nunder it, so `/api` does on `/api/fs`. The bar also holds the logo, the search\nbox (see `showSearchBox`), the theme switch and, with a `repository`, a GitHub\nlink.","summary":"Each `navbar` item has:","examples":[],"children":[]},{"kind":"section","id":"previous-and-next-links","name":"Previous and next links","title":"Previous and next links","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"With `showCrossLinks` on, each page ends with links to the previous and next\nitems of the sidebar: its groups' top-level items, in order. It needs a\n`sidebar`. A module split into section pages always gets these links, on its\nown page and its sections', stepping through them in document order.","summary":"With `showCrossLinks` on, each page ends with links to the previous and next items of the sidebar: its groups' top-level items, in order. It needs a `sidebar`. A module split into section pages always gets these links, on its own page and its sections', stepping through them in document order.","examples":[],"children":[]},{"kind":"section","id":"version-selector","name":"Version selector","title":"Version selector","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"The sidebar shows a version selector when there are versions to list: the\nreleases in `changelog`, or the `versions` of the\n[remote configuration](#remote-configuration). Each entry links to the current\npage in that version through `pageURL`. A page lists only the versions from\nthe major in its `added` (or `introduced_in`) metadata onwards.","summary":"The sidebar shows a version selector when there are versions to list: the releases in `changelog`, or the `versions` of the remote configuration. Each entry links to the current page in that version through `pageURL`. A page lists only the versions from the major in its `added` (or `introduced_in`) metadata onwards.","examples":[],"children":[]}]},{"kind":"section","id":"remote-configuration","name":"Remote configuration","title":"Remote configuration","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`remoteConfigUrl` points at a JSON file the browser fetches once per visit, so\npublished docs can show news and list newer releases without a rebuild. Both\nkeys are optional:\n\n```json displayName=\"site.json\"\n{\n  \"websiteBanners\": {\n    \"index\": {\n      \"text\": \"Kittens 3 is out!\",\n      \"link\": \"https://example.com/blog/kittens-3\",\n      \"startDate\": \"2026-11-01\",\n      \"endDate\": \"2026-12-01\"\n    },\n    \"v2\": {\n      \"text\": \"Kittens 2 reaches its end of life in December.\",\n      \"type\": \"warning\"\n    }\n  },\n  \"versions\": [\n    {\n      \"url\": \"https://example.com/docs/v3.x{path}.html\",\n      \"label\": \"v3.x\",\n      \"major\": 3\n    },\n    {\n      \"url\": \"https://example.com/docs/v2.x{path}.html\",\n      \"label\": \"v2.x\",\n      \"major\": 2\n    }\n  ]\n}\n```\n\n* `websiteBanners` {Object} Announcement banners, by section: `index` shows on\n  every version's docs, and `v<major>`, such as `v2`, only on that major's.\n  When both are active, `index` wins.\n  * `text` {string} The banner's text.\n  * `link` {string} Where the banner links to, opened in a new tab.\n  * `type` {string} `'default'`, `'warning'` or `'error'`.\n  * `startDate` {string} The date the banner starts showing.\n  * `endDate` {string} The date the banner stops showing.\n* `versions` {Object[]} Entries that replace the version selector's, so docs\n  built for an older release still list the ones that came after it. Each is\n  `{ url, label, major }`, with `{path}` in `url` standing for the page's\n  path.\n\nA banner the reader dismisses stays hidden until its text changes. If the file\nis on another origin, its host must allow cross-origin requests.","summary":"`remoteConfigUrl` points at a JSON file the browser fetches once per visit, so published docs can show news and list newer releases without a rebuild. Both keys are optional:","examples":[{"language":"json","displayName":"site.json","code":"{\n  \"websiteBanners\": {\n    \"index\": {\n      \"text\": \"Kittens 3 is out!\",\n      \"link\": \"https://example.com/blog/kittens-3\",\n      \"startDate\": \"2026-11-01\",\n      \"endDate\": \"2026-12-01\"\n    },\n    \"v2\": {\n      \"text\": \"Kittens 2 reaches its end of life in December.\",\n      \"type\": \"warning\"\n    }\n  },\n  \"versions\": [\n    {\n      \"url\": \"https://example.com/docs/v3.x{path}.html\",\n      \"label\": \"v3.x\",\n      \"major\": 3\n    },\n    {\n      \"url\": \"https://example.com/docs/v2.x{path}.html\",\n      \"label\": \"v2.x\",\n      \"major\": 2\n    }\n  ]\n}"}],"children":[]},{"kind":"section","id":"head-tags","name":"Head tags","title":"Head tags","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`meta` and `links` take one attribute object per tag, so you can repeat a tag\nand pick `name` or `property` per tag. A `true` value renders a valueless\nattribute, such as `crossorigin`; `false`, `null` and `undefined` leave the\nattribute out. `html` holds raw markup for anything else.\n\n```js displayName=\"doc-kit.config.mjs\"\nexport default {\n  html: {\n    head: {\n      meta: [{ property: 'og:image', content: 'https://example.com/og.png' }],\n      links: [\n        { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },\n        {\n          rel: 'preconnect',\n          href: 'https://fonts.example.com',\n          crossorigin: true,\n        },\n      ],\n      html: ['<meta name=\"theme-color\" content=\"#000000\" />'],\n    },\n  },\n};\n```\n\nThe built-in template writes the `<title>`, `og:title` and `og:type` itself,\nand the stylesheet bundles its own fonts, so none of them need an entry.","summary":"`meta` and `links` take one attribute object per tag, so you can repeat a tag and pick `name` or `property` per tag. A `true` value renders a valueless attribute, such as `crossorigin`; `false`, `null` and `undefined` leave the attribute out. `html` holds raw markup for anything else.","examples":[{"language":"js","displayName":"doc-kit.config.mjs","code":"export default {\n  html: {\n    head: {\n      meta: [{ property: 'og:image', content: 'https://example.com/og.png' }],\n      links: [\n        { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },\n        {\n          rel: 'preconnect',\n          href: 'https://fonts.example.com',\n          crossorigin: true,\n        },\n      ],\n      html: ['<meta name=\"theme-color\" content=\"#000000\" />'],\n    },\n  },\n};"}],"children":[]},{"kind":"section","id":"stylesheets","name":"Stylesheets","title":"Stylesheets","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`stylesheets` are bundled into the site's stylesheet after the built-in one, so\ntheir rules and custom properties win. They go through the bundler like any\nother CSS, so `@import` and `url()` work. The built-in stylesheet reads these\ncustom properties:\n\n| Property                                   | Sets                                                                                                     | Default        |\n| ------------------------------------------ | -------------------------------------------------------------------------------------------------------- | -------------- |\n| `--color-brand-100` to `--color-brand-900` | The accent color, lightest to darkest, for links, focus rings and active states, in light and dark mode. | A neutral grey |\n| `--font-open-sans`                         | The text's font stack.                                                                                   | Open Sans      |\n| `--font-ibm-plex-mono`                     | The code's font stack.                                                                                   | IBM Plex Mono  |\n\nDark mode sets `data-theme=\"dark\"` on `<html>`, from the reader's choice in the\ntheme switch or their system setting, so scope dark-only rules to\n`[data-theme='dark']`. [Colors and fonts](https://doc-kit.nodejs.org/customization#colors-and-fonts)\nhas a worked example.","summary":"`stylesheets` are bundled into the site's stylesheet after the built-in one, so their rules and custom properties win. They go through the bundler like any other CSS, so `@import` and `url()` work. The built-in stylesheet reads these custom properties:","examples":[],"children":[]},{"kind":"section","id":"html-template","name":"HTML template","title":"HTML template","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`templatePath` points at the HTML file every page is rendered into. It's\nevaluated as a JavaScript template literal, so a `${...}` placeholder takes any\nexpression, and a literal backtick or `${` must be escaped with a backslash.\nStart from a copy of the built-in template, at\n`node_modules/@doc-kit/generator-react/src/html/template.html`.\n\nThese variables are in scope:\n\n* `title` {string} The page's full title, HTML-escaped, such as\n  `'File system | Node.js 22.11.0 Documentation'`.\n* `head` {string} The tags from the `head` option.\n* `assets` {string} The tags that load the client scripts and stylesheet, and\n  the data client-side navigation reads. Put it in `<head>`, or pages load no\n  script and no stylesheet.\n* `preloads` {string} Preload hints for the fonts the bundler reported.\n* `themeScript` {string} A script that applies the saved light or dark theme\n  before the first paint, for an inline `<script>` in `<head>`.\n* `speculationRules` {string} Speculation rules, as JSON, that prefetch\n  same-origin links leaving the site when they're hovered or pressed, for a\n  `<script type=\"speculationrules\">`. Links within the site navigate\n  client-side instead.\n* `dehydrated` {string} The server-rendered page, for the `<body>`.\n* `root` {string} The site root, relative to the page (such as `'../'`), or\n  absolute with `useAbsoluteURLs`. On `404.html` and `all.html`, which can be\n  served from any path, it's `'/'` unless `useAbsoluteURLs` is on. Prefix your\n  own files with it, as in `${root}favicon.svg`.\n* `metadata` {Object} The page's metadata, frontmatter included.\n* `config` {Object} The resolved `html` configuration.\n\nFor example, to add a canonical link and give the home page a title of its own:\n\n```html displayName=\"template.html\"\n<title>${metadata.api === 'index' ? 'Kittens' : title}</title>\n<link rel=\"canonical\" href=\"https://example.com/docs${metadata.path}.html\" />\n```\n\nThe filled-in template is the page as written, minified first when `minify` is\non.","summary":"`templatePath` points at the HTML file every page is rendered into. It's evaluated as a JavaScript template literal, so a `${...}` placeholder takes any expression, and a literal backtick or `${` must be escaped with a backslash. Start from a copy of the built-in template, at `node_modules/@doc-kit/generator-react/src/html/template.html`.","examples":[{"language":"html","displayName":"template.html","code":"<title>${metadata.api === 'index' ? 'Kittens' : title}</title>\n<link rel=\"canonical\" href=\"https://example.com/docs${metadata.path}.html\" />"}],"children":[]},{"kind":"section","id":"theme-slots","name":"Theme slots","title":"Theme slots","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Every page is put together from six components, each imported through a\n`#theme/*` alias. Point an alias in `imports` at a component of your own to\nreplace that part; the aliases you leave out keep their defaults.\n\n| Alias               | Default                                                                   | Props                                             |\n| ------------------- | ------------------------------------------------------------------------- | ------------------------------------------------- |\n| `#theme/Layout`     | `Layout`: the whole page, around the content                              | `metadata`, `headings`, `readingTime`, `children` |\n| `#theme/Navigation` | `NavBar`: the logo, links, search box, theme switch and GitHub link       | `metadata`                                        |\n| `#theme/Logo`       | `ProjectName`: `project` as plain text                                    | none                                              |\n| `#theme/Sidebar`    | `SideBar`: the page links and the version selector                        | `metadata`                                        |\n| `#theme/Metabar`    | `MetaBar`: the table of contents, reading time, and \"Edit this page\" link | `metadata`, `headings`, `readingTime`             |\n| `#theme/Footer`     | `NoOp`: nothing                                                           | none                                              |\n\n```js displayName=\"doc-kit.config.mjs\"\nimport { join } from 'node:path';\n\nexport default {\n  html: {\n    imports: {\n      '#theme/Footer': join(import.meta.dirname, 'theme/Footer.jsx'),\n    },\n  },\n};\n```\n\n* Components are [Preact](https://preactjs.com/) components written in JSX.\n  Imports of `react` resolve to `preact/compat`, and components can import CSS\n  files and CSS modules.\n* An alias can point at a package, such as\n  `'@node-core/ui-components/Common/NodejsLogo'`. With the Vite adapter, a\n  relative path resolves against Vite's `root`, the working directory by\n  default.\n* The default layout renders the navigation bar, sidebar, metabar and footer\n  slots, and the default navigation bar renders `#theme/Logo`. A component of\n  your own renders only the slots it imports, so a custom `#theme/Navigation`\n  that leaves out `#theme/Logo` has no logo.\n* To wrap or reuse a built-in component, import it from\n  `@doc-kit/generator-react/html/ui/components/`, such as `Layout/index.jsx`.","summary":"Every page is put together from six components, each imported through a `#theme/*` alias. Point an alias in `imports` at a component of your own to replace that part; the aliases you leave out keep their defaults.","examples":[{"language":"js","displayName":"doc-kit.config.mjs","code":"import { join } from 'node:path';\n\nexport default {\n  html: {\n    imports: {\n      '#theme/Footer': join(import.meta.dirname, 'theme/Footer.jsx'),\n    },\n  },\n};"}],"children":[{"kind":"section","id":"layout-props","name":"Layout props","title":"Layout props","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"`#theme/Layout` receives:\n\n* `metadata` {Object} The page's metadata: `api`, `path`, `basename`, its\n  frontmatter fields (such as `added`, or fields of your own), and, on section\n  pages, `chunk`, describing the module and section it was split from. On\n  `404.html`, `api` is `'404'`.\n* `headings` {Object[]} The table of contents, as\n  `{ depth, value, slug, stability, data }` entries.\n* `readingTime` {string | undefined} The estimated reading time, such as\n  `'5 min read'`. Only set while the `jsx-ast` option `showReadingTime` is on,\n  and never on `all.html`.\n* `children` {ComponentChildren} The page's rendered content.\n\n[Page layouts](https://doc-kit.nodejs.org/customization#page-layouts) shows a\nlayout that gives some pages a shape of their own.","summary":"`#theme/Layout` receives:","examples":[],"children":[]}]},{"kind":"section","id":"components-in-mdx","name":"Components in MDX","title":"Components in MDX","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Pages written in MDX (`.mdx` files, or any page with `mdx: true` in its\nfrontmatter) can use JSX components. `components` registers yours by tag name,\nnext to the built-in ones. Each entry is either a module specifier, for the\nmodule's default export, or `{ name, source, isDefaultExport }`, where\n`isDefaultExport: false` imports the export called `name`. The source can be\nan alias defined in `imports`, a package, or an absolute path:\n\n```js displayName=\"doc-kit.config.mjs\"\nimport { join } from 'node:path';\n\nexport default {\n  html: {\n    components: {\n      Hero: '#theme/Hero',\n      Stats: { name: 'Stats', source: '#theme/stats', isDefaultExport: false },\n    },\n    imports: {\n      '#theme/Hero': join(import.meta.dirname, 'theme/Hero.jsx'),\n      '#theme/stats': join(import.meta.dirname, 'theme/stats.jsx'),\n    },\n  },\n};\n```\n\nA page then uses them as tags, such as `<Hero title=\"Kittens\" />`. Each page\nimports only the components it uses.\n\nThese are built in and need no entry: `AlertBox`, `Badge`, `Blockquote`,\n`ChangeHistory`, `CodeBox`, `CodeTabs`, `DataTag`, `FunctionSignature`, the\n`MDXTooltip` family, the `ArrowUpRightIcon` icon, and `DocumentationIndex`,\nwhich lists every page with its stability and description.","summary":"Pages written in MDX (`.mdx` files, or any page with `mdx: true` in its frontmatter) can use JSX components. `components` registers yours by tag name, next to the built-in ones. Each entry is either a module specifier, for the module's default export, or `{ name, source, isDefaultExport }`, where `isDefaultExport: false` imports the export called `name`. The source can be an alias defined in `imports`, a package, or an absolute path:","examples":[{"language":"js","displayName":"doc-kit.config.mjs","code":"import { join } from 'node:path';\n\nexport default {\n  html: {\n    components: {\n      Hero: '#theme/Hero',\n      Stats: { name: 'Stats', source: '#theme/stats', isDefaultExport: false },\n    },\n    imports: {\n      '#theme/Hero': join(import.meta.dirname, 'theme/Hero.jsx'),\n      '#theme/stats': join(import.meta.dirname, 'theme/stats.jsx'),\n    },\n  },\n};"}],"children":[{"kind":"section","id":"interactive-components","name":"Interactive components","title":"Interactive components","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Components render to static HTML at build time. To make one interactive, wrap\nits export with `withIsland`, which hydrates it in the browser as an island:\n\n```jsx displayName=\"theme/Counter.jsx\"\nimport withIsland from '@doc-kit/generator-react/html/ui/islands/withIsland.jsx';\nimport { useState } from 'react';\n\nconst Counter = ({ start = 0 }) => {\n  const [count, setCount] = useState(start);\n\n  return <button onClick={() => setCount(count + 1)}>{count}</button>;\n};\n\nexport default withIsland(Counter, { name: 'Counter', on: { idle: true } });\n```\n\n`withIsland(Component, options)` takes:\n\n* `name` {string} The component's tag name in `components`. The browser loads\n  islands by that name, so every component that hydrates needs a `components`\n  entry, even a theme slot that no page uses as a tag.\n* `on` {Object} When the island hydrates: the\n  [is-land](https://is-land.11ty.dev/) conditions without their `on:` prefix.\n  Required.\n\nA condition set to `true` takes no value:\n\n| Condition                     | Hydrates the island                                                               |\n| ----------------------------- | --------------------------------------------------------------------------------- |\n| `idle: true`                  | Once the page has loaded and the browser is idle                                  |\n| `load: true`                  | Once the page has loaded                                                          |\n| `visible: true`               | Once it scrolls into view                                                         |\n| `interaction: true`           | On the first click or touch inside it (a string names other events)               |\n| `media: '(min-width: 768px)'` | Once the media query matches                                                      |\n| `'save-data': 'false'`        | Only if the browser doesn't ask to save data (or, set to `true`, only if it does) |\n\nAn island with several conditions waits for all of them, and `on: {}`\nhydrates it right away.\n\nProps are written into the page as JSON, so pass data, not functions. Children\nare rendered on the server and kept as they are. [Interactive components](https://doc-kit.nodejs.org/customization#interactive-components)\nin the guide walks through building one.","summary":"Components render to static HTML at build time. To make one interactive, wrap its export with `withIsland`, which hydrates it in the browser as an island:","examples":[{"language":"jsx","displayName":"theme/Counter.jsx","code":"import withIsland from '@doc-kit/generator-react/html/ui/islands/withIsland.jsx';\nimport { useState } from 'react';\n\nconst Counter = ({ start = 0 }) => {\n  const [count, setCount] = useState(start);\n\n  return <button onClick={() => setCount(count + 1)}>{count}</button>;\n};\n\nexport default withIsland(Counter, { name: 'Counter', on: { idle: true } });"}],"children":[]}]},{"kind":"section","id":"theme-config-module","name":"Theme config module","title":"Theme config module","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Components read build-time values from `#theme/config`, a module the generator\nwrites for each build. Import only what you need, and the rest is left out of\nthe bundle:\n\n```js\nimport { project, version, pages } from '#theme/config';\n```\n\n| Export                   | Value                                                                                                                                         |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |\n| `version`                | The documented version as a semver object: `version.version` is `'22.11.0'`, `version.major` is `22`.                                         |\n| `versions`               | The version selector's entries, `{ url, label, major }`, with `{path}` left in `url`.                                                         |\n| `editURL`                | `editURL` with everything but `{path}` filled in, or `undefined`.                                                                             |\n| `pages`                  | `[name, path]` pairs for every module page, in the default sidebar's order.                                                                   |\n| `documentationIndex`     | What `<DocumentationIndex />` lists: `{ api, name, index, description }` for every module page but `index`. `index` is the stability, if any. |\n| `chunks`                 | Each split module's section pages, by the module's path: `{ label, items }`, nested by heading depth. Empty without section pages.            |\n| `languageDisplayNameMap` | A `Map` from arrays of a language's names and aliases to its display name, for code block labels.                                             |\n| `server`                 | `true` in the server build, `false` in the browser.                                                                                           |\n\nEvery other option of the `html` section is exported under its own name too,\nincluding the global options it inherits, such as `project`, `repository`,\n`baseURL`, `useAbsoluteURLs`, `navigation`, `showSearchBox` and\n`remoteConfigUrl`. The exceptions are `changelog`, `index`, `imports`,\n`virtualImports`, `stylesheets`, `components`, `head` and `bundler`.\n`repository`, `baseURL` and `remoteConfigUrl` are `undefined` when unset.\n\nFor values of your own, `virtualImports` adds modules next to it. Each key is a\nmodule ID, and each value the module's source:\n\n```js displayName=\"doc-kit.config.mjs\"\nexport default {\n  html: {\n    virtualImports: {\n      '#theme/site': `export const chat = 'https://chat.example.com';`,\n    },\n  },\n};\n```\n\nA component then imports it like any other module:\n`import { chat } from '#theme/site'`.","summary":"Components read build-time values from `#theme/config`, a module the generator writes for each build. Import only what you need, and the rest is left out of the bundle:","examples":[{"language":"js","displayName":null,"code":"import { project, version, pages } from '#theme/config';"},{"language":"js","displayName":"doc-kit.config.mjs","code":"export default {\n  html: {\n    virtualImports: {\n      '#theme/site': `export const chat = 'https://chat.example.com';`,\n    },\n  },\n};"}],"children":[]},{"kind":"section","id":"client-side-navigation","name":"Client-side navigation","title":"Client-side navigation","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"The site moves between its own pages client-side, through the\n[Navigation API](https://developer.mozilla.org/docs/Web/API/Navigation_API):\nfollowing a link fetches the next page, prefetched when the link is hovered or\npressed, and swaps it into the current document. Scripts, stylesheets, fonts,\nthe search index and the remote configuration stay loaded, and back and\nforward, scrolling and focus behave as they do for full loads. Links outside\nthe site, files that aren't pages, pages that load other scripts or\nstylesheets (such as a newer deployment's), and browsers without the\nNavigation API get regular navigations.\n\nWhen you customize the site, keep in mind that:\n\n* The whole `<body>` is replaced, and scripts in it don't run. Of the\n  `<head>`, only the `<title>` and the page-specific tags follow the page:\n  `<meta>` tags, and `<link>` tags other than stylesheets and preloads (such as\n  `canonical`).\n* Islands are unmounted and hydrated again on every page. Anything shared\n  across pages belongs in module scope, which lasts for the whole visit. An\n  island that scrolls keeps its position when the next page renders it too.\n* Once the document has navigated client-side, `<html>` has a `data-navigated`\n  attribute, for styles that only belong on the first page, such as entry\n  animations.\n* Analytics can count client-side page views through the Navigation API's\n  `navigatesuccess` event.","summary":"The site moves between its own pages client-side, through the Navigation API: following a link fetches the next page, prefetched when the link is hovered or pressed, and swaps it into the current document. Scripts, stylesheets, fonts, the search index and the remote configuration stay loaded, and back and forward, scrolling and focus behave as they do for full loads. Links outside the site, files that aren't pages, pages that load other scripts or stylesheets (such as a newer deployment's), and browsers without the Navigation API get regular navigations.","examples":[],"children":[]},{"kind":"section","id":"bundler-adapters","name":"Bundler adapters","title":"Bundler adapters","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"The `bundler` option takes a small adapter, so the site can be built with Vite\n(the default) or any other bundler.\n\n> \\[!WARNING]\n> Worker threads receive a copy of the configuration, and functions can't be\n> copied. A configuration that sets `bundler`, the Vite adapter included, must\n> build with `threads: 1` (or `--threads 1`). With more threads, the build\n> fails with a `DataCloneError`.","summary":"The `bundler` option takes a small adapter, so the site can be built with Vite (the default) or any other bundler.","examples":[],"children":[{"kind":"section","id":"writing-an-adapter","name":"Writing an adapter","title":"Writing an adapter","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"An adapter is an object with three methods. The generator calls `buildServer`\nand `buildClient` once each and `compile` once per page, then renders the pages\nfrom the compiled modules across the worker threads.\n\n* `buildServer(options)` {Function} Bundles the component library for Node.js\n  and returns a promise of the `file:` URL of the built module.\n  * `entry` {string} The library's source: re-exports of every component a\n    page may render, Preact's `h` and `Fragment`, and `renderToStringAsync`.\n  * `virtualImports` {Object} In-memory modules to make available, by module\n    ID, `#theme/config` included.\n  * `outDir` {string} A temporary directory to write the library to, which the\n    generator removes afterwards.\n  * `config` {Object} The resolved `html` configuration.\n* `compile(code, fileName)` {Function} Turns one page module, written in JSX,\n  into plain JavaScript Node.js can import, and returns a promise of it.\n  Compile JSX with the classic runtime, to calls of the `_jsx` and `_Fragment`\n  bindings the module imports from the library, so the page and the library\n  share one Preact.\n* `buildClient(options)` {Function} Bundles the client entry into\n  `config.output` and returns a promise of the assets every page loads:\n  `{ scripts, preloads, stylesheets, fonts }`, as paths relative to the output\n  directory. `preloads` are the chunks the scripts import statically, rendered\n  as `modulepreload` hints, and `fonts` (optional) are fonts to preload.\n  * `entry` {string} The client program every page shares.\n  * `virtualImports` {Object} In-memory modules to make available, by module\n    ID, `#theme/config` included.\n  * `config` {Object} The resolved `html` configuration.\n\nBoth builds must compile the components' JSX for Preact, handle CSS imports and\nCSS modules, resolve the `config.imports` aliases, and serve the\n`virtualImports`. In the server build, `#theme/config` exports `server` as\n`true`; in the client build, `false`. Also:\n\n* Bundle the server library into one self-contained module, with no external\n  dependencies: the page modules import it from a temporary directory.\n* Put the client's CSS in stylesheets that every page loads up front, rather\n  than splitting it per chunk. Islands hydrate after the page is on screen, so\n  CSS that arrives with their chunks would style it late.\n* Name client files after a hash of their content, so hosts can cache them\n  indefinitely.\n\nAn adapter can live in your project, without changes to doc-kit. Pass it as\n`bundler`, with `threads: 1`:\n\n```js displayName=\"webpack-bundler.mjs\"\nexport const createWebpackBundler = webpackOptions => ({\n  async buildServer({ entry, virtualImports, outDir, config }) {\n    // Run webpack's Node.js target on `entry`, serving `virtualImports` and\n    // the `config.imports` aliases, write one self-contained module under\n    // `outDir`, and return its `file:` URL.\n  },\n\n  async compile(code, fileName) {\n    // Transform the JSX (classic runtime, pragma `_jsx`, fragment pragma\n    // `_Fragment`) and return the module's source.\n  },\n\n  async buildClient({ entry, virtualImports, config }) {\n    // Run webpack's browser target into `config.output` and return\n    // { scripts, preloads, stylesheets } as output-relative paths.\n  },\n});\n```","summary":"An adapter is an object with three methods. The generator calls `buildServer` and `buildClient` once each and `compile` once per page, then renders the pages from the compiled modules across the worker threads.","examples":[{"language":"js","displayName":"webpack-bundler.mjs","code":"export const createWebpackBundler = webpackOptions => ({\n  async buildServer({ entry, virtualImports, outDir, config }) {\n    // Run webpack's Node.js target on `entry`, serving `virtualImports` and\n    // the `config.imports` aliases, write one self-contained module under\n    // `outDir`, and return its `file:` URL.\n  },\n\n  async compile(code, fileName) {\n    // Transform the JSX (classic runtime, pragma `_jsx`, fragment pragma\n    // `_Fragment`) and return the module's source.\n  },\n\n  async buildClient({ entry, virtualImports, config }) {\n    // Run webpack's browser target into `config.output` and return\n    // { scripts, preloads, stylesheets } as output-relative paths.\n  },\n});"}],"children":[]},{"kind":"section","id":"vite-adapter","name":"Vite adapter","title":"Vite adapter","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Without a `bundler`, the generator builds with Vite. To configure Vite, create\nthe adapter yourself with `createViteBundler`, passing it a Vite configuration:\n\n```js displayName=\"doc-kit.config.mjs\"\nimport { createViteBundler } from '@doc-kit/generator-react/html/bundlers/vite';\n\nimport myVitePlugin from './my-vite-plugin.mjs';\n\nexport default {\n  threads: 1,\n\n  html: {\n    bundler: createViteBundler({\n      plugins: [myVitePlugin()],\n      define: {\n        'process.env.ANALYTICS_ID': JSON.stringify('UA-XXXXX'),\n      },\n    }),\n  },\n};\n```\n\nYour configuration is merged in first, then the generator sets what\ncoordinates its builds:\n\n* **Replaced:** `configFile`, `base`, `appType`, `publicDir`, the build's\n  inputs, output directory, output format and asset file names, `build.write`,\n  `build.emptyOutDir`, `build.copyPublicDir`, `build.watch`, `build.lib`, CSS\n  code splitting (off in the client build), the server-side rendering\n  settings, the JSX settings (Preact's automatic runtime), and\n  `css.transformer`, which is always Lightning CSS (its own options, under\n  `css.lightningcss`, are yours). The server build, which never ships, also\n  has `build.manifest` and `build.minify` off.\n* **Merged:** `resolve`, with the aliases and conditions the generator adds.\n  Your `plugins` run after the generator's virtual-module plugin, so they can\n  transform every module of both builds.\n* **Yours, with these defaults:**\n  * `root` {string} The directory relative `imports` paths resolve against.\n    **Default:** the working directory.\n  * `logLevel` {string} **Default:** `'warn'`.\n  * `clearScreen` {boolean} **Default:** `false`.\n  * `build.minify` {boolean | string} Minifies the client assets. The `minify`\n    option only covers the pages' HTML. **Default:** `true`.\n  * `build.manifest` {boolean | string} Keeps Vite's manifest in the output, at\n    `.vite/manifest.json` or the path given. The adapter writes one either way\n    to find the assets' names, and removes it unless you asked for it.\n    **Default:** `false`.\n\nVite builds the client from a module entry, not from the HTML pages, so\nplugins never see the pages and `transformIndexHtml` hooks don't run. Change\nthe pages through the [HTML template](#html-template) instead.","summary":"Without a `bundler`, the generator builds with Vite. To configure Vite, create the adapter yourself with `createViteBundler`, passing it a Vite configuration:","examples":[{"language":"js","displayName":"doc-kit.config.mjs","code":"import { createViteBundler } from '@doc-kit/generator-react/html/bundlers/vite';\n\nimport myVitePlugin from './my-vite-plugin.mjs';\n\nexport default {\n  threads: 1,\n\n  html: {\n    bundler: createViteBundler({\n      plugins: [myVitePlugin()],\n      define: {\n        'process.env.ANALYTICS_ID': JSON.stringify('UA-XXXXX'),\n      },\n    }),\n  },\n};"}],"children":[]}]}]}