Repository navigation
docs: migrate site from astro to fumadocs - #12
Conversation
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>
…makov/data-path into docs/fumadocs-migration
There was a problem hiding this comment.
💡 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 |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
Replaces Astro 6 + Starlight with Fumadocs on Next 16 static export, ported from the working setup in the sibling
playwright-page-objectrepo rather than authored fresh.URL parity is exact
This is what made the migration low risk. Starlight (
base: "", default trailing slash) and Fumadocs (baseUrl: "/"+ NexttrailingSlash: true) emit identical paths, so all 15 doc routes plus/survive with no redirects.scripts/validate-routes.mjspins this as a build gate — it asserts every route emitted anout/**/index.htmland that each canonical ishttps://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
titleanddescription, which satisfies the FumadocspageSchema, and no code fence used expressiveCode meta — so Shiki is a drop-in on the samegithub-light/github-darkthemes. Three content changes total::::noteasides →<Callout type="info">(guides/templates.mdx,integrations/zod.mdx)index.mdx(template: splash) →app/(home)/page.tsxsidebararray → fivemeta.jsonfiles preserving the existing orderAll 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
lastModifiedfromgit logper file.Theme
Yellow accent over the
neutral.csstoken preset.--color-fd-primary-foregroundis 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.mjsrasterises the card so the PNG cannot drift from its SVG source.Blog ships without posts
Infrastructure is included, but
blog/[slug]andblog/tags/[tag]sit under a Next private folder (app/(home)/blog/_pending/).output: "export"fails the build outright on an emptygenerateStaticParams()— 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 inapp/(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 runslint→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: 0is retained — Fumadocs needs git history forlastModified.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.noImgElement(next/imagebuys nothing withimages.unoptimized+ static export of SVGs) and twonoImportantStyles(scroll-lock overrides that must defeatreact-remove-scroll).🤖 Generated with Claude Code
Summary by cubic
Migrates the docs site from
astro/@astrojs/starlighttofumadocsonnext16 static export. Old: builds todocs/distwith/sitemap-index.xml; new: builds todocs/outwith/sitemap.xml. All documentation URLs are unchanged; new public routes include/blog/, author pages, and an RSS feed.scripts/validate-routes.mjsasserts every exported route, canonical URL, and selected titles/H1s match the legacy set.<Callout type="info">; sidebar arrays →meta.json. Files were git-renamed to preservelastModified.fumadocs-uineutral; forces--color-fd-primary-foregroundto near-black in both themes for WCAG contrast./blog/, author pages, and/blog/rss.xmlare live; post and tag routes sit under a private_pending/folder until there is content.app/api/searchserves the index; a client dialog is wired viafumadocs-core’s static client.app/robots.tspoints to/sitemap.xml.docs/outwithactions/deploy-pages@v5. Root sitemap dispatch is removed.Rollout and follow-ups
/sitemap.xml(the old/sitemap-index.xmlwill 404 after deploy).app/(home)/blog/_pending/[slug]→app/(home)/blog/[slug]and_pending/tags→tags, re-add the “Blog” nav link, and include the new routes inscripts/validate-links.mjsandscripts/validate-routes.mjs.Written for commit 30937a6. Summary will update on new commits.