Build a documentation site with the static stack

Start state: nothing. End state: a full documentation site — sidebar, live search, per-page table of contents, prev/next pager, breadcrumbs, copy buttons on every code block — as plain HTML files you can host anywhere for free. The static stack has no build tool and no framework: content lives in JSON, a generator turns it into pages.

1. Scaffold the static shape

pnpm new fielddocs --title "Field Docs" --stack static --type website-blog-docs \
  --design editorial --accent teal --mode light

Expected: apps/fielddocs scaffolded from _template/static-site — index.html, css/, js/main.js, and content/blog.json + content/docs.json. There is no package.json in the app and no install step: --stack static means plain HTML+JS+BEM, website types only, deploy = files as-is.

--features does not apply here — features belong to --type webapp. The static stack's feature set is the site itself: website, blog, and docs pages driven by the two JSON files.

2. Understand the content schema

Open apps/fielddocs/content/docs.json. It is an array of sections; each section has a title and pages; each page is a slug, a title, and a body of typed blocks:

[
  {
    "title": "Getting started",
    "pages": [
      {
        "slug": "introduction",
        "title": "Introduction",
        "body": [
          { "kind": "p", "text": "Welcome to the Field Docs documentation." },
          { "kind": "h2", "text": "Structure" },
          { "kind": "list", "items": ["Sections group related pages", "Each page is a slug + title + blocks", "The sidebar renders itself"] }
        ]
      }
    ]
  }
]

Block kinds: p, h2, h3, list, code, and quote. The sidebar, search index, and pager are all derived from this one file — you never edit navigation by hand.

3. Write your docs

Replace the sample sections with your own. A useful starting shape: one "Getting started" section (introduction, install, quickstart), one "Guides" section (a page per task), one "Reference" section (a page per command or API). Keep slugs lowercase-with-dashes — each slug becomes <slug>.html.

4. Regenerate the pages

pnpm regen-blog fielddocs   # regenerates blog AND docs from the JSON files

Expected: the generator emits one HTML file per docs page plus search-index.json. Each page gets, for free:

  • Live search — the docs search box lazy-fetches search-index.json on focus and filters as you type, with keyboard navigation over the hits
  • "On this page" — a table-of-contents box generated from the page's h2 blocks (appears on any page with 2+ h2s)
  • Prev/next pager cards across the whole docs order, plus breadcrumbs
  • A copy button on every code block
Edit content/docs.json, not the generated HTML — the next regen overwrites generated output. The JSON is the source of truth and versions cleanly with your site.

5. Preview locally

open apps/fielddocs/index.html   # or: npx serve apps/fielddocs

Static files need no dev server, though search's fetch of search-index.json is disabled on file:// — use a local server to try search.

6. Deploy

pnpm deploy fielddocs --to github-pages

Expected: deploy.mjs detects "kind: static files" (index.html present, no package.json), skips the build entirely, and pushes the directory as-is. Any of the 6 providers works; GitHub Pages and Surge are the classic free homes for docs. The choice is remembered in apps/fielddocs/.deploy.json.

What you built

  • A documentation site with search, TOC, pager, breadcrumbs, and copy buttons — zero JavaScript written by you
  • A content pipeline: edit content/docs.json → pnpm regen-blog fielddocs → deploy
  • A site with no dependencies to update, ever — plain HTML, CSS, and one small JS file