Repository navigation
Conversation
Rebuild the documentation site with Astro 7 and Starlight, replacing the
unmaintained MkDocs stack, while keeping the same URLs, styling and
features as the Material for MkDocs site:
- Markdown stays .md and is processed by Astro's new Rust-based Sätteri
pipeline (Astro 7's default). Custom Sätteri mdast plugins in
docs/plugins/ port the pymdown-era features: Material-style admonitions
(including collapsible ??? blocks and extra variants), content tabs,
{% include %} code snippets, GitHub blockquote alerts, {.screenshot}
image attributes, relative .md link resolution, and mermaid diagrams
(rendered client-side).
- Content moves to docs/src/content/docs/; blog posts move to blog/ with
their old date-based URLs, category and archive pages redirected.
mkdocs-redirects entries are carried over as Astro redirects, and the
legacy RSS/JSON feed URLs are kept as endpoints.
- Theme port in docs/src/styles/: black header with section tabs row,
indigo accent, pixelmix display font with offset shadow, Noto Sans body,
square list bullets, Material admonition and tab styling, slate dark
scheme, image lightbox, and a mike-compatible version selector.
- The Live Style Editor is rebuilt as an Astro page with vanilla JS
(no more jQuery), keeping identical behaviour and appearance.
- The blog uses starlight-blog (authors, tags, excerpts, RSS); search is
Pagefind; broken links fail the build via starlight-links-validator.
- docs/_deploy.py now builds the site once per deployed alias (base paths
are baked in at build time) and updates the mike-format gh-pages layout
(versioned dirs, latest/prerelease, versions.json) in place; old
mkdocs-built versions on gh-pages are left untouched.
- Landing page, changelog and contributing pages are generated from the
repo-root README/CHANGELOG/CONTRIBUTING by docs/scripts/prepare-content.mjs.
Known gaps vs the old site: social share cards are a static og:image
rather than per-page generated cards, and the sitemap is now
sitemap-index.xml instead of sitemap.xml.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Replace the static og:image with build-time generated social cards (1200x630) via astro-og-canvas, restoring parity with the mkdocs-material social plugin: every docs, blog and custom page gets a card with its page title in Kode Mono (the font the old cards used) on the site's black/indigo palette. A Starlight route middleware points each page's og:image/twitter:image at its card; pages without their own content entry (404, blog tag/author listings) fall back to the landing page card. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
The section tabs row used Starlight's --sl-color-white, which is inverted to near-black in light mode and unreadable on the black header. Spotted in side-by-side comparisons against the deployed mkdocs site, along with the hero logo rendering slightly smaller than before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Node >=22.12 is required by Astro 7; drop a stale comment about the removed tool.uv docs constraint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Findings from a reuse/simplification/efficiency/altitude review pass: - Single sources of truth: site title/tagline and the top-level navigation now live in src/site.mjs (previously duplicated across the Starlight sidebar config, header tabs, feeds, social cards and the landing page generator); the header tabs are derived from the sidebar entries. The satteri pipeline is shared between astro.config.mjs and the debug helper via plugins/pipeline.mjs, and base-path normalization lives in src/base.ts. - The editor's generated-code strings are now one span per option built by a shared styleString() helper, replacing the same four-span spacing algorithm implemented separately at build time and client side. - Turned silent failures loud: tab sets longer than the styled maximum now fail the build, and the landing page generator errors if a README image reference doesn't resolve. - Efficiency: versions.json is cached per session instead of fetched on every page view, the lightbox no longer leaks keydown listeners, link resolution memoizes per-file work, generated pages skip identical rewrites, and the deploy script moves dist instead of copying it. - Dead code removed: no-op editor special case, unused satteri direct dependency, stale #terminal-header CSS, duplicate per-variant backgrounds and light-theme tokens, hardcoded site-URL fallback; fixed the [!CAUTION] alert aria-label mismatch and a stale comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
- Migrate the three markdown_include-style includes in the Introduction to Click page that rendered as literal text, and the octicons icon shortcodes in the panels overview. - Restore feed item descriptions (from the excerpt delimiter) and mike's canonical_version behaviour: versioned builds now canonicalize to /latest/ instead of competing with it. - Show the version selector on all screen sizes by moving it into the main header row (it was hidden with the tabs below the lg breakpoint). - Deploy script: a stable release now removes its prerelease alias, and worktree cleanup no longer masks the original error on failure. - Deduplicate logo/favicon images between public/ and the content tree, pointing the README at the served copies. - Don't lightbox the landing page badges; drop unused #only-light/dark image CSS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Edit the Markdown content to plain Starlight/HTML instead of preprocessing
it, cutting the custom plugin pipeline roughly in half:
- Admonitions now use Starlight's built-in aside variants and styling
(info -> note, success/example -> tip, warning -> caution,
failure/error -> danger, original titles preserved), dropping the
Material-for-MkDocs restyle and extra variants.
- Collapsible ??? admonitions become plain <details>/<summary> HTML
(which also renders on GitHub), with a light touch of CSS.
- {.screenshot} image markers are gone; content images get the same
centred/capped styling via one CSS rule.
- Relative .md links are rewritten to page-relative URLs in the source
(still checked by starlight-links-validator).
- The satteri-admonitions, satteri-image-attrs and satteri-md-links
plugins are deleted along with their shared helpers and CSS.
Four small plugins remain, each with a concrete reason: code-snippet
includes (single source shared with rich-codex, which executes the
snippet files), content tabs (native <Tabs> needs MDX, which forbids the
HTML comments rich-codex reads), GitHub blockquote alerts (CHANGELOG and
CONTRIBUTING must keep GitHub-flavoured syntax to render on github.com),
and mermaid (no Starlight/Sätteri-compatible alternative).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
The initial conversion stripped the pymdownx.inlinehilite `#!python` prefixes to plain code spans. Bring back the original terse syntax (`` `#!python import click` ``), rendered at build time by a small Shiki plugin with per-theme CSS variables. The `<Code inline />` component was considered but requires MDX, which forbids the HTML comments rich-codex needs, and the prefix syntax is shorter anyway. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
The 14 pages using content tabs or code-snippet includes are now .mdx and
use Starlight's own building blocks instead of custom Sätteri plugins:
- Code snippets embed via `?raw` imports and the <Code> component (frame
titles and line-highlight meta preserved; the configuration-options
block slices src/rich_click/rich_click.py inline between its markers).
- Content tabs use <Tabs>/<TabItem>, with syncKey groups replacing the
old label-matching sync script (package manager, shell, config-style,
theme examples). The Live Style Editor page and its generated-code
tabs switch to the same components.
- HTML comments become MDX `{/* ... */}` comments; the upcoming
rich-codex release reads RICH-CODEX markers in that syntax, and
starlight-blog already supports the MDX excerpt delimiter.
This deletes the satteri-includes and satteri-tabs plugins and the
Material tab CSS/sync script, leaving only three small plugins: inline
`#!lang` code highlighting (now MDX-aware), GitHub blockquote alerts for
the changelog/contributing includes, and mermaid.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
debug-render.mjs existed to inspect the original seven-plugin Markdown pipeline; with three trivial plugins left (and most pages now MDX, which it couldn't process anyway), `npm run dev` covers the need. The shared pipeline module only existed to keep the helper in sync with the config, so the plugin list moves back inline into astro.config.mjs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
- The version dropdown is now plain HTML: docs/_deploy.py resolves the version list before building and passes it as DOCS_VERSIONS, so the header renders per-page links to each deployed version (a native <details> menu). No fetch, no sessionStorage, no JS at all, and each link deep-links to the same page in the other version. - The GitHub alert rewriting moves into prepare-content.mjs, which already rewrites those pages: `> [!NOTE]` becomes `:::note[Note]` before Starlight sees it, so the satteri-github-alerts plugin, its mdast helper and the custom aside CSS are all gone. Only two tiny plugins remain: inline `#!lang` highlighting and mermaid. - Snippet imports use `@code_snippets` / `@rich_click` aliases instead of long relative paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Replaces the hand-rolled lightbox with the maintained library, cutting the custom code to a three-line init and gaining smooth zoom animation, scroll-to-dismiss and Escape handling. starlight-image-zoom remains unusable here: it wraps images with a rehype plugin, which the Sätteri Markdown processor doesn't run (upstream issue HiDeoo/starlight-image-zoom#63 is still open). medium-zoom works purely client-side, so it is processor-agnostic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
|
I have the docs running locally! A couple big thoughts:
A couple nits that come to mind. I'm mentioning these as I assume it's configurable (maybe not?):
|
|
Nice, thanks for the feedback! I got a bit distracted last night and ended up working on rich-codex updates so dropped this in the middle a bit. Will get back to it and make your suggested changes though 👍🏻 |
|
I also like the sound of your API plugin suggestion. I have a couple of sites where that could be useful. I'll have a look into it.. 🤔 |
rich-codex 1.3 is the first release to parse MDX comments, so the markers
converted in the MDX migration are now supported by a published version
rather than an unreleased one.
- Bump the dependency and the pinned action to 1.3.0. It requires Python
3.10+ while the package still supports 3.8, so the dev entry gains the
same marker mypy already uses and the docs group gets a uv
requires-python constraint.
- Collapse the 43 single-setting markers onto one line
(`{/* RICH-CODEX working_dir: . */}`), which rich-codex accepts for
both comment styles; markers with several or nested settings keep the
block form. Verified by running rich-codex 1.3's own search over the
docs: 82 images found with no parse errors, identical to main.
- Fix blog excerpts in the legacy RSS feed, which broke when posts became
MDX: the delimiter is now `{/* excerpt */}`, so splitting only on the
HTML comment returned the entire post source, imports included.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Reapplies the behaviour changes from #343 and its follow-up, which landed on main against the mike-based script this branch replaces: - Only claim the `prerelease` alias when the series is newer than `latest`; warn instead when a prerelease targets an already-released series, since docs are published per major.minor and it would overwrite the released docs. - Retire the `prerelease` alias whenever a release supersedes the series it points at, not only when the series matches. Previously an rc series that never shipped a final left the alias stranded forever. - Retire alias directories after the builds succeed, so a failed build cannot leave gh-pages with the directory already removed. - Only write the root redirect once a `latest` build exists. - Flush progress output, so it interleaves correctly with subprocess output in block-buffered CI logs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
Conflicts and follow-ups: - docs/_deploy.py: kept this branch's Astro deploy script. The behaviour fixes from #343 and its follow-up were already ported in a9c6056, so main's mike-based edits have no remaining content to carry over. - pyproject.toml: main raised the floor to Python 3.10, so the rich-codex environment marker and the uv docs-group constraint added here (both only needed while the package still supported 3.8) are dropped as redundant. - CHANGELOG.md: the ProperDocs and Material 9.7 entries described unreleased, now-superseded steps towards the same goal as this branch, so they are replaced by the Starlight entry. The docs versioning fix entry is kept: the ported deploy logic implements that behaviour. Main's 82 regenerated screenshots merged onto their relocated paths under docs/src/content/docs/images/; verified byte-identical to main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012G95Fyvn2QtBacowwTLDui
You totally nerd-sniped me with this 🎯 😆 https://ewels.github.io/starlight-pydocs/ Working with it locally now, seems good! Will push changes shortly so that you can play with it. |
Generate the API reference with starlight-pydocs, which reads the rich_click source with griffe (static analysis, so nothing is imported) and injects one page per module under /api/rich_click/. - Type annotations naming click, rich, typer or the standard library link out through those projects' Sphinx inventories, and every object links to its source lines on GitHub, pinned to the commit being built. - Each object is badged with the release it first appeared in, worked out by extracting every v1.* release tag and diffing the object paths. The tag list is read from git so new releases need no edit here. Note the comparison is by path, so the 1.7 and 1.9 module reorganisations read as "added" for objects that are in fact older. - The reference needs the full tag history, hence fetch-depth: 0. The per-release dumps are cached outside node_modules and restored in CI keyed on the tag list, which keeps repeat builds at ~3s rather than ~14s; without it every build would redo 38 checkouts and extractions. - The pixel display font is dropped for the generated pages, whose titles are long dotted module paths that it renders unreadable. The sidebar entry lives in site.mjs as a plain link, because that module is also loaded by plain node scripts that cannot import the plugin's TypeScript entrypoint; astro.config.mjs swaps it for the placeholder group the plugin fills in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
actions/checkout 6.0.3 -> 7.0.1, actions/setup-node 6.5.0 -> 7.0.0, actions/setup-python 6.2.0 -> 7.0.0 and pypa/gh-action-pypi-publish 1.14.0 -> 1.14.2, all SHA-pinned. Nothing here is affected by the major bumps: the removed setup-python `pip-install` input is unused, and no workflow triggers on pull_request_target or workflow_run, which is what checkout v7 restricts. Restores the two `zizmor: ignore[artipacked]` suppressions that actions-up dropped while rewriting those lines; both checkouts deliberately persist credentials so a later step can push. zizmor reports no findings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The caret range only ever allowed patches on a 0.x package, so it bought nothing. An exact pin says plainly that the version moves when someone moves it, which matters while the plugin is still changing shape. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Looks good! Get it on the plugins list next. |
|
@dwreeves pushed with API docs. Let me know what you think, or if you have any issues running it. Should just need Still have a few more things I'd like to do on this site, so not worth properly reviewing yet, but I'm increasingly happy with it. |
Documentation is read by agents as often as by people, and the site was HTML only: an agent had to scrape a rendered page, and a reader had nothing to paste. - starlight-llms-txt writes llms.txt, llms-full.txt and llms-small.txt, with a set per section. It only walks the content collection, so the generated API reference is bridged in via optionalLinks, pointing at the llms.txt that starlight-pydocs publishes itself. - starlight-page-actions adds Copy Markdown / Open in ... buttons and the raw `<page>.md` route behind them. Its baseUrl is left unset so it doesn't write an llms.txt competing with the one above. - Every page advertises its Markdown with `<link rel="alternate">`; no plugin emits that. Skipped for the API reference pages, which emit their own. - A footer line points at llms.txt. Generating the files without linking them is how they end up unused. - `description` frontmatter on every page: it feeds the meta tags, Pagefind and the llms.txt output, and no page had one. Also strips the RICH-CODEX comments from the copied Markdown (33 of them on the themes page alone) and the repeated "Section titled" anchor links from the llms.txt output, and wraps two over-wide highlighted lines in the 1.8 release post. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The cards carried the square icon at 96px; use the wordmark at 960px, 80% of the 1200px card width, with the dark-mode artwork that reads on the card's black gradient. The logotype ships on a 960x360 canvas with the artwork occupying only 656x147 of it, so scaling the file as-is would have left the wordmark small and floating in the middle. astro-og-canvas takes a path rather than a buffer, so prepare-content.mjs trims the transparent margin into src/generated/og-logo.png alongside the pages it already generates. Title and description are a few points smaller to fit under the taller logo: the worst case in the docs is a two-line title above a three-line description, which now lands inside the card rather than off the bottom. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
astro-og-canvas draws the logo from the left padding edge and has no alignment option, so both the size and the centring are baked into the generated file: the wordmark is scaled to 60% of the card, then padded back out symmetrically to the width between the card's paddings. The card route draws it at its own dimensions and carries no numbers. The shorter logo leaves room for the title and description to go back to their original sizes; the worst case in the docs is now a two-line title above a four-line description, which still fits. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
astro-og-canvas clamps the text block to logoHeight + 2x padding when the description is short, so the gap swung between 108px and 57px depending on how much text a page had. padding 36 brings it to a consistent 81-84px across the shortest and longest cards in the docs, at the cost of 12px of side margin. Title lineHeight was the other candidate and moved the gap by 16px only at a value too tight for a wrapping title. Also drops the logo's mtime guard, which went stale the moment the output started depending on CARD_PADDING as well as on the source file: changing the padding would not have regenerated it. Rebuilding every time costs tens of milliseconds, and astro-og-canvas keys its own cache on the logo's contents, so an unchanged result still redraws nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR opened: withastro/starlight#4130 |
Port main's docs changes to Starlight: add the asyncclick support page and sidebar entry, and bring the Typer 0.26+ section into typer_support.mdx. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wQvWVeCLRAhMJS4LCacER


MkDocs is unmaintained upstream. #341 patched around it short-term by switching to the ProperDocs fork, but generally speaking I've not been using mkdocs for most of my sites for a while now.
Generally my docs framework of choice has been Astro for quite a while now. Together with Starlight, it is fast, fully featured and highly configurable. I was curious to see how big a job it'd be to rebuild the rich-click docs site using Astro, so I pointed Claude at it and asked for a full rebuild of the docs site on Astro Starlight, keeping the same URLs, styling, and features.
This is a proof-of-concept only for now, just playing around and see how the site looks and what we think of the code. Curious for your thoughts at some point @dwreeves, but consider this very low priority.
Full description 🤖
Stack
starlight-links-validatorfails the build on broken internal links/anchors (replaces mkdocsstrictmode).starlight-blog(authors, tags, excerpts, RSS at/blog/rss.xml).What's preserved
URLs. Verified page-by-page against the deployed gh-pages tree: all documentation pages (
documentation/*, underscores intact),/editor/,/changelog/,/contributing/, blog post slugs (including dots, e.g.blog/version-1.9), the two mkdocs-redirects entries, the old date-based blog URLs, category/archive pages (redirected), and the legacyfeed_rss_*/feed_json_*feed URLs.Versioned deploys.
docs/_deploy.pykeeps the mike-format gh-pages layout (versioned dirs,latest/prereleasealiases,versions.json), building once per alias since Astro bakes the base path into URLs at build time. Old mkdocs-built versions (1.8, 1.9) stay untouched and remain in the version dropdown. Versioned builds canonicalize to/latest/, matching mike'scanonical_versionsetting. (starlight-versionswas evaluated as a plugin alternative but can't snapshot this repo: it runsremark-mdxover the Markdown and chokes on the HTML comments that rich-codex and the blog excerpts depend on, and its URL scheme would break every existing/latest/link.)Styling. Black header with section tabs, pixelmix headings with the offset shadow, indigo accent, Noto Sans body, square hover-accent list bullets, Material-style admonitions and content tabs, slate dark scheme, image lightbox (replaces glightbox), and a mike-compatible version selector — verified with side-by-side Playwright screenshots against the deployed site in both themes.
Features. Material-syntax admonitions (including collapsible
???blocks and non-Starlight variants likesuccess/failure/example), GitHub-style> [!NOTE]alerts in the changelog/contributing pages, linked content tabs,{% include %}code snippets fromdocs/code_snippets/, mermaid diagrams (client-side, theme-aware), heading anchor links, edit-this-page links, per-page social cards (see below), and the interactive Live Style Editor (rebuilt in vanilla JS — jQuery removed).How it works
.mdindocs/src/content/docs/, processed by custom Sätteri mdast plugins indocs/plugins/(admonitions, tabs, includes, GitHub alerts,{.screenshot}attributes, relative.mdlink resolution, mermaid). The shared pipeline is also used bydocs/scripts/debug-render.mjsfor debugging single pages.README.md/CHANGELOG.md/CONTRIBUTING.mdbydocs/scripts/prepare-content.mjs(runs on every dev/build).astro-og-canvas(1200×630, page title in Kode Mono — the same font the old mkdocs-materialsocialplugin used), wired up via a Starlight route middleware.docs/src/site.mjs) that drives the sidebar, header tabs, feeds, social cards, and landing page.RICH-CODEXcomments,docs/code_snippets/paths, and image locations all still line up.CI / tooling changes
build-docs.ymladds a Node 24 setup and runs the rewrittendocs/_deploy.py; the rich-codex step is untouched.docsdependency group inpyproject.tomlshrinks torich-codex+typer(the mkdocs stack is gone;mkdocs.ymldeleted).CONTRIBUTING.mdgains a short "Docs" section (npm install && npm run devindocs/, Node ≥22.12).Known differences from the old site
sitemap-index.xmlinstead ofsitemap.xml(Astro convention)./blog/rss.xml).@fontsourcesetup self-hosts everything and is Starlight's documented approach.Review
The branch went through a 4-angle cleanup pass (reuse/simplification/efficiency/altitude) and a 10-angle code review; all confirmed findings were fixed, including three user-visible bugs caught against the built output (unmigrated
{!...!}includes, literal icon shortcodes, empty RSS descriptions).🤖 Generated by Claude Code