Conversation
`--format website` statically exports the site, as the Docker image does.
`--format fragment` — the default — compiles MDX to plain HTML with no layout,
stylesheet or script, from a folder or from stdin, and needs nothing but node.
The CLI is built on commander, as `material-theme-builder` is. Every website
option is declared once, with the environment variable the app reads it as, and
`Option.env()` does the fallback — which is how the Docker image and the
reusable workflow keep working with no flags at all.
The app is copied out of the package before being built: Turbopack refuses to
process a project rooted under `node_modules`, which is exactly where an
installed package lives. The copy mirrors what `files` publishes — no `/api`,
whose Route Handlers cannot be exported, and no tests or stories, which reach
for `.storybook` and `vitest` — so nothing has to be moved aside and put back.
`bin/build.mjs` is gone: it predated the Docker image, was never published (the
package was `private`), and built a server bundle rather than a static export.
`Toc` leaves the MDX components map. The page renders it from the layout with
`toc={doc.tableOfContents}`, so writing `<Toc />` in a doc only ever crashed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5
Tailwind v4 discovers its sources from the project root, so a `className=` written in an `.mdx` file only produced a rule when the docs happened to sit inside that root. True of a build run from this repo — the in-repo build was right by accident — and false of one run from the CLI, which is handed a folder from anywhere and builds in a copy under `.pmndrs-docs`. Every utility class authored in MDX was missing from the stylesheet, and the elements using it rendered unstyled: `docs/authoring/introduction.mdx` sizes an iframe with `h-60`, the rule was dropped, and the page collapsed by 105px. `globals.css` now names the docs itself. The declaration has to be static — Tailwind resolves it at build time, against the stylesheet — so the CLI stages the folder at the path it names rather than the other way around. That is also where the Docker image already mounts it, `/app/docs`. Note that `tailwind.config.ts` could not carry this: its `content` is v3 syntax, and v4 only reads the file through an `@config` directive this app does not have. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5
Tailwind v4 only loads a config file through an `@config` directive, which this app does not have — so `tailwind.config.ts` has been inert. Its `content` globs never applied (automatic source detection did the work), and neither did its two plugins: `prose` appears nowhere in the app or the docs, and `aspect-video`, the one aspect utility in use, is core in v4. Being inert is not the problem; looking authoritative is. `Layout.tsx` pointed at it for the `grid-areas-*` values, which actually live in `globals.css` as `@utility` rules — and the natural place to reach for when a class goes missing from the stylesheet is exactly the file that cannot fix it. Verified by rebuilding both ways: the emitted CSS is byte-identical with the file and without it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5
`npx @pmndrs/docs build docs out`, so that the commands to come — `dev`, `serve` — sit next to it rather than behind it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5
`npx @pmndrs/docs build --format website` could not find Next: the app was copied to the caller's folder, and Node looks for Next by walking up from there — through folders that have no reason to contain it. It only worked when the caller happened to have Next installed too. The copy now goes beside the `node_modules` this package's own dependencies are in, wherever a package manager put them. Linking them into the caller's folder instead is not an option: Turbopack rejects a symlink that leaves its project root. Verified on the three install shapes — cold `npx`, a local install, and a run from this repo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5
npm authenticates the release with OIDC, the way `@pmndrs/uikit` and `abernier/material-theme-builder` already do, so there is no `NPM_TOKEN` secret to create, rotate or leak. Setting one would in fact break it: `changesets/action` then writes an `.npmrc` and skips OIDC. The `id-token: write` permission the job already had is what signs the publish. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the generator installable from npm, so a docs build no longer needs Docker.
--format fragment, the default, is the compiled MDX and nothing else — no layout, no stylesheet, no script — so it needs nothing but node. Mermaid stays a fenced block (server-side SVG is a follow-up) and Sandpack shows its code without the editor. Every website option (--libname,--base-path,--icon,--theme-*…) falls back to the environment variable it maps to, so Docker andbuild.ymlkeep working untouched.Notable
npx @pmndrs/docsreads the terminal. It always could, but in silence, which is indistinguishable from a hang — it now says what it expects and how to end it, on stderr so stdout stays a clean pipe.abernier/material-theme-builderis. Every website option is declared once with the environment variable the app reads it as, andOption.env()does the fallback — so--helpdocuments the pairing and the Docker image keeps working with no flags at all.node_modules, which is exactly where an installed package lives. The app is therefore copied out before building — which also simplified things:/apiis kept out of the export by not being copied, so nothing is moved aside and put back, and noexithandler is needed to survivenextBuildcallingprocess.exit()itself.bin/build.mjsis deleted. It predated the Docker image, was never usable (the package wasprivate, sonpx @pmndrs/docs buildalways 404'd), and built a server bundle rather than a static export.Tocleaves the MDX components map. The page renders it from the layout withtoc={doc.tableOfContents}; written in a doc it only ever crashed.compileMdxContenttakes an options object, so the CLI can override components — that is how a fragment keeps theh1the website draws from frontmatter.next/image→<img>inPeople.images.unoptimizedis already set, so it bought nothing but awidthrequirement that broke rendering outside Next.Verified
npm pack→ install in a third-party folder → the three modesBefore merging
NPM_TOKENmust exist as a repository secret. The release step published nothing while the package wasprivate; now it really publishes, and this PR wires the token into itsenv.🤖 Generated with Claude Code
https://claude.ai/code/session_01HfCBpbazo3fr1n2EUXNyh5