Documentation Build

How are these docs implemented?

Docs-Only Build

Our custom npm run docs script starts Next with FLAG_DOCSONLY=true so the root / redirects into /docs and we don't render the guestbook application.

Defined as a package.json script, npm run docs calls scripts/docs-dev.mjs, which starts the same next dev as npm run dev, then sets FLAG_DOCSONLY=true on that process:

  • src/lib/flags.ts reads that environment variable as flags.docsOnly.
  • next.config.ts also lists FLAG_DOCSONLY under .env.local so Next can send it to the browser.

Basics & Custom Theme

Documentation has its own custom theme, separate from the guestbook defined in src/app/docs/docs.css. The custom theme works together with:

next.config.ts registers remark-gfm (GitHub-flavoured markdown) and my custom Remark (Markdown parsing and serialization) plugin, which allows:

  • Defining subtitle and flag visibility within a YAML frontmatter block
  • Using ::: fences for formatting content
  • Using shortcodes: {.heart} () / {.lock} () / {.gear} () / {.select} (SELECT) / {.insert} (INSERT) / {.update} (UPDATE) / {.delete} (DELETE) / {.get} (GET) / {.post} (POST) / {.put} (PUT) / {.patch} (PATCH) / {.button} (button link) / {.blank} (target="_blank") / [text]{.smallcaps} (small caps)

The Remark plugin also pulls page metadata out of the paired content.mdx so page.tsx can import it:

  • title — The # heading, which informs the browser tab
  • toc — ## / ### headings, used for the sticky-note table of contents and anchor links
  • frontmatter — The YAML defined at the top of the page

next.config.ts also registers Rehype (HTML parsing and serialization) plugins:

  • rehype-slug adds id to each heading from its text so that anchor links are functional.
  • My custom plugin runs rehype-highlight (our themed syntax highlighting) with a small language set we use in the docs.

Documentation Pages

Landing Page

The main documentation landing page (/docs) lives at src/app/docs/page.tsx, and is built by React, not MDX and hides the sidebar navigation:

  • A .docs-index wrapper is what switches the layout from a guide to a landing.
  • Displayed subtitle is wrapped into a feature flag (src/lib/flags.ts) defined in docsIndexSubtitle, rendering different text depending on the FLAG_PUBLIC value at build time.

This landing page:

  • Reads DOCS_NAV_SECTIONS from _nav/docs-nav-data.ts (sidebar navigation).
  • Keeps Installation / Guides / Reference sections under tilted sticky notes, defined by LANDING_TITLES.
  • Turns Etc. / Support into footer buttons.

Content Pages

Since every guide in documentation is wrapped in src/app/docs/layout.tsx, the per-page page.tsx defines the rest.

Articles feature a sidebar inspired by the poetry book pages, and a sticky-note table of contents, courtesy of DocsPage.

API Library

The API Library is a hybrid page (src/app/docs/api/page.tsx) — it first renders content from content.mdx, then DocsApiReference embeds my customized Scalar API explorer underneath.

The API explorer catalogs operations from a live PostgREST OpenAPI document. loadDatabaseOpenApi.ts fetches {project}/rest/v1/ with the service_role key so admin tables and RPCs still appear. FLAG_PUBLIC picks which project to introspect:

  • FLAG_PUBLIC=false — NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SECRET_KEY (my custom install)
  • FLAG_PUBLIC=true — API_PUBLIC_URL, API_SERVICE_ROLE_KEY, and API_ANON_KEY so that public project is fully specified (example database for the packaged guestbook)

Live OpenAPI

loadDatabaseOpenApi.ts:

  • Caches the fetch (React cache() plus a 60-second revalidate) so the API Library sidebar and the Scalar API explorer share one request on /docs/api. Guides do not fetch OpenAPI.
  • After fetch it stamps our info title/description, apikey / bearerAuth security, and servers overlay.
  • Overlays required PostgREST row filters on PATCH / DELETE (replacing the matching $ref) so Test Request does not list the same query parameter twice.
  • typesToOpenApi.ts still builds sidebar links from Scalar inline call anchors (openApiNav) and marks non-anon operations as requiring a JWT.

Scalar API Explorer

DocsApiReferenceView:

  • Loads the @scalar/api-reference browser bundle from /docs/api/scalar-standalone and renders it after the content in src/app/docs/api/content.mdx, which also displays differently depending on the build.
  • page.tsx preloads that script so the download overlaps the article render, and streams a Loading API reference skeleton behind Suspense until the spec is ready. On the public build, the view waits until the stored project ref is read from sessionStorage before mounting Scalar.
  • Enables the Enter Your Supabase Project ID field above the explorer, pointing test requests at the visitor's entered project on the public build.

Search & LLM Ingestion

For every documentation page except API Library (/docs/api), Search Docs opens the shared overlay powered by DocsSearch.tsx:

  • DocsSearchProvider loads /docs/search-index the first time Search Docs or the landing search bar opens (not on /docs/api, which uses Scalar search). Until the index arrives the overlay shows Indexing….
  • searchTerms.ts expands light English inflections at index and query time (managing ↔ manage), with prefix match and a bit of fuzzy room on longer terms.

What Gets Indexed

  • Host pages only (**/content.mdx); _snippets are never rendered or indexed independently
  • Snippet imports are inlined into the host before flattening headings/body
  • When FLAG_PUBLIC=true, pages with public: false are omitted (anything under "Internal" in the sidebar)
  • {.public} / {.private} fences and *-public embed pairs resolve through the live Remark plugin so public deploys never index private-only copy
  • OpenAPI / Scalar operations are not returned in the user guide search — only other docs .mdx files (including /docs/api prose from api/content.mdx when it is searchable)

Accessibility

Along with checking pages with Chrome's Lighthouse accessibility audit, as part of pull request checks, a11y lint runs a spot check on all documentation pages to ensure they are somewhat accessible and any errors discovered are patched before deployment.