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.tsreads that environment variable asflags.docsOnly.next.config.tsalso listsFLAG_DOCSONLYunder.env.localso 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 tabtoc—##/###headings, used for the sticky-note table of contents and anchor linksfrontmatter— The YAML defined at the top of the page
next.config.ts also registers Rehype (HTML parsing and serialization) plugins:
rehype-slugaddsidto 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.
src/mdx-components.tsx runs last at render, swapping native MDX tags for custom styled ones, and registering my fence components so that they display correctly:
h1/h2/h3become.docs-title/.docs-heading/.docs-subheadingprebecomesDocsCodeBlocktablegets.docs-tableligets the heart list item icon (orDocsTaskItemfor- [x], which are interactive!! checkboxes)DocsCalloutbecomes a callout boxDocsColumnsbecomes mobile-responsive columnsDocsTabsetbecomes mobile-responsive tabbed or drop-down section
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-indexwrapper is what switches the layout from a guide to a landing. - Displayed subtitle is wrapped into a feature flag (
src/lib/flags.ts) defined indocsIndexSubtitle, rendering different text depending on theFLAG_PUBLICvalue at build time.
This landing page:
- Reads
DOCS_NAV_SECTIONSfrom_nav/docs-nav-data.ts(sidebar navigation). - Keeps
Installation/Guides/Referencesections under tilted sticky notes, defined byLANDING_TITLES. - Turns
Etc./Supportinto 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.
The API Library (/docs/api) widens the sidebar and replaces the default links with anchors to calls, with no table of contents by skipping import of DocsPage on purpose, instead importing DocsApiReference.
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_URLandSUPABASE_SECRET_KEY(my custom install)FLAG_PUBLIC=true—API_PUBLIC_URL,API_SERVICE_ROLE_KEY, andAPI_ANON_KEYso that public project is fully specified (example database for the packaged guestbook)
Live OpenAPI
- 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
infotitle/description,apikey/bearerAuthsecurity, andserversoverlay. - Overlays required PostgREST row filters on PATCH / DELETE (replacing the matching
$ref) so Test Request does not list the same query parameter twice. typesToOpenApi.tsstill builds sidebar links from Scalar inline call anchors (openApiNav) and marks non-anon operations as requiring a JWT.
- Shared docs chrome only (sidenav + search provider). It does not fetch OpenAPI.
- On
/docs/api,DocsApiReferencehydratesDocsApiNavfrom the live spec. Until that lands, the sidebar shows a same-height Loading API catalog placeholder.DocsSidenavstill swaps the usual docs nav for those API anchors.
Scalar API Explorer
- Loads the
@scalar/api-referencebrowser bundle from/docs/api/scalar-standaloneand renders it after the content insrc/app/docs/api/content.mdx, which also displays differently depending on the build. page.tsxpreloads that script so the download overlaps the article render, and streams a Loading API reference skeleton behindSuspenseuntil the spec is ready. On the public build, the view waits until the stored project ref is read fromsessionStoragebefore 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.
- Turns Scalar's own theme off and injects my
customCssthat remaps Scalar onto guestbook tokens instead. - Hides Scalar's native sidebar and sticky search bar, instead, the sidebar search opens Scalar's search.
- Hides the Models section (
hideModels). - Starts with every tag collapsed (
defaultOpenAllTags/defaultOpenFirstTagoff). The API sidenav matches: section headings show a caret and stay collapsed until you open that heading or the matching explorer tag. Click a sidebar operation to jump to that call. - Hides the Test Request functionality unless
FLAG_PUBLIC(public build) orFLAG_APITEST(custom build, for my internal testing) flags are on. - With
scalarApiKey.ts, injects the saved anonapikeyand admin JWT on Send so focusing one Authentication tab does not drop the other.
Search & LLM Ingestion
For every documentation page except API Library (/docs/api), Search Docs opens the shared overlay powered by DocsSearch.tsx:
DocsSearchProviderloads/docs/search-indexthe 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.tsexpands 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);_snippetsare never rendered or indexed independently - Snippet imports are inlined into the host before flattening headings/body
- When
FLAG_PUBLIC=true, pages withpublic: falseare omitted (anything under "Internal" in the sidebar) {.public}/{.private}fences and*-publicembed 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
.mdxfiles (including/docs/apiprose fromapi/content.mdxwhen it is searchable)
On the API Library page (/docs/api), DocsSidenav labels the control Search API and calls Scalar's native search modal via openScalarSearch() instead of the user guide search.
Agents follow discover → fetch → cite against the same composed guides the user guide search.
/llms.txt
src/app/llms.txt/route.ts serves a plain-text catalog from buildLlmsTxt():
- One line per indexed guide with HTML and
.mdlinks - Grouped by nav section when available
- A short API pointer to
/docs/api(Scalar) — no OpenAPI schema dump - Same opt-outs as the user guide search (
public: falseunderFLAG_PUBLIC)
/docs/<slug>.md
next.config.ts rewrites /docs/:path*.md to the handler under src/app/docs/raw/:
- Each response is the composed markdown for that host (
#title, sections, inlined snippets). Unknown or opted-out slugs return404. - There are no mirrors of individual Scalar operations.
| Mechanism | Effect on search / LLM surfaces |
|---|---|
public: false + FLAG_PUBLIC=true | Page dropped from the composed corpus for that deploy |
{.public} / {.private} fences | Only the active fence's copy is composed/indexed |
*-public embed pairs | Remark picks the embed that matches the flag |
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.