Skip to content

Migrate docs from MkDocs to Astro Starlight - #342

Draft
ewels wants to merge 24 commits into
mainfrom
claude/docs-astro-starlight-migration-eo5r40
Draft

ewels wants to merge 24 commits into
mainfrom
claude/docs-astro-starlight-migration-eo5r40

Conversation

@ewels

@ewels ewels commented Aug 10, 2026 •

Copy link
Copy Markdown
Owner

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

  • Astro 7.2 + Starlight 0.41 (current latest), with Markdown processed by Sätteri, Astro's new Rust-based pipeline (default since Astro 7) — builds the whole site in seconds.
  • Search via Pagefind (built into Starlight), link prefetching on hover (Starlight default), and starlight-links-validator fails the build on broken internal links/anchors (replaces mkdocs strict mode).
  • Blog via 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 legacy feed_rss_* / feed_json_* feed URLs.

Versioned deploys. docs/_deploy.py keeps the mike-format gh-pages layout (versioned dirs, latest/prerelease aliases, 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's canonical_version setting. (starlight-versions was evaluated as a plugin alternative but can't snapshot this repo: it runs remark-mdx over 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 like success/failure/example), GitHub-style > [!NOTE] alerts in the changelog/contributing pages, linked content tabs, {% include %} code snippets from docs/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

  • Markdown stays .md in docs/src/content/docs/, processed by custom Sätteri mdast plugins in docs/plugins/ (admonitions, tabs, includes, GitHub alerts, {.screenshot} attributes, relative .md link resolution, mermaid). The shared pipeline is also used by docs/scripts/debug-render.mjs for debugging single pages.
  • The landing page, changelog, and contributing pages are generated from the repo-root README.md / CHANGELOG.md / CONTRIBUTING.md by docs/scripts/prepare-content.mjs (runs on every dev/build).
  • Social cards are generated per page at build time with astro-og-canvas (1200×630, page title in Kode Mono — the same font the old mkdocs-material social plugin used), wired up via a Starlight route middleware.
  • Site metadata and top-level navigation live in one module (docs/src/site.mjs) that drives the sidebar, header tabs, feeds, social cards, and landing page.
  • rich-codex screenshot regeneration is unchanged: RICH-CODEX comments, docs/code_snippets/ paths, and image locations all still line up.

CI / tooling changes

  • build-docs.yml adds a Node 24 setup and runs the rewritten docs/_deploy.py; the rich-codex step is untouched.
  • The docs dependency group in pyproject.toml shrinks to rich-codex + typer (the mkdocs stack is gone; mkdocs.yml deleted).
  • CONTRIBUTING.md gains a short "Docs" section (npm install && npm run dev in docs/, Node ≥22.12).

Known differences from the old site

  • The sitemap is sitemap-index.xml instead of sitemap.xml (Astro convention).
  • The old homepage had a right-hand table of contents; Starlight's splash template doesn't support one.
  • The RSS feed moves content-wise from full HTML to excerpt descriptions at the legacy URLs (starlight-blog also serves its own full feed at /blog/rss.xml).
  • Astro's stable Fonts API was considered for font optimization but not adopted: it fetches font files from a CDN at build time (fragile in restricted CI), while the current @fontsource setup 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

claude added 6 commits August 10, 2026 16:08
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
@ewels ewels changed the title Migrate docs from MkDocs to Astro Starlight [experimentation] Migrate docs from MkDocs to Astro Starlight Aug 10, 2026
@ewels ewels added the documentation Improvements or additions to documentation label Aug 10, 2026
@ewels
ewels requested a review from dwreeves August 10, 2026 18:34
claude added 6 commits August 10, 2026 18:37
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
@dwreeves

dwreeves commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator

I have the docs running locally!

A couple big thoughts:

  • The style editor. Basically, I suck at Javascript, and I wrote docs/overrides/editor.html back in April 2024, back when LLM coding was much more of a toy than anything that could produce serious output. So, the style editor is not very good. This page is a perfect candidate for modernization with vibe coding:
    • Implement the themes
    • Implement all of our options, not just a select few.
    • Probably some more things could be done here.
  • Docstrings: At some point I was thinking maybe one day we would use mkdocstrings to have API docs because why not. I was never quite sure of how necessary that is, and never prioritized it. There are no Astro Starlight plugins for Python docstrings, so RIP to that idea.
    • That said, mkdocstrings is actually very well composed and they decouple the mkdocs hook from the Python program parsing logic (Timothee aka "pawamoy" on Github is a GOAT). The latter is in a separate library called griffe: https://github.com/mkdocstrings/griffe It doesn't seem unreasonable to me to use griffe, then vibe code some nonsense that parses it into API docs. It's also not an unreasonable open source project idea to just vibe code something which does this more generally, but, that's an entirely different project haha.

A couple nits that come to mind. I'm mentioning these as I assume it's configurable (maybe not?):

  • Full navigation is shown in Documentation, Changelog, Contributing. Feels redundant.
image
  • I really, really hate the scroll bar for overflowing tabs. Scrollability should be represented with left and right arrows and a gradient, no scroll bar:
image

@ewels

ewels commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

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 👍🏻

@ewels

ewels commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

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.. 🤔

claude added 3 commits August 12, 2026 00:15
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
@ewels

ewels commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author
  • It doesn't seem unreasonable to me to use griffe, then vibe code some nonsense that parses it into API docs. It's also not an unreasonable open source project idea to just vibe code something which does this more generally, but, that's an entirely different project haha.

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.

ewels and others added 3 commits August 14, 2026 18:10
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>
@dwreeves

dwreeves commented Aug 14, 2026 •

Copy link
Copy Markdown
Collaborator

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.

Looks good! Get it on the plugins list next.

@ewels

ewels commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

@dwreeves pushed with API docs. Let me know what you think, or if you have any issues running it. Should just need uv on your command line and everything else will run off an npm install && npm run dev.

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>
ewels and others added 3 commits August 14, 2026 23:15
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>
@ewels

ewels commented Aug 17, 2026

Copy link
Copy Markdown
Owner Author

Looks good! Get it on the plugins list next.

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
@ewels ewels changed the title [experimentation] Migrate docs from MkDocs to Astro Starlight Migrate docs from MkDocs to Astro Starlight Sep 29, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants