A batteries-included documentation experience for MkDocs Material.
Shiki syntax highlighting Β· runnable live code Β· API-symbol hover & auto-linking Β· mkdocstrings enum/decorator badges Β· a polished collapsible TOC β all from a single plugin entry.
MkDocs Material is excellent, but a great API-docs site usually means hand-wiring a pile of hooks, JavaScript, CSS, and mkdocstrings template overrides β and then re-doing it for the next project. richdocs packages that whole experience into one configurable plugin. The default look is the VS Code "Shades of Purple" theme; everything is overridable.
- π¨ Shiki syntax highlighting β VS Code-accurate highlighting at runtime (the default theme is Shades of Purple), in both code blocks and inline code.
βΆοΈ Runnable live code β execute Python/shell blocks against a local Jupyter kernel, right in the page (dev only).- π API-symbol hover & auto-linking β inline
`Symbol`mentions link to your mkdocstrings reference automatically, and hovering symbols in code blocks shows rich tooltips; click to jump. - π·οΈ Smarter mkdocstrings rendering β enums render as
enum/member, and headings & the TOC show decorator badges (property,classmethod, β¦). - π§ Polished TOC β scroll-spy tuned for long API pages, plus collapsible sections that expand on demand.
- π One dark palette, fully themeable β override any color or the Shiki theme from
mkdocs.yml.
pip install richdocsrichdocs pulls in mkdocs-material and mkdocstrings[python], so that's all you need.
Add the plugin to mkdocs.yml β the only required option is the package you're documenting:
theme:
name: material
palette:
scheme: slate # richdocs ships a single dark scheme
plugins:
- search
- richdocs: # β list BEFORE mkdocstrings
package: your_package
- mkdocstrings:
handlers:
python:
paths: [src]That's it. The plugin registers its stylesheets/scripts, points mkdocstrings at its templates, indexes your API, and serves the Shiki theme β no extra_css / extra_javascript / custom_templates plumbing required.
Ordering: list
richdocsbeforemkdocstringsso its enum/badge templates take effect.
Everything except package is optional and has sensible defaults:
plugins:
- richdocs:
package: your_package # required: the Python package to document & index
api:
id_prefix: your_package # mkdocstrings anchor prefix (default: = package)
page_priority_overrides: {} # {"/api/reference/": 100, ...} canonical-page tuning
registry_exports: {} # {name: "pkg.module.singleton"} for registry singletons
ambiguous_short_names: [] # short names to never auto-link
prefer_class_for_short: {} # {short_name: ClassName} to disambiguate
short_name_blocklist: []
extra_modules: [] # extra submodules whose __all__ to index
section_suffixes: ["-functions", "-attributes", "-classes"]
linkify: # what / how symbols get linked
short_names: true # link `Robot`, not just `pkg.Robot`
dotted: true # resolve `fmt.joint_names` via the rightmost segment
skip_extensions: [md, py, yaml, yml, npz, json, toml, txt]
aliases: {} # {"the result": "pkg.RetargetingResult"}
symbols: # relabel / recolor API badges (override-only)
labels: {} # {function: def, attribute: var, enum: enum, ...}
colors: {} # {enum: {fg: "#7ee8d3", bg: "#7ee8d324"}, class: {fg: "#b362ff"}, ...}
decorator_labels: {} # rewrite decorator-label text {property: prop, classmethod: cls}
live_code: # runnable code blocks
enabled: true
runtime: jupyter # jupyter (local) | pyodide (browser) | auto
jupyter_url: http://127.0.0.1:8888/
token: your_package-docs # default: <package>-docs
launcher_port: 8889
launcher_script: scripts/docs-jupyter.sh
kernel: python3
runnable_languages: [python, bash]
pyodide: # in-browser Python (works on a published static site)
version: "0.27.2"
packages: [] # WASM-wheel packages to install, e.g. [numpy, pandas]
theme:
palette: {} # override any color (see Theming)
layout: {} # TOC spacing tokens
highlight: # Shiki syntax highlighting
theme: shades-of-purple # bundled id, or path to a Shiki theme JSON
inline: true # highlight inline code too
default_language: python # for untagged fenced blocks
languages: [python, bash, yaml, toml, json, markdown]
aliases: {py: python, sh: bash, yml: yaml}
toc:
collapse_default: true # nested API/category sections start collapsed
scrollspy_offset: 0 # extra px added to the active-heading threshold
features: # coarse on/off
shiki: true
api_hover: true
linkify_inline_code: true
toc_collapsible: true
toc_scrollspy: true
hide_empty_toc: trueOverride individual palette colors with friendly keys (or any raw --rd-* CSS custom property):
plugins:
- richdocs:
package: your_package
theme:
palette:
page_bg: "#1e1e3f"
code_bg: "#2d2b55"
sidebar_bg: "#222244"
gold: "#fad000" # accent
purple_bright: "#b362ff" # links
# ...or a raw token:
"--rd-text-muted": "#a599e9"Supported friendly keys: page_bg, sidebar_bg, code_bg, code_fg, surface_1βsurface_4, text, text_muted, text_soft, purple, purple_bright, gold/accent, enum_fg/enum_bg, member_fg/member_bg, and the TOC layout tokens toc_row_gap, toc_branch_gap, toc_section_gap, toc_link_min_height.
Rename or recolor the API symbol-kind badges (headings and TOC) per construct β override-only, so unset kinds keep the bundled defaults:
plugins:
- richdocs:
package: your_package
symbols:
labels:
function: def # "" empties the badge
attribute: var
enum: enum
member: member
colors:
enum: { fg: "#7ee8d3", bg: "#7ee8d324" }
class: { fg: "#b362ff" }Recognized kinds: module, class, enum, function, method, attribute,
type_alias, member. Setting a kind's color also recolors the decorator labels
that map to it (e.g. property follows attribute). Dataclasses keep griffe's
built-in dataclass label out of the box; recolor them via the class kind.
You can also rewrite the text of mkdocstrings' decorator labels (these come from griffe, so they're handled by a build-time pass rather than the badge CSS):
plugins:
- richdocs:
package: your_package
symbols:
decorator_labels: { property: prop, classmethod: cls, staticmethod: static }Runnable code blocks have two backends, chosen by live_code.runtime:
jupyter(default) β a local Jupyter kernel (full Python, any package). On a published site it shows a "needs Jupyter" notice rather than running.pyodideβ CPython compiled to WebAssembly, running in the browser (in a Web Worker, so the page stays responsive), so blocks are runnable on a published static site (e.g. GitHub Pages) with no server. The header shows a "Python Β· browser" indicator. Python only (shell blocks need Jupyter); install WASM-wheel packages vialive_code.pyodide.packages(e.g.numpy,pandasβ nottorch/mujoco).autoβ use Jupyter if it's reachable, otherwise fall back to Pyodide.
Opt into web execution deliberately. Pyodide can only run browser-compatible code β crucially, your own package usually isn't available in the browser, so blocks that
import your_packagewill fail underpyodide/auto. Use them only when your runnable blocks are stdlib- or WASM-wheel-only. That's why the default isjupyter.
plugins:
- richdocs:
package: your_package
live_code:
runtime: auto
pyodide:
packages: [numpy]A single RichDocsPlugin (MkDocs entry point) replaces what used to be four build hooks plus scattered asset wiring:
| Event | What it does |
|---|---|
on_config |
validates config, derives defaults, registers bundled CSS/JS, points mkdocstrings at the templates |
on_files |
emits richdocs-config.js (window.__richdocsConfig, read by the JS) and richdocs-palette.css (your overrides) |
on_page_markdown |
auto-links inline `Symbol` mentions to the API reference |
on_post_build |
scans the built site to index API anchors (javascripts/api-symbols.json) and injects decorator badges into the TOC |
on_serve |
runs a tiny local helper so the page can launch Jupyter for live code |
- Python 3.11+
- MkDocs Material 9.5+
- mkdocstrings[python] 0.25+ (for the API features)
git clone https://github.com/ryanrudes/richdocs
cd richdocs
uv venv && uv pip install -e ".[dev]"
uv run pytest # unit tests
uv run ruff check . # lintApache-2.0 Β© Ryan Rudes