Skip to content

docs: migrate site from astro to fumadocs - #12

Merged
sergeyshmakov merged 3 commits into
mainfrom
docs/fumadocs-migration
Aug 25, 2026
Merged

sergeyshmakov merged 3 commits into
mainfrom
docs/fumadocs-migration

Conversation

@sergeyshmakov

@sergeyshmakov sergeyshmakov commented Aug 24, 2026 •

Copy link
Copy Markdown
Owner

Replaces Astro 6 + Starlight with Fumadocs on Next 16 static export, ported from the working setup in the sibling playwright-page-object repo rather than authored fresh.

URL parity is exact

This is what made the migration low risk. Starlight (base: "", default trailing slash) and Fumadocs (baseUrl: "/" + Next trailingSlash: true) emit identical paths, so all 15 doc routes plus / survive with no redirects.

scripts/validate-routes.mjs pins this as a build gate — it asserts every route emitted an out/**/index.html and that each canonical is https://path.shmakov.tools<route>. The route list in that script is the old Starlight route set, unchanged.

Content barely moved

Every page already carried just title and description, which satisfies the Fumadocs pageSchema, and no code fence used expressiveCode meta — so Shiki is a drop-in on the same github-light/github-dark themes. Three content changes total:

  • Two Starlight :::note asides → <Callout type="info"> (guides/templates.mdx, integrations/zod.mdx)
  • index.mdx (template: splash) → app/(home)/page.tsx
  • Starlight's explicit sidebar array → five meta.json files preserving the existing order

All 15 content files land as git renames (100% similarity on 13, 97%/99% on the two Callout conversions), so per-file history survives — which matters because Fumadocs reads lastModified from git log per file.

Theme

Yellow accent over the neutral.css token preset. --color-fd-primary-foreground is forced near-black in both themes: the preset's light value is near-white, which on yellow is ~2.6:1 and fails WCAG. The shipped pair gives 6.6:1 light / 11.7:1 dark, verified against the compiled CSS rather than the source, since the override has to win on cascade order.

Brand assets are generated in-repo — favicon, logo, hero, and a 1200×630 OG card sharing one indent-cascade motif. scripts/render-png.mjs rasterises the card so the PNG cannot drift from its SVG source.

Blog ships without posts

Infrastructure is included, but blog/[slug] and blog/tags/[tag] sit under a Next private folder (app/(home)/blog/_pending/). output: "export" fails the build outright on an empty generateStaticParams() — it does not silently emit nothing. /blog/, the author page and the RSS feed build fine, because the author list is a static map rather than post-derived.

Activation is two git mvs, documented in app/(home)/blog/_pending/README.md. I chose this over inventing a placeholder post so no fabricated content ships.

CI

Build output moves docs/dist → docs/out, so the Pages artifact path changes with it. The workflow now also gates PRs and runs lint → typecheck → build → validate:links → validate:routes, and drops the root-sitemap dispatch that no longer has a consumer (per the 2026-08-06 decision that each custom-domain host owns its sitemap). fetch-depth: 0 is retained — Fumadocs needs git history for lastModified.

Verification

All green locally: build, typecheck, lint, validate:links (0 errors), validate:routes (18 routes). Exported route list diffed against the old Astro route set — exact match. Pre-commit hook ran the library suite too: 205 runtime + 60 type tests passing.

Reviewer notes

  • /sitemap-index.xml → /sitemap.xml. Astro used the former, Next emits the latter. If the old path is submitted in Search Console it will 404 after deploy and needs resubmitting.
  • Four biome warnings are intentional and match the reference repo: two noImgElement (next/image buys nothing with images.unoptimized + static export of SVGs) and two noImportantStyles (scroll-lock overrides that must defeat react-remove-scroll).

🤖 Generated with Claude Code


Summary by cubic

Migrates the docs site from astro/@astrojs/starlight to fumadocs on next 16 static export. Old: builds to docs/dist with /sitemap-index.xml; new: builds to docs/out with /sitemap.xml. All documentation URLs are unchanged; new public routes include /blog/, author pages, and an RSS feed.

  • URL parity is enforced: scripts/validate-routes.mjs asserts every exported route, canonical URL, and selected titles/H1s match the legacy set.
  • Content moved with minimal edits: homepage is a React page; two Starlight notes → <Callout type="info">; sidebar arrays → meta.json. Files were git-renamed to preserve lastModified.
  • Theme: yellow accent over fumadocs-ui neutral; forces --color-fd-primary-foreground to near-black in both themes for WCAG contrast.
  • Blog infrastructure ships without posts: /blog/, author pages, and /blog/rss.xml are live; post and tag routes sit under a private _pending/ folder until there is content.
  • Search is static: app/api/search serves the index; a client dialog is wired via fumadocs-core’s static client.
  • Robots and sitemap are generated by Next routes: app/robots.ts points to /sitemap.xml.
  • CI: Pages workflow now runs on PRs, executes lint → typecheck → build → link and route validators, and uploads docs/out with actions/deploy-pages@v5. Root sitemap dispatch is removed.

Rollout and follow-ups

  • Update Search Console to use /sitemap.xml (the old /sitemap-index.xml will 404 after deploy).
  • No redirects are required; documentation route set is identical.
  • To enable blog posts later: move app/(home)/blog/_pending/[slug] → app/(home)/blog/[slug] and _pending/tags → tags, re-add the “Blog” nav link, and include the new routes in scripts/validate-links.mjs and scripts/validate-routes.mjs.

Written for commit 30937a6. Summary will update on new commits.

Review in cubic

sergeyshmakov and others added 3 commits August 24, 2026 10:41
Replace Astro 6 + Starlight with Fumadocs on Next 16 static export, ported
from the working setup in the sibling playwright-page-object repo rather than
authored fresh.

URL parity is exact, which is what made this low risk: Starlight (base "",
default trailing slash) and Fumadocs (baseUrl "/" plus Next trailingSlash)
emit identical paths, so all 15 doc routes plus / survive with no redirects.
scripts/validate-routes.mjs pins that as a build gate, asserting every
canonical against path.shmakov.tools.

Content needed almost no editing. Every page already carried just title and
description, which satisfies the Fumadocs pageSchema, and no code fence used
expressiveCode meta, so Shiki is a drop-in on the same github-light and
github-dark themes. Only three changes: two Starlight :::note asides became
Callouts, and index.mdx (template: splash) became app/(home)/page.tsx.
Starlight's explicit sidebar array became five meta.json files preserving the
existing order.

The accent is yellow over the neutral token preset. primary-foreground is
forced near-black in both themes because the preset's light value is
near-white, which on yellow is ~2.6:1 and fails WCAG; the shipped pair gives
6.6:1 light and 11.7:1 dark.

Blog infrastructure is included but has no posts yet, so blog/[slug] and
blog/tags/[tag] sit under a Next private folder: output "export" fails the
build outright on an empty generateStaticParams(). /blog/, the author page and
the RSS feed build fine, since the author list is a static map rather than
post-derived. See app/(home)/blog/_pending/README.md to activate.

Build output moves docs/dist -> docs/out, so the Pages workflow artifact path
changes with it. The workflow also now gates PRs and runs lint, typecheck and
both validators, and drops the root-sitemap dispatch that no longer has a
consumer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace Astro 6 + Starlight with Fumadocs on Next 16 static export, ported
from the working setup in the sibling playwright-page-object repo rather than
authored fresh.

URL parity is exact, which is what made this low risk: Starlight (base "",
default trailing slash) and Fumadocs (baseUrl "/" plus Next trailingSlash)
emit identical paths, so all 15 doc routes plus / survive with no redirects.
scripts/validate-routes.mjs pins that as a build gate, asserting every
canonical against path.shmakov.tools.

Content needed almost no editing. Every page already carried just title and
description, which satisfies the Fumadocs pageSchema, and no code fence used
expressiveCode meta, so Shiki is a drop-in on the same github-light and
github-dark themes. Only three changes: two Starlight :::note asides became
Callouts, and index.mdx (template: splash) became app/(home)/page.tsx.
Starlight's explicit sidebar array became five meta.json files preserving the
existing order.

The accent is yellow over the neutral token preset. primary-foreground is
forced near-black in both themes because the preset's light value is
near-white, which on yellow is ~2.6:1 and fails WCAG; the shipped pair gives
6.6:1 light and 11.7:1 dark.

Blog infrastructure is included but has no posts yet, so blog/[slug] and
blog/tags/[tag] sit under a Next private folder: output "export" fails the
build outright on an empty generateStaticParams(). /blog/, the author page and
the RSS feed build fine, since the author list is a static map rather than
post-derived. See app/(home)/blog/_pending/README.md to activate.

Build output moves docs/dist -> docs/out, so the Pages workflow artifact path
changes with it. The workflow also now gates PRs and runs lint, typecheck and
both validators, and drops the root-sitemap dispatch that no longer has a
consumer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a15e1e09d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

working-directory: docs
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin the workflow to released action majors

Every documentation push, pull request, and manual run stops before npm ci because actions/checkout@v7 cannot be resolved; the same workflow also references the unavailable actions/setup-node@v7. Use released action tags (the repository's publish workflow currently uses the corresponding @v6 majors) so the documentation build and deployment can start.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These majors do exist and resolve — I checked before pinning them:

action tag used resolves to latest release
actions/checkout @v7 3d3c42e5aa v7.0.1
actions/setup-node @v7 8207627860 v7.0.0
actions/configure-pages @v6 — v6.0.0
actions/upload-pages-artifact @v5 — v5.0.0
actions/deploy-pages @v5 — v5.0.0

Every version in this workflow is the current latest released major, verified via repos/{action}/releases/latest and git/ref/tags/v7.

The observation about publish.yml using @v6 is correct, but the inference is inverted: that means publish.yml is one major behind, not that v7 is unavailable. The same five pins run in production today in the sibling playwright-page-object repo, which this workflow was ported from.

Not applying this one. Bumping publish.yml and pr.yml (still on @v4) to current majors is a reasonable follow-up, but out of scope for a docs migration.

@sergeyshmakov
sergeyshmakov merged commit 0bb7b01 into main Aug 25, 2026
4 checks passed
@sergeyshmakov
sergeyshmakov deleted the docs/fumadocs-migration branch August 25, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant