mdjango
A reusable, drop-in markdown-documentation Django app. Point it at a tree of markdown and get
a themed documentation site — served at runtime, or exported static with mdjango_build. One
opinionated house style; seven CSS seeds to make it yours. See CONTEXT.md for the glossary and
docs/adr/ for the decisions.
Status: 0.1.0 — the first public release, on PyPI (
pip install mdjango); a 0.x line, so the API may still change. Runtime serving, the render/registry core, the self-shelled theme, dark mode, code-copy, scroll-spy, MiniSearch, thellms.txtartifacts, response caching and the static export all work end-to-end. The full documentation lives insite/content/and is what thesite/project serves (ADR 0006).
Install (consumer)
pip install mdjango
Then, in a consuming Django project:
# settings.py
INSTALLED_APPS = [
# ...
"django_cotton", # mdjango's templates use cotton; this auto-wires its loader
"mdjango",
]
MDJANGO_CONTENT_DIR = BASE_DIR / "content" # your markdown tree (required)
MDJANGO_BRAND = "acme" # wordmark / <title>
MDJANGO_VERSION = "v2.3.0" # display string
MDJANGO_GITHUB_URL = "https://github.com/acme/acme"
# Also: MDJANGO_HOME_URL, MDJANGO_SITE_TITLE, MDJANGO_HEADER_LINKS, MDJANGO_DESCRIPTION,
# MDJANGO_LLM_DOCS, MDJANGO_INCLUDE_DRAFTS, MDJANGO_ALWAYS_REBUILD, MDJANGO_CACHE_SECONDS —
# see site/content/reference/settings.md.
# urls.py
urlpatterns = [
path("docs/", include("mdjango.urls")),
]
Content tree
Convention-driven, capped at three levels (section → subsection → page):
content/
_index.md # optional docs landing (served at the mount root)
about.md # optional loose page -> /docs/about/
getting-started/ # a section
_index.md # section title/weight (frontmatter only)
quickstart.md # a page -> /docs/getting-started/quickstart/
guides/
networking/ # a subsection — a nav group, not a page of its own
_index.md # subsection title/weight (frontmatter only)
ingress.md # -> /docs/guides/networking/ingress/
secrets.md
Frontmatter (a flat block of scalars): title, weight (int, orders nav), draft (bool,
hidden unless DEBUG/MDJANGO_INCLUDE_DRAFTS), description.
Pages and groups interleave by weight inside their parent, so a subsection can sit anywhere
in a curated sequence. Subsections are labels, not destinations — they get no URL, and their
_index.md supplies only a title and a weight. A group with no visible pages is omitted. A fourth
level is a build error.
Theming
The house style is fixed. A consumer sets the two ends of each colour ramp, plus an accent, a font and a base size (ADR 0003); everything between the ends is computed and locked, so the intermediate contrast steps cannot be broken:
:root {
/* surface ramp: ground -> hairline */
--background: #ffffff;
--border: #e2e6ec;
/* text ramp: full emphasis -> lowest emphasis */
--foreground: #14181f;
--foreground-subtle: #7b8494;
--accent: #2f6df6; /* optional; defaults to --foreground (monochrome) */
--font: "Inter", system-ui, sans-serif;
--font-size: 15px; /* scales the whole site */
}
Point MDJANGO_EXTRA_CSS at the stylesheet holding those :root rules and mdjango loads it after
its own sheet — no template to shadow. Dark mode is two selectors; override both. See the
Change the colours and type how-to for the full recipe.
The three interior stops — --surface, --foreground-body, --foreground-muted — are
color-mix()-derived from the ends of their own ramp and locked. Code is always mono
(--code-font), independent of --font.
The font on your own pages
mdjango self-hosts IBM Plex Mono — no CDN, works offline and in the static export. Docs pages need
nothing: the @font-face rules are inside mdjango.css. For your own templates, which mdjango
doesn't render, link the faces on their own instead of loading the font again from elsewhere:
<link rel="stylesheet" href="{% static 'mdjango/fonts.css' %}">
That is only the @font-face declarations (~2KB) — none of the docs shell. The woff2 files are
shared with mdjango.css, so the browser fetches each face once across both stylesheets. Set
--font yourself if you would rather use a different face.
Developing this package
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
./theme/build.sh # compile the stylesheets (python3 only; --watch to rebuild on change)
python manage.py runserver # serves the site/ project + its docs content
pytest # content model, render pipeline, end-to-end serving (fixture tree)
python manage.py mdjango_build --check # the content gate for site/content/
The theme source (theme/), the site/ project and tests/ are dev-only; the wheel ships
just the mdjango/ package (templates + compiled static/).
Release files for mdjango 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mdjango-0.1.0.tar.gz | 4.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mdjango-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.5 MB
Release files / mdjango-0.1.0.tar.gz
| Download URL | mdjango-0.1.0.tar.gz |
|---|---|
| Size | 4.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c02778dbe886a8dde59f8ddd933e854952d32e1b52afa8bd3a247926273787c5
|
|
BLAKE2b-256 checksum How to use checksums |
7252f54a66f5605a2267f813984e78b5da4500186be2ba9553f80f86ecdf8ac7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency logRelease files / mdjango-0.1.0-py3-none-any.whl
| Download URL | mdjango-0.1.0-py3-none-any.whl |
|---|---|
| Size | 182.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e103171342fd32ef657004a54fbaac9d01619c273b219caa093cf6eef541a448
|
|
BLAKE2b-256 checksum How to use checksums |
96e9c58c1bc67175ef364938c6444160268b731f66031ec540ebd574c89b05fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency log