I like how rust-lang's mdBook looks, the layout, the chrome, the five built-in color schemes (Light, Rust, Coal, Navy, Ayu). Starlight is the docs framework I actually reach for. So I built a Starlight theme that looks and behaves like mdBook, published as an installable npm plugin.

npm: starlight-theme-mdbook
Demo: starlight-theme-mdbook.netlify.app
Repo: 2u841r/starlight-theme-mdbook

Copying the real thing, not my memory of it

Every time I got unsure about a detail, I went and read mdBook's own source instead of guessing. The clearest example: the sidebar resize handle. I had it nested inside the scrollable chapter list, so it visually scrolled away with the content. It looked like an obvious bug once I opened mdBook's actual index.hbs template and saw the resize handle sitting as a sibling of the chapter list, not a child. position: absolute inside an overflow: auto container scrolls with that container's content; only position: fixed truly escapes it. Fixing it meant moving the fixed positioning and sizing off the inner scrollable pane and onto the outer nav element, with the resize handle as its sibling, matching mdBook's own structure exactly.

Two-tier theming

Starlight has its own binary light/dark mode baked into search, code blocks, and a few other internals. I didn't want to fight that, so the theme keeps Starlight's own data-theme (light/dark) working untouched, and layers a second attribute, data-mdbook-theme, on top of it that picks the actual palette (light, rust, coal, valentine, caramellatte). Rust, Valentine, and Caramellatte all map to Starlight's light mode under the hood; Coal maps to dark. Two of the color schemes (Valentine and Caramellatte) are ported from daisyUI, which meant using oklch() colors directly since Starlight's target browsers already support it natively, no conversion to hex needed.

From demo site to installable plugin

The first version was a full Astro+Starlight site with the overrides living directly in its own src/. That's a fine demo, but nobody can npm install a site. To make it a real plugin I studied four published Starlight themes (Rapide, Obsidian, Black, Catppuccin) directly in their source, not just their READMEs, to find the actual working shape: a StarlightPlugin factory with a config:setup hook that calls updateConfig({ components, customCss }), merging into whatever config the consumer already has instead of overwriting it. The helper that registers component overrides warns and skips instead of clobbering if the consumer already overrode that same component themselves, a small detail worth copying.

The repo ended up as a pnpm workspace: the installable package under packages/starlight-theme-mdbook, and a docs/ site that's both the dev sandbox and the theme's own usage guide, depending on the package via workspace:*.

Two build systems, two different bugs

Getting docs/ to actually deploy surfaced two unrelated failures, both traceable to the same root cause: something outside pnpm not understanding the workspace:* protocol.

On Cloudflare Workers Builds, wrangler deploy auto-detected there was no wrangler.jsonc yet and tried to run its own astro add cloudflare setup, which shells out to plain npm install to add the adapter. npm doesn't understand workspace:* and failed with EUNSUPPORTEDPROTOCOL. The fix was to pre-generate a minimal wrangler.jsonc for static-asset serving myself, so the auto-setup path never runs on the build server at all.

On Netlify, a duplicate site appeared, one dashboard-created project with git-triggered deploys, one from a stray CLI run, each with a different subdomain and both looking half-broken. Once that got sorted through the dashboard, the actual deploy path issue was simpler: the site's base directory was already scoped into docs/, but the publish directory setting was still docs/dist, a repo-root-relative path, so the two combined into a nonexistent docs/docs/dist. Publish directory needed to just be dist, relative to the already-scoped base.

Getting it into Starlight's own themes page

Starlight keeps a community themes showcase, and its contributing guide spells out the submission steps precisely: screenshots at exactly 1280x720, named <theme>-light.png/<theme>-dark.png, a new entry appended at the end of the existing list with a title, a short description, the live demo URL, and the two screenshot filenames. Followed all of it against a fork of the Starlight repo and opened the PR.