ds deployment.pmndrs/docs
pmndrs/* projects.npm@pmndrs/docs storybook chromaticstorybook chromaticplaywright
Summary
A static MDX documentation generator, with a GitHub reusable workflow. It is primarily used for some pmndrs/* projects, but will work for anyone.

Those projects are known to be using this generator.
INSTALL
Clone the repository
$ git clone https://github.com/pmndrs/docs.git
$ cd docs
Use the right Node version
With nvm, which reads the repository's .nvmrc:
$ nvm install
$ nvm use
$ node -v # make sure your version satisfies package.json#engines.node
nb: if you want this node version to be your default nvm's one:
nvm alias default node
Install the dependencies
$ pnpm install
Configuration
Default value is always: "" (think empty).
* Required
Readers can re-seed THEME_PRIMARY, THEME_SCHEME, THEME_CONTRAST and THEME_COLOR_MATCH from the theme controls in the sidebar. An overridden control is outlined, and a double-click (or Delete) brings the site's value back.
MDX_BASEURL
Given a advanced/introduction.mdx file in the MDX folder:

becomes (for a MDX_BASEURL=http://localhost:60141 value):

http://localhost:60141 being the MDX folder served.
When deployed on GitHub Pages, MDX_BASEURL will typically value something like https://github.com/pmndrs/uikit/raw/main/docs, thanks to build.yml rule.
THEME_*
We implement m3 design system, using material-theme-builder.
This is an embed iframe of material-theme-builder's <Mtb> story.
- Material Color for more information
- The defaults are pmndrs/design-system's palette: the lime
#CAF543under color match, a warm grey neutral, and seven colors every site gets as custom colors,lime,teal,cyan,purple,red,orangeandyellow(<Color role="teal" />). THEME_PRIMARYis the seed: the secondary and tertiary colors are derived from it, unless the site gives its own (THEME_SECONDARY,THEME_TERTIARY). The neutral, neutral variant and error colors are the pmndrs ones, unless the site gives its own (THEME_NEUTRAL,THEME_NEUTRAL_VARIANT,THEME_ERROR), orautoto derive them from the primary too, as Material Theme Builder does.THEME_COLOR_MATCH(on unless"false") is Material Theme Builder's "Color match - Stay true to my color inputs": each core and custom color keeps its input's chroma instead of being toned down to the scheme's, soTHEME_SCHEMEis ignored and the scheme toggle is hidden while color match is on. Readers can toggle color match from the sidebar. A custom color with:blendis still harmonized first.
Keep your previous look
Up to v4.19, a site's palette was the one its primary alone made (#323e48 under tonalSpot by default). Since v4.20 the defaults are the pmndrs palette, so a site that pinned only its primary now gets it under color match, on the pmndrs neutrals. To keep the previous look exactly, pin color match off and let the primary derive the neutrals and the error color again:
uses: pmndrs/docs/.github/workflows/build.yml@v4
with:
theme_primary: '#323e48' # yours
theme_scheme: 'tonalSpot'
theme_color_match: 'false'
theme_neutral: 'auto'
theme_neutral_variant: 'auto'
theme_error: 'auto'
or, for a workflow that sets the environment itself:
env:
THEME_PRIMARY: '#323e48' # yours
THEME_SCHEME: 'tonalSpot'
THEME_COLOR_MATCH: 'false'
THEME_NEUTRAL: 'auto'
THEME_NEUTRAL_VARIANT: 'auto'
THEME_ERROR: 'auto'
The palette is then the v4.19 one, variable for variable. The alert colors are unchanged, so their THEME_NOTE… pins can stay or go. What is new either way: the seven pmndrs colors (--md-sys-color-lime… and their bg-lime… utilities), which nothing renders unless a page asks for one (<Color role="teal" />).
VERSION_*, LIB_VERSION, TAG_MATCH
- Label:
git describe --tags(eg.v10.7.9,v10.7.9-3-ga1b2c3d), or<branch>@<shortsha>without tags.LIB_VERSIONoverrides it. TAG_MATCHnarrows the tags:
VERSION_URL_TEMPLATEenables the switcher: the label lists the branches, each leading to the same page on its deployment. Full public URL (base path included), with a placeholder:
VERSION_PRODUCTION_BRANCHleads toVERSION_PRODUCTION_URL; every other branch shows a banner linking to production.- Branches: the remote's, minus
dependabot/,renovate/,changeset-release/.VERSION_BRANCHESreplaces that filter,VERSION_BRANCHES_LISTthe whole list. Read at build time.
- The per-branch URLs are yours to create: Vercel's Git integration does,
vercel deploydoes not (vercel alias set, seeci.yml). - Git needs full history and tags (
fetch-depth: 0).
Usage
dev
This is dev.sh. The site listens on PORT (default 3000), the MDX folder on any free port, so several checkouts can run side by side:
$ (
trap 'kill -9 0' SIGINT
export PORT=${PORT:-3000}
export _PORT=$(node -e 'const s = require("net").createServer().listen(0, () => { console.log(String(s.address().port)); s.close() })')
export MDX=docs
export NEXT_PUBLIC_LIBNAME="Poimandres"
export NEXT_PUBLIC_LIBNAME_SHORT="pmndrs"
export BASE_PATH=
export DIST_DIR=
export OUTPUT=
export HOME_REDIRECT=
export MDX_BASEURL=http://localhost:$_PORT
export SOURCECODE_BASEURL="vscode://file$(pwd)"
export EDIT_BASEURL="vscode://file$(pwd)/docs"
export NEXT_PUBLIC_URL=
export ICON=
export LOGO=gutenberg.jpg
export GITHUB=https://github.com/pmndrs/docs
export DISCORD=https://discord.com/channels/740090768164651008/1264328004172255393
export CONTRIBUTORS_PAT=
npx serve $MDX -p $_PORT --no-port-switching --no-clipboard &
pnpm run dev &
wait
)
Then go to: http://localhost:3000
If HOME_REDIRECT= empty, / will not redirect, and instead displays an index of libraries.
build
This is start.sh, same ports:
$ (
trap 'kill -9 0' SIGINT
# `next dev` leaves types pointing at `src/app/api`, which the export build moves aside
rm -rf out .next/dev/types
export PORT=${PORT:-3000}
export _PORT=$(node -e 'const s = require("net").createServer().listen(0, () => { console.log(String(s.address().port)); s.close() })')
export MDX=docs
export NEXT_PUBLIC_LIBNAME="Poimandres"
export NEXT_PUBLIC_LIBNAME_SHORT="pmndrs"
export BASE_PATH=
export DIST_DIR=
export OUTPUT=export
export HOME_REDIRECT=
export MDX_BASEURL=http://localhost:$_PORT
export SOURCECODE_BASEURL=
export EDIT_BASEURL=
export NEXT_PUBLIC_URL=
export ICON=
export LOGO=gutenberg.jpg
export GITHUB=https://github.com/pmndrs/docs
export DISCORD=https://discord.com/channels/740090768164651008/1264328004172255393
export CONTRIBUTORS_PAT=
pnpm run build
npx serve $MDX -p $_PORT --no-port-switching --no-clipboard &
npx serve out -p $PORT &
wait
)
CLI
No clone, no install — the published CLI does the same build:
$ cd ~/code/pmndrs/react-three-fiber
$ (
trap 'kill -9 0' SIGINT
export PORT=${PORT:-3000}
export _PORT=$(node -e 'const s = require("net").createServer().listen(0, () => { console.log(String(s.address().port)); s.close() })')
export NEXT_PUBLIC_LIBNAME="React Three Fiber"
export NEXT_PUBLIC_LIBNAME_SHORT="r3f"
export BASE_PATH=/toto
export HOME_REDIRECT=/getting-started/introduction
export MDX_BASEURL=http://localhost:$_PORT
export ICON=🇨🇭
export GITHUB=https://github.com/pmndrs/react-three-fiber
npx -y @pmndrs/docs@latest build docs docs/out --format website
npx serve docs -p $_PORT --no-port-switching --no-clipboard &
npx -y serve docs/out -p $PORT &
wait
)
Then go to: http://localhost:3000
Every option above has a flag too — npx @pmndrs/docs@latest build --help lists them.
Agents
llms.txt dumps and the pmndrs MCP server moved to their own page: Agents.