Sumi
A modern, local-first documentation theme for MkDocs. Markdown in, beautiful docs out.
November 2, 2026
Sumi documentation#
Sumi is a documentation theme for MkDocs. It turns a folder of plain Markdown into a complete, searchable, printable website — with a home page, a blog and release notes — without Node.js, a CDN or a hosted service.
- Install Add Sumi to an existing MkDocs project in two lines of configuration.
- Write Sixteen components, all written in native Markdown.
- Pages Landing pages, a blog, release downloads and a print edition.
- Customize Colours, navigation, search, languages, versions and template overrides.
What you get#
| Area | Included |
|---|---|
| Reading | Top tabs, collapsible sidebar, breadcrumbs, table of contents with scroll spy, previous / next |
| Discovery | Local full-text search with CJK support, merged with a command palette (⌘ K) |
| Authoring | Callouts, tabs, steps, cards, fields, captions, galleries, badges, file trees, Mermaid, ECharts, mind maps, KaTeX, keys, 1 900+ icons |
| Pages | Data-driven landing page, blog with tags and Atom feed, releases with checksums, whole-site print edition |
| Outputs | HTML, print, a Markdown copy of every page, llms.txt, Atom |
| Operations | Dark mode, mike versions, language switcher, edit links, Git "last updated" dates, analytics |
Principles#
Sumi follows four rules. They are explained in Design principles.
- Content stays Markdown. A component is a blockquote, a list, a table or a fence with one attribute line.
- Everything is local. Every font, icon and script is part of your built site.
- Pay only for what you use. Diagrams and formulas load their runtime on the pages that contain them.
- People and agents are both readers. Every page is published as HTML and as Markdown.
Install#
Sumi is a Python package. It depends on MkDocs NG, so installing Sumi installs MkDocs too.
Requirements#
- Python 3.10 or newer
- An MkDocs project, or an empty folder
Install the package#
pip install mkdocs-sumi
uv add --dev mkdocs-sumi
pipx install mkdocs-ng --include-deps
pipx inject mkdocs-ng mkdocs-sumi
Enable the theme and the plugin#
Sumi has two halves: the theme draws the page and the plugin adds the Markdown components, the blog and the extra outputs. Enable both:
theme:
name: sumi
plugins:
- search # powers ⌘K search
- llms_txt # optional: llms.txt and a Markdown copy of every page
- sumi
Important
When you add a plugins: list, MkDocs no longer enables search for you. List it explicitly, as above.
Preview#
mkdocs serve
Open http://127.0.0.1:8000/. Press ⌘ K (or Ctrl K) to search.
Next#
Continue with Your first site, or jump to Writing.
Your first site#
This page builds a small site from scratch: a home page, two guide pages and a blog post.
-
Create the project
mkdocs new my-docs cd my-docs -
Lay out the files
my-docs/
docs/
- index.mdHome page
guide/
- index.mdSection landing, linked from the sidebar title
- install.md
blog/
posts/
- hello.mdAny page under blog/ is a post
- mkdocs.ymlSite configuration
-
Configure the site
mkdocs.ymlsite_name: My Docs site_url: https://example.com/ repo_url: https://github.com/me/my-docs theme: name: sumi plugins: - search - sumi -
Preview and build
mkdocs serve # live preview mkdocs build # writes static files to site/
How navigation is built#
Top-level sections become tabs in the header. Pages inside the active tab appear in the sidebar. A section whose first page is index.md uses that page as its landing: the section title links to it.
Tip
Without a nav: key, MkDocs builds the navigation from your folders. Blog posts are kept out of the sidebar automatically.
Deploy#
mkdocs build writes plain HTML, CSS and JavaScript to site/. Upload that folder to any static host — GitHub Pages, Cloudflare Pages, Netlify, an S3 bucket or an Nginx server. Nothing needs to run after deployment.
mkdocs gh-deploy # build and push to the gh-pages branch
Migrate from Material#
Sumi reads the content and most of the configuration written for Material for MkDocs. Switching is usually a two-line change.
theme:
- name: material
+ name: sumi
plugins:
- search
+ - sumi
What keeps working#
| You use | In Sumi |
|---|---|
admonition, !!! note and ??? note |
Styled as callouts, same colours as > [!NOTE] |
pymdownx.details, pymdownx.tabbed (alternate style) |
Styled natively |
pymdownx.superfences, pymdownx.highlight |
Supported; Sumi then leaves highlighting to them |
pymdownx.arithmatex |
Rendered with the bundled KaTeX |
pymdownx.keys |
Kept; Sumi's own ++Ctrl+K++ steps aside |
extra.social, extra.alternate, extra.analytics |
Read as-is, including fontawesome/brands/… icon names |
mkdocstrings API pages |
Styled: signatures, symbol badges (show_symbol_type_heading), labels, nested members, backlinks and source panels. Recommended options |
Plugin CSS that uses Material's --md-* colours or [data-md-color-scheme] |
Follows Sumi's palette and dark mode. Authors: make your plugin work with Sumi |
mike versions |
theme.version_provider: mike |
Blog: blog/posts/, .authors.yml, date, authors, tags, <!-- more --> |
Read as-is |
hide: [toc, navigation] front matter |
hide: [toc, sidebar] |
What changes#
- Material's
theme.featuresandtheme.palettelists are ignored. Use theme options instead. - Icons written as
:material-…:or:octicons-…:need thepymdownx.emojiextension as before. Sumi's own inline icons are:lucide-name:and:brand-name:. - Material Insiders features are not emulated.
Note
If a page looks wrong after switching, open an issue with the Markdown that causes it.
Writing with Sumi#
Sumi adds sixteen components to Markdown. None of them introduces new syntax that breaks on GitHub: each one is a blockquote, a list, a table, an image or a code fence, sometimes followed by a single attribute line in braces.
1. Install the theme
2. Enable the plugin
{.steps}
If a renderer does not know Sumi, the reader still sees a numbered list. Sumi sees a procedure.
Components#
| Component | Written as | Page |
|---|---|---|
| Callout | > [!NOTE], > [!TIP]- |
Callouts |
| Code block with title | ```py title="app.py" |
Code blocks |
| Tabs | ```sh {tab="Linux"} |
Tabs |
| Steps | ordered list + {.steps} |
Steps |
| Cards | bullet list of links + {.cards} |
Cards |
| Fields | table + {.fields} |
Fields |
| Table options | table + {.wide}, {caption="…"} |
Tables |
| Figure | image + {caption="…"} |
Images |
| Gallery | list of images + {.gallery} |
Galleries |
| File tree | ```filetree |
File trees |
| Diagram | ```mermaid |
Diagrams |
| Chart | ```echarts (YAML or JSON) |
Charts |
| Mind map | ```markmap |
Mind maps |
| Math | $…$, $$…$$ |
Math |
| Keys and icons | ++Ctrl+K++, :lucide-rocket: |
Keys and icons |
| Badge | **New**{.badge} |
Badges |
Attribute lines#
An attribute line is a paragraph that contains only {…} and sits directly under the block it modifies. It can carry classes, an id and key–value pairs:
| Name | Type |
| --- | --- |
| `port` | integer |
{.fields #server-options caption="Server options"}
Classes that Sumi does not recognise are kept, so you can style blocks yourself in extra_css.
Markdown extensions#
The sumi plugin enables these Python-Markdown extensions for you, so they need no configuration:
attr_list,md_in_html,tables,toc(with permalinks),footnotes,def_list,admonitionfenced_codeandcodehilite— unlesspymdownx.superfencesandpymdownx.highlightare configured, in which case Sumi leaves code to them
Any other extension in markdown_extensions continues to work.
Tip
Every page of this site was written with these components. Click Copy as Markdown at the top of a page to see its source.
Callouts#
A callout draws attention to information that should not be missed. Sumi uses the syntax GitHub introduced for alerts, so callouts render on GitHub, in your editor's preview and in Sumi alike.
> [!NOTE]
> Sumi reads the same syntax GitHub does.
Note
Sumi reads the same syntax GitHub does.
The five types#
Note
Useful information that users should know, even when skimming.
Tip
Helpful advice for doing things better or more easily.
Important
Key information users need to achieve their goal.
Warning
Urgent information that needs immediate attention to avoid problems.
Caution
Advises about risks or negative outcomes of certain actions.
> [!NOTE]
> [!TIP]
> [!IMPORTANT]
> [!WARNING]
> [!CAUTION]
Sumi also accepts the type names used by Material for MkDocs — info, success, question, failure, danger, bug, example, quote, abstract and todo — each mapped to the closest colour.
Custom titles#
Text after the marker replaces the default title:
> [!WARNING] Back up first
> `mkdocs gh-deploy --force` replaces the whole `gh-pages` branch.
Back up first
mkdocs gh-deploy --force replaces the whole gh-pages branch.
Collapsible callouts#
Add - after the marker for a callout that starts closed, or + for one that starts open but can be closed.
> [!TIP]- Why is it called Sumi?
> *Sumi* (墨) is the ink of East Asian calligraphy: one colour, many shades.
Why is it called Sumi?
Sumi (墨) is the ink of East Asian calligraphy: one colour, many shades.
Collapsed callouts are printed open.
Rich content#
A callout can hold anything a blockquote can — lists, code, tables, even other callouts.
Before you upgrade
1. Read the release notes.
2. Pin the version in requirements.txt:
```text
mkdocs-sumi==0.1.0
```
Admonitions#
If your pages already use the admonition extension, they keep working and look the same:
!!! warning "Deprecated"
Use `theme.palette` instead.
Deprecated
Use theme.palette instead.
??? note and ???+ note from pymdownx.details are styled as collapsible callouts.
Code blocks#
Fenced code blocks are highlighted at build time by Pygments. Every block gets a copy button; no JavaScript highlighter is shipped.
def greet(name: str) -> str:
return f"Hello, {name}!"
File names#
Add title="…" after the language to show a file name above the code:
```toml title="pyproject.toml"
[project]
name = "my-docs"
dependencies = ["mkdocs-sumi"]
```
[project]
name = "my-docs"
dependencies = ["mkdocs-sumi"]
Line numbers and highlighted lines#
These options come from Python-Markdown's codehilite, or from pymdownx.highlight if you use it:
```python hl_lines="2 3"
def total(items):
subtotal = sum(i.price for i in items)
return subtotal * 1.2
```
def total(items):
subtotal = sum(i.price for i in items)
return subtotal * 1.2
To show line numbers on every block, configure the extension:
markdown_extensions:
- codehilite:
linenums: true
Shell sessions#
Use console for transcripts with prompts and output. The copy button then copies only the commands — without the $ prompts and without the output.
$ mkdocs build
INFO - Building documentation to directory: site
INFO - Documentation built in 0.42 seconds
Diffs#
theme:
- name: mkdocs
+ name: sumi
Inline code#
Write inline code with single backticks: mkdocs serve. In prose, Sumi gives inline code a quiet background that works in light and dark mode.
Tip
To show tabs with one variant per operating system or language, see Tabs.
Tabs#
Consecutive code fences that each carry {tab="…"} become one tab set. Without Sumi, they are simply consecutive code blocks with a hint of what each one is for.
```sh {tab="macOS"}
brew install python
```
```sh {tab="Ubuntu"}
sudo apt install python3
```
```powershell {tab="Windows"}
winget install Python.Python.3.12
```
brew install python
sudo apt install python3
winget install Python.Python.3.12
Synchronised tabs#
Tab sets with the same labels stay in sync: choose Windows once and every matching tab set on every page follows. The choice is remembered in the browser.
python3 -m venv .venv && source .venv/bin/activate
python3 -m venv .venv && source .venv/bin/activate
py -m venv .venv; .venv\Scripts\Activate.ps1
Titles inside tabs#
title and tab combine:
```yaml {tab="YAML" title="mkdocs.yml"}
theme: sumi
```
```toml {tab="TOML" title="pyproject.toml"}
[tool.mkdocs]
theme = "sumi"
```
theme:
name: sumi
[tool.mkdocs]
theme = "sumi"
Tabs with prose#
For tabs that contain more than code, use pymdownx.tabbed with alternate_style: true. Sumi styles it to match:
markdown_extensions:
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
In print and in the Markdown copy, every tab is shown one after another under its label.
Steps#
Put {.steps} under an ordered list to draw it as a procedure: numbered markers joined by a line, with each step's first line as its heading.
1. Create a virtual environment
```sh
python -m venv .venv
```
2. Install Sumi
```sh
pip install mkdocs-sumi
```
3. Start the preview server
Run `mkdocs serve` and open the address it prints.
{.steps}
-
Create a virtual environment
python -m venv .venv -
Install Sumi
pip install mkdocs-sumi -
Start the preview server
Run
mkdocs serveand open the address it prints.
Short steps#
Steps do not need bodies:
- Write
- Preview
- Publish
Guidelines#
- Start each step with a verb.
- Keep one action per step. If a step needs a list of its own, it is probably two steps.
- Indent step content by four spaces so it stays inside the step.
Cards#
A bullet list of links followed by {.cards} becomes a grid. The link is the card title; the text after it is the description; an inline icon at the start becomes the card icon.
- :lucide-rocket: [Install](../start/install.md)
Add Sumi to a project.
- :lucide-pen-line: [Write](index.md)
Components in plain Markdown.
- :lucide-palette: [Customize](../customize/theme.md)
Colours, fonts and layout.
{.cards}
- Install Add Sumi to a project.
- Write Components in plain Markdown.
- Customize Colours, fonts and layout.
Columns#
Cards fill as many columns as fit, up to three. Set cols to fix the number:
{.cards cols=2}
- Source code Report a bug or send a pull request.
- PyPI Release history and downloads.
Cards without links#
Items without a link render as static cards — useful for a list of features:
- Local-first Every asset ships with your site.
- Fast No framework; one small script.
- Printable A whole-site print edition.
Fields#
Configuration options and API parameters are usually written as a table, and tables become hard to read once descriptions grow. Add {.fields} and Sumi turns each row into a reference entry: the name and its type, default and required flag on one line, the description underneath.
| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `site_name` | string | | yes | The title of the site, used in the header and in every page title. |
| `site_url` | string | `null` | | The canonical address of the built site. Required for the sitemap, the feed and `llms.txt`. |
| `strict` | boolean | `false` | | Stop the build on any warning, such as a broken link. |
{.fields}
site_namestringrequiredsite_urlstringDefault nullllms.txt.strictbooleanDefault falseWhich columns are metadata#
The first column is always the name. Columns headed Type, Default and Required (or 类型, 默认值, 必填) become chips next to it. Every other column becomes part of the description.
To choose the metadata columns yourself, list their headings in meta:
{.fields meta="type since"}
idintegerSince 1.0assetslistSince 1.2Linking to a field#
Each entry keeps the content of its name cell, so you can add an anchor with attr_list:
| `strict`{#strict} | boolean | `false` | Stop on warnings. |
The Markdown copy of the page keeps the original table.
Tables#
Tables are standard GitHub-flavoured Markdown. Sumi gives them a frame, sticky headers and horizontal scrolling on narrow screens.
| Command | Purpose |
|---|---|
mkdocs new |
Create a new project |
mkdocs serve |
Start a live-reloading preview |
mkdocs build |
Build the site |
mkdocs gh-deploy |
Build and deploy to GitHub Pages |
Captions#
| Plan | Price |
| --- | --- |
| Open source | Free |
{caption="Sumi is licensed under Apache 2.0."}
| Plan | Price |
|---|---|
| Open source | Free |
Variants#
Add one or more of these classes on the attribute line:
| Class | Effect |
|---|---|
.wide |
Let the table use the full width of the content area |
.compact |
Reduce cell padding for dense data |
.striped |
Shade alternate rows |
.center |
Centre all cells |
{.compact .striped caption="Variants"}
Alignment#
Column alignment uses the standard colon syntax:
| Left | Centre | Right |
| :--- | :---: | ---: |
| a | b | 1.00 |
| Left | Centre | Right |
|---|---|---|
| a | b | 1.00 |
| longer text | c | 120.50 |
Tip
For option and parameter references, use fields instead of a wide table.
Images#
Images use standard Markdown. Paths are relative to the page, so the image works in your editor, on GitHub and on the site.

Figures#
Add {caption="…"} on the line after an image to turn it into a figure:

{caption="The Sumi mark: an ink stroke and a vermilion seal."}
Zoom#
Click an image in the content to view it full-screen; press Esc or click again to close. Images inside links and cards are not zoomable. To opt an image out, give it the no-zoom class:
{.no-zoom}
Light and dark variants#
Append #only-light or #only-dark to an image URL to show it in one colour scheme only:


Size and placement#
With attr_list, set the width or float an image:
{width="120"}
{.left width="320"}
Note
Images are lazy-loaded. Give large images explicit width and height attributes to avoid layout shift.
Galleries#
A bullet list of images followed by {.gallery} becomes an image grid. Text after an image is its caption. Click an image to see it full size.
- 
Home page
- 
Dark mode
- 
Documentation
- 
Documentation, dark
- 
Blog
- 
Releases and downloads
{.gallery}
Home page
Dark mode
Documentation
Documentation, dark
Blog
Releases and downloads
On GitHub and in the Markdown copy of the page, this is still a list of images with captions.
Columns and shape#
The grid fits as many columns as there is room for, up to three. Set cols to change that. Images are cropped to 4:3; set --ratio in style for another shape:
- 
- 
{.gallery cols="2" style="--ratio: 16 / 10"}
Light
Dark
Links#
Wrap an image in a link to open a page instead of the full-size image:
- [](../pages/blog.md)
Blog layout
{.gallery}
File trees#
A filetree fence turns a directory listing into a navigable tree with folder and file icons. Paste the output of tree, or write the structure with plain indentation.
```filetree
my-docs/
├── docs/ # Markdown sources
│ ├── index.md
│ ├── guide/
│ │ ├── install.md
│ │ └── usage.md
│ └── assets/
│ └── logo.svg
├── overrides/ # Template overrides (optional)
│ └── main.html
├── pyproject.toml
└── mkdocs.yml # Site configuration
```
my-docs/
docs/Markdown sources
- index.md
guide/
- install.md
- usage.md
assets/
- logo.svg
overrides/Template overrides (optional)
- main.html
- pyproject.toml
- mkdocs.ymlSite configuration
Rules#
- A name ending in
/, or a name with children, is a folder. Click a folder to fold it. - Text after
#is a comment and is aligned in a column. - File icons follow the extension: code, text, configuration, data, images and archives.
- Indentation by spaces works as well as box-drawing characters.
Plain indentation#
```filetree title="Package layout"
src/
mkdocs_sumi/
plugin.py # The MkDocs plugin
theme/ # Jinja templates and assets
tests/
```
src/
mkdocs_sumi/
- plugin.pyThe MkDocs plugin
- theme/Jinja templates and assets
- tests/
The tree is built at build time from plain HTML <details> elements, so it needs no JavaScript and prints fully expanded.
Diagrams#
A mermaid fence is drawn as a diagram by Mermaid. Sumi bundles Mermaid with the theme and loads it only when a diagram is about to scroll into view. Mermaid is split by diagram type, so a page with one flowchart downloads the flowchart code and nothing else. Other pages do not pay for it at all.
```mermaid
flowchart LR
md[Markdown] --> mk{MkDocs}
mk --> html[HTML pages]
mk --> txt[llms.txt]
mk --> copy[Markdown copies]
```
flowchart LR
md[Markdown] --> mk{MkDocs}
mk --> html[HTML pages]
mk --> txt[llms.txt]
mk --> copy[Markdown copies]Diagrams follow the colour scheme: switch to dark mode and they are redrawn.
Hover a diagram (or tap it on a phone) for three buttons: open it full screen, copy its Mermaid source, and download it as an SVG file. Until a diagram is drawn, and in a browser without JavaScript, readers see its source text.
Sequence diagrams#
sequenceDiagram participant B as Browser participant S as Static host B->>S: GET /guide/ S-->>B: index.html B->>S: GET /search/search_index.json (on ⌘K) S-->>B: JSON index Note over B: Search runs locally
Captions#
A title on the fence becomes the caption:
```mermaid title="Build pipeline"
flowchart LR
a --> b
```
stateDiagram-v2 direction LR [*] --> Config Config --> Files Files --> Nav Nav --> Pages Pages --> [*]
Note
Mermaid is loaded as a JavaScript module, which browsers do not allow from file:// pages. Open the built site through a web server (mkdocs serve, python -m http.server) or any static host.
Note
If you use pymdownx.superfences with a custom fence for Mermaid, remove it: Sumi handles mermaid fences itself. To turn Sumi's handling off, set mermaid: false on the plugin.
Charts and mind maps#
Two more fences draw pictures from text. Like diagrams, their scripts ship with Sumi and load only when a chart or mind map is about to scroll into view.
Charts#
An echarts fence holds an Apache ECharts option, written in YAML or JSON. Sumi checks it when the site is built: a fence that is not valid YAML or JSON is left as a code block and the build prints a warning.
```echarts title="Build time for 1 000 pages (seconds)"
xAxis:
type: category
data: [MkDocs 1.6, MkDocs NG, NG + Sumi]
yAxis:
type: value
series:
- type: bar
data: [18.1, 8.4, 12.0]
```
xAxis:
type: category
data: [MkDocs 1.6, MkDocs NG, NG + Sumi]
yAxis:
type: value
series:
- type: bar
data: [18.1, 8.4, 12.0]Charts use the site's accent colour first, follow the colour scheme, and redraw when the page is resized. Set height on the fence to change the height in pixels (the default is 360):
```echarts height="240"
series:
- type: pie
radius: ["45%", "70%"]
data:
- {name: Markdown, value: 60}
- {name: Templates, value: 15}
- {name: Search index, value: 16}
- {name: Other, value: 9}
```
tooltip: {}
series:
- type: pie
radius: ["45%", "70%"]
data:
- {name: Markdown, value: 60}
- {name: Templates, value: 15}
- {name: Search index, value: 16}
- {name: Other, value: 9}Without JavaScript, and in the Markdown copy of the page, readers see the YAML source.
Note
Options are data, not code: functions such as custom formatter callbacks cannot be written in YAML or JSON. Use template strings like formatter: "{b}: {c}" instead.
Mind maps#
A markmap fence draws a mind map from headings and nested lists. Click a node to fold or unfold its branch; drag to pan and scroll to zoom.
```markmap height="360"
# Sumi
## Write
- Callouts
- Tabs and steps
- Diagrams and charts
## Publish
- Landing page
- Blog
- Releases
## Read
- ⌘K search
- Dark mode
- Print edition
```
Sumi
- Write
- Callouts
- Tabs and steps
- Diagrams and charts
- Publish
- Landing page
- Blog
- Releases
- Read
- ⌘K search
- Dark mode
- Print edition
The outline is turned into a tree when the site is built. Without JavaScript, in print and in the Markdown copy, readers see it as a nested list.
Turning them off#
If another plugin already handles echarts or markmap fences, switch Sumi's handling off:
plugins:
- sumi:
echarts: false
markmap: false
Math#
Write TeX between dollar signs. Sumi typesets it with a bundled copy of KaTeX, loaded only on pages that contain a formula.
Inline formulas#
Euler's identity, $e^{i\pi} + 1 = 0$, links five constants.
Euler's identity, e^{i\pi} + 1 = 0, links five constants.
A dollar sign followed by a space, or preceded by a letter or digit, is not math — so prices such as $5 or $10 stay as written. Escape a literal dollar sign as \$.
Display formulas#
$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x)\, e^{-2\pi i x \xi}\, dx
$$
Multi-line environments work too:
Compatibility#
If pymdownx.arithmatex is configured with generic: true, Sumi renders its output with the same KaTeX and turns its own $ parsing off. To disable math entirely, set math: false on the plugin.
Keys and icons#
Keyboard shortcuts#
Wrap a key combination in double plus signs:
Press ++Ctrl+Shift+P++ to open the command palette, or ++Esc++ to close it.
Press Ctrl+Shift+P to open the command palette, or Esc to close it.
For a single key in running text, plain <kbd> also works: Enter.
Icons#
Write :lucide-name: to insert any of the 2 000 Lucide icons, or :brand-name: for a brand logo. Icons are inlined as SVG at build time and take the colour of the surrounding text.
:lucide-rocket: Launch · :lucide-shield-check: Secure · :lucide-languages: Translated
Launch · Secure · Translated
Unknown names are left as text, so a typo is easy to spot.
Brand icons#
| Icon | Name | Icon | Name |
|---|---|---|---|
brand-github |
brand-gitlab |
||
brand-gitee |
brand-codeberg |
||
brand-python |
brand-pypi |
||
brand-docker |
brand-rust |
||
brand-x |
brand-mastodon |
||
brand-bluesky |
brand-discord |
||
brand-wechat |
brand-zhihu |
||
brand-bilibili |
brand-xiaohongshu |
||
brand-youtube |
brand-rss |
The full set also includes bitbucket, telegram, reddit, stackoverflow, npm, go, postgresql, markdown, readthedocs, matrix, qq, medium, devdotto, substack, facebook and instagram.
In templates and front matter#
The same names are used everywhere Sumi accepts an icon: landing page features, buttons, social links and cards.
extra:
social:
- icon: brand-github
link: https://github.com/mkdocs-ng/mkdocs-sumi
Badges#
Put {.badge} right after bold or italic text to turn it into a badge. This is the standard attr_list syntax, so a renderer that does not know Sumi still shows the word in bold.
## Command palette **New**{.badge .new}
| Option | Status |
| --- | --- |
| `palette` | **Stable**{.badge .tip} |
| `brand` | **Beta**{.badge .warning} |
| `legacy_nav` | **Deprecated**{.badge .deprecated} |
| Option | Status |
|---|---|
palette |
Stable |
brand |
Beta |
legacy_nav |
Deprecated |
Tones#
A second class picks the colour. Without one, a badge is neutral grey.
| Class | Badge |
|---|---|
| (none) | Default |
.note, .info |
Note |
.tip, .success |
Stable |
.important, .new |
New |
.warning |
Beta |
.caution, .danger, .deprecated |
Removed |
.accent |
Sumi |
In headings#
A badge in a heading shrinks to fit, and stays out of the table of contents text.
Version 0.2 Latest#
## Version 0.2 **Latest**{.badge .tip}
Landing pages#
A landing page is a Markdown file with template: landing.html. Its sections are described in front matter, so the page stays data you can review in a diff — no HTML, no components to import. The home page of this site is one.
---
template: landing.html
hero:
eyebrow: "v2.0 is out"
title: "Ship docs<br>*people read*."
text: One sentence that explains the product.
actions:
- {text: Get started, link: guide/, icon: rocket, primary: true}
- {text: GitHub, link: "https://github.com/me/project", icon: brand-github}
install: pip install my-project
sections:
- type: features
title: Why it is different
items:
- {icon: zap, title: Fast, text: Builds in under a second., link: guide/speed/}
- {icon: lock, title: Private, text: Runs entirely on your machine.}
- type: cta
title: Ready?
actions:
- {text: Read the guide, link: guide/, primary: true}
---
A link to a Markdown file, such as guide.md or guide/speed.md#cache, leads to the page built from it. Like a Markdown link, it is relative to the landing page, and the build warns when the file doesn't exist. Other links are relative to the site root, like nav entries. Text fields accept inline Markdown.
Hero#
eyebrowstringtitlestring<br> breaks lines; *emphasis* is drawn in the accent colour. Defaults to the page title.textstringsite_description.actionslisttext, link, icon, primary.installstringcodeobjecttitle, lang, content.imagestringcode.Sections#
Every entry in sections has a type and optional eyebrow, title, text, id and tone: soft (a tinted background).
featuresKeys items, columnsicon, title, text and an optional link.splitKeys bullets, actions, code or image or markdown, reversestatsKeys itemsvalue and label.stepsKeys itemstitle and text.faqKeys itemsq and a (Markdown).ctaKeys actionscontentsections is omitted, the body is shown under the hero.Tip
Keep the page body for content that agents and search should see. The front-matter sections are visual; the Markdown copy of a landing page contains its title, text and body.
Blog#
Every Markdown file under docs/blog/ is a post. Sumi generates the blog index, keeps posts out of the sidebar, and writes an Atom feed. You don't need another plugin.
docs/
blog/
- .authors.ymloptional: author names and avatars
- index.mdoptional: the blog's title and intro
posts/
- 2026-11-01-introducing-sumi.md
- 2026-11-15-search.md
See it on this site's blog.
Writing a post#
---
date: 2026-11-01
authors: [xianpeng]
tags: [release, design]
description: Sumi 0.1 is out.
---
# Introducing Sumi
The first paragraph is the excerpt shown on the index.
<!-- more -->
The rest of the post.
datedateupdateddateauthorslist.authors.yml, or plain names.tagslistcategories is accepted too.descriptionstring<!-- more --> is used.imagestringdocs/. Shown at the top of the post, on its card in the card view, and as the preview when the post is shared. Set hide_cover: true to keep it off the post itself.draftbooleanAuthors#
authors:
xianpeng:
name: Xianpeng Shen
description: Maintainer
avatar: https://github.com/shenxianpeng.png
url: https://github.com/shenxianpeng
The format is the same as the Material blog plugin's, so an existing file can be reused.
The index page#
Without docs/blog/index.md, Sumi creates one titled "Blog". To add an introduction, create the file; any Markdown in it is shown above the list of posts. Tag filters appear when posts have tags, and a filter can be linked to as blog/#tag=release.
List and card views#
The index shows posts as a list, or as a grid of cards with cover images. Readers can switch with the buttons next to the tag filters; their choice is remembered. Pick the default with blog.layout:
plugins:
- sumi:
blog:
layout: cards # or list (the default)
A post without an image gets a cover drawn from the accent colour and its first tag.
Feed#
An Atom feed is written to blog/feed.xml and linked from every page's <head>, so feed readers find it from any URL. It needs site_url to produce absolute links.
Options#
plugins:
- sumi:
blog:
dir: blog # folder under docs/ that contains posts
feed: true
feed_items: 20
layout: list # or cards
Releases#
A page with template: releases.html lists versions with their notes and downloadable assets. Each asset shows its size and a SHA-256 checksum that copies on click. See this site's releases.
---
template: releases.html
releases: releases.yml
---
# Releases
Download the latest version, or browse the history.
releases is either a list in the front matter or a path to a YAML file relative to docs/.
- version: 0.2.0
date: 2026-12-01
summary: Versioned docs and a Japanese interface.
notes: |
- **New:** `version_provider: mike`.
- **Fixed:** tabs no longer jump when switching.
assets:
- name: mkdocs_sumi-0.2.0-py3-none-any.whl
url: https://example.com/mkdocs_sumi-0.2.0-py3-none-any.whl
size: 1.9 MB
sha256: 9f2c0a…
- version: 0.1.0
date: 2026-11-01
notes: First release.
Fields#
versionstringreleases/#v0-2-0 links to it.titlestringdatedatestatusstringlatest, stable, prerelease or archived. The first release defaults to latest.summarystringnotesstringassetslistname, url, size, sha256.The page's own Markdown is shown above the list, so you can add installation instructions or a support policy.
Print edition#
Sumi writes the whole site as one long page at /print/, in navigation order, with a cover and a table of contents. Open it and print to PDF for an offline manual. Open this site's print edition.
Every page also prints cleanly on its own: the header, sidebars and buttons are hidden, collapsed callouts are expanded, tabs are shown one after another, and links show their address.
How it works#
The print edition is built from the same HTML as the pages, after they are rendered. Links between pages become links within the document; images and assets are rebased so they resolve from /print/.
Options#
plugins:
- sumi:
print:
enabled: true
path: print/
Use the Print command in the page actions menu, or press ⌘ K and type > print.
Tip
To leave a page out of the print edition, add print: false to its front matter.
Theme options#
Theme options are set under theme: in mkdocs.yml. All of them are optional.
theme:
name: sumi
palette: auto
accent: indigo
logo: assets/logo.svg
announcement: 'Sumi 0.1 is out — <a href="blog/">read the post</a>'
Options#
palettestringDefault autolight, dark or auto (follow the operating system). Readers can switch at any time; their choice is remembered.accentstringDefault vermilionvermilion, indigo, moss, plum, ochre, teal, ink, or any CSS colour.localestringDefault enlogostringdocs/ for the header. The Sumi mark is used if unset.logo_darkstringlogo is used in both if unset.faviconstringdocs/ for the browser tab.social_imagestringdocs/ (about 1200×630, needs site_url) or a full URL. A page can use its own with image: in its front matter.brandstringDefault site_name"MkDocs<b>ng</b>".announcementstringnavigation_tabsbooleanDefault truefalse, the whole navigation is in the sidebar.toc_depthintegerDefault 3hidelistDefault []toc, sidebar, breadcrumbs, actions, pager, footer.version_providerstringmike to show a version menu. See Navigation.repo_iconstringbrand-gitlab. Detected from repo_url by default.keyboardbooleanDefault truecreditbooleanDefault trueAccent colours#
| Name | Light | Dark |
|---|---|---|
vermilion |
#c4432b |
#ef7258 |
indigo |
#2f5d93 |
#86b4ea |
moss |
#4b6b2f |
#9ccc6e |
plum |
#7a3b74 |
#dc93d4 |
ochre |
#9a6512 |
#e7b45a |
teal |
#1f6f6a |
#6fd0c6 |
ink |
#2a2926 |
#e8e4dc |
A named accent has a tuned variant for dark mode. A custom CSS colour is used in both modes.
Per-page options#
Front matter on a page can change its layout:
hidehide: [toc, pager]. navigation is accepted as an alias for sidebar.layoutwide removes both sidebars for a full-width page.templatedescription<meta name="description">, link previews, cards and the blog index.Footer and social links#
copyright: © 2026 The MkDocs NG project
extra:
social:
- icon: brand-github
link: https://github.com/mkdocs-ng
name: GitHub
- icon: fontawesome/brands/x-twitter # Material names work too
link: https://x.com/mkdocs
Custom CSS#
Every colour, font and size is a CSS variable. Add a stylesheet with extra_css:
:root {
--sumi-font: "IBM Plex Sans", system-ui, sans-serif;
--sumi-content-w: 820px;
}
[data-theme="dark"] {
--sumi-bg: #0f1115;
}
extra_css:
- assets/extra.css
Plugin options#
The sumi plugin adds the Markdown components, the blog, the print edition, the Markdown copy button and Git dates. The theme needs it; you can use it without options.
plugins:
- search
- llms_txt
- sumi:
git_dates: true
assistant_links: [claude, chatgpt]
Options#
languagestringDefault theme.localemathbooleanDefault true$…$ and $$…$$ and load KaTeX on pages that use them.mermaidbooleanDefault truemermaid fences and load Mermaid on pages that use them.echartsbooleanDefault trueecharts fences (YAML or JSON options) as charts. See Charts.markmapbooleanDefault truemarkmap fences (Markdown outlines) as mind maps. See Mind maps.markdown_copybooleanDefault trueassistant_linkslistDefault []claude, chatgpt. Off by default, because they send the page address to a third party.git_datesbooleanDefault falsegit log runs per build.blog.enabledbooleanDefault trueblog.dir as posts.blog.dirstringDefault blogdocs/.blog.feedbooleanDefault trueblog/feed.xml.blog.feed_itemsintegerDefault 20blog.authorsmappingDefault {}.authors.yml.print.enabledbooleanDefault trueprint.pathstringDefault print/Markdown extensions#
The plugin enables a small set of Python-Markdown extensions that the components rely on. They are listed in Writing. Your own markdown_extensions entries take precedence: if you configure one of them with options, your options are used.
Git dates in CI#
Shallow clones only contain the last commit, which makes every page look updated today. Fetch the full history before building:
- uses: actions/checkout@v5
with:
fetch-depth: 0
Search#
Press ⌘ K, Ctrl K or / anywhere to search. Search runs in the browser against an index built by MkDocs' search plugin; nothing is sent to a server.
plugins:
- search
- sumi
How results are ranked#
- Every page and every section heading is a result, so a match takes you straight to the right place.
- Matches in titles rank above matches in text; a title that starts with your query ranks highest.
- All words must match. Matching words are highlighted in the result and in a snippet of the text.
The index is loaded the first time the palette opens, not with the page.
Chinese, Japanese and Korean#
Text in CJK scripts has no spaces between words, so word-based search finds nothing. Sumi matches CJK text as substrings: searching for 安装 finds 安装指南 and 如何安装. Mixed queries such as mkdocs 主题 are split into a Latin term and a CJK term.
Commands#
The palette is also a command launcher. Type > (or press > on a page) to list commands only:
| Command | Action |
|---|---|
| Toggle dark mode | Switch between light and dark (t) |
| Copy page as Markdown | Copy the Markdown source of the page |
| View page as Markdown | Open the Markdown copy in a new tab |
| Edit this page | Open the page in your repository |
| Print the current page | |
| Go to top / Go home | Navigate |
| Switch language / version | When extra.alternate or versions are configured |
| Open llms.txt | When the llms_txt plugin is enabled |
Press ? to see every keyboard shortcut.
Excluding pages#
Use the standard search front matter to keep a page out of the index:
---
search:
exclude: true
---
Languages#
Sumi's interface — buttons, labels, the search palette, dates and default callout titles — is available in English, Simplified Chinese and Traditional Chinese.
theme:
name: sumi
locale: zh # en, zh (zh_CN), zh_TW
CJK typography#
When the page language is Chinese, Japanese or Korean, Sumi uses a system font stack suited to the script, a slightly larger line height and no letter-spacing on headings. Search matches CJK text by substring (see Search).
Callout titles#
Default callout titles follow the language: > [!TIP] is titled Tip in English and 提示 in Chinese. A custom title is always used as written.
Multilingual sites#
With MkDocs NG 1.11 or later, one mkdocs.yml holds every language. The first language is built at the root of the site and the others into /zh/ and so on; each one can change any setting, such as site_name or nav. Sumi takes its interface language from each language's code.
site_url: https://example.com/
languages:
en: English
zh:
name: 简体中文
site_name: 我的项目
nav:
- 首页: index.md
- 指南: guide.md
Put each translation next to the original page, as guide.zh.md next to guide.md, or give the language a folder of its own with docs_dir: docs-zh. mkdocs build builds every language and mkdocs serve previews them all; add --language zh to work on one.
What Sumi does on such a site:
- The language menu in the header, and the Switch to … commands in the command palette, link to the same page in the other language.
- A page that isn't translated yet is shown in the default language, with a note at the top that says so. Its text is marked with its own
lang, so screen readers and browsers read it in the right language. - Each page lists its translations with
<link rel="alternate" hreflang="…">, so search engines show readers the version in their language. - Search,
llms.txtand the print edition are built for each language.
See Multi-language Sites in the MkDocs NG docs for all the settings. This site and sumi-starter are built this way.
With an older MkDocs, build one site per language and link them with extra.alternate; see Navigation.
Adding a language#
Interface strings live in one Python file, mkdocs_sumi/i18n.py. A missing string falls back to English. Pull requests for new languages are welcome.
Template overrides#
Sumi is a standard MkDocs theme, so any template can be overridden with custom_dir:
theme:
name: sumi
custom_dir: overrides
overrides/
- main.htmlextend blocks for every page
partials/
- footer.htmlreplace one partial
Extending blocks#
Create overrides/main.html and extend base.html:
{% extends "base.html" %}
{% block extrahead %}
<meta name="robots" content="noindex">
{% endblock %}
| Block | Contains |
|---|---|
site_meta |
<meta> and <link> tags in <head> |
htmltitle |
The <title> element |
styles |
Stylesheets |
analytics |
The analytics snippet |
extrahead |
Empty; for your own tags |
header |
The site header |
layout |
Sidebar, article and table of contents |
content |
The article |
page_content |
The rendered Markdown |
article_footer |
Last updated, edit link, previous / next |
footer |
The site footer |
scripts |
Scripts at the end of <body> |
Partials#
| Partial | Purpose |
|---|---|
partials/header.html |
Logo, tabs, search button, menus |
partials/logo.html |
The logo only |
partials/breadcrumbs.html |
The path above the title |
partials/page-actions.html |
Copy as Markdown and the page menu |
partials/toc.html |
Table of contents |
partials/article-footer.html |
Below the article |
partials/footer.html |
Site footer |
partials/palette.html |
Search and command palette |
partials/analytics.html |
Analytics |
Analytics#
Google Analytics is supported with the same keys as Material:
extra:
analytics:
provider: google
property: G-XXXXXXXXXX
For another provider, add its snippet to the analytics block in overrides/main.html.
Note
Analytics is the one feature that contacts a third party. It is off unless configured.
For plugin authors#
Material for MkDocs is in maintenance mode, with critical fixes through 2027-05-05. Many plugins were written and tested only against it. This page is for their authors: what already works in Sumi, what needs a small change, and how to get your plugin listed.
Sumi is a standard MkDocs theme. A plugin that only uses MkDocs events and writes plain HTML into the page works without changes.
Does my plugin need changes?#
| Your plugin… | In Sumi |
|---|---|
Uses MkDocs events and writes HTML into page.content |
Works. |
Ships CSS that reads --md-* variables |
Works: Sumi maps the common ones to its own colours, in both palettes. |
Switches its dark CSS on [data-md-color-scheme="slate"] |
Works: Sumi sets that attribute next to its own data-theme. |
Writes !!! note admonitions, <details>, tables or highlighted code |
Works: Sumi styles them like its own callouts and code blocks. |
Scopes CSS to .md-typeset |
Change: add .sumi-prose, the class of Sumi's content container. |
Runs JavaScript on document$ (Material's instant loading) |
Change: Sumi loads every page normally; run on DOMContentLoaded. |
Checks config.theme.name == "material" |
Change: accept "sumi" as well, or test for the feature you need. |
Imports from the material package or overrides Material partials |
Change: see Templates. |
Detect the theme#
def on_config(self, config):
self.is_sumi = config.theme.name == "sumi"
Prefer this over checking for installed packages: a site can have Material installed and still build with Sumi.
Styles#
Rendered Markdown lives in <div class="sumi-content sumi-prose">. Scope your CSS to it the same way you scoped it to .md-typeset:
:is(.md-typeset, .sumi-prose) .my-widget { margin: 1em 0; }
Use Sumi's tokens when you want an exact match, and keep Material's as the fallback so one stylesheet serves both themes:
.my-widget {
border: 1px solid var(--sumi-line, var(--md-default-fg-color--lightest));
background: var(--sumi-bg-elev, var(--md-default-bg-color));
color: var(--sumi-fg-2, var(--md-default-fg-color--light));
}
| Token | Use |
|---|---|
--sumi-fg, --sumi-fg-2, --sumi-fg-3 |
Text: body, secondary, muted |
--sumi-bg, --sumi-bg-elev, --sumi-bg-soft, --sumi-bg-sunken |
Page, raised panels, subtle fills, wells |
--sumi-line, --sumi-line-2 |
Borders: hairline and stronger |
--sumi-accent |
The accent colour chosen with theme.accent |
--sumi-link |
Links |
--c-note, --c-tip, --c-important, --c-warning, --c-caution |
Status colours, the same as callouts |
--sumi-code-bg, --sumi-code-inline |
Code block and inline code backgrounds |
--sumi-font, --sumi-font-mono |
Text and code fonts |
--sumi-radius, --sumi-radius-sm |
Corner radii |
All tokens change with the palette, so CSS that uses them needs no dark-mode rules. If you write your own, key them on [data-theme="dark"].
To add your stylesheet, append it to config.extra_css in on_config and copy the file in on_post_build, as you would for Material. Sumi loads extra_css after its own CSS.
Scripts#
Sumi has no instant loading: each link is a normal page load, so a script that runs on DOMContentLoaded runs on every page. When the reader switches between light and dark, Sumi updates data-theme on <html> and fires an event:
document.addEventListener("sumi:theme", (event) => {
redraw(event.detail.theme); // "light" or "dark"
});
data-theme is set before the page is drawn, so a script can read it at any time. Don't rely on Sumi's own script having run first: it is deferred.
Templates#
Sumi has the usual MkDocs blocks. A plugin or user override that extends Material's main.html maps like this:
| Material block | In Sumi |
|---|---|
extrahead, htmltitle, site_meta, styles, analytics |
Same name |
scripts, content, header, footer |
Same name |
announce |
The theme.announcement option |
outdated |
Built in with theme.version_provider: mike |
tabs, hero |
Header tabs are built in; use a landing page for a hero |
Sumi's partials have different names and contents from Material's; see Template overrides. If your plugin replaces a Material partial, such as the "last updated" line, write the information into the page instead, or ask for a hook in an issue.
Features Sumi already has#
Some plugins exist to fill a gap that Sumi covers itself. If yours is one of them, document how it fits next to Sumi's version rather than duplicating it.
| Material plugin | In Sumi |
|---|---|
blog |
The blog reads the same folder, front matter and .authors.yml |
tags |
Tags on blog posts, with filters on the index |
search |
MkDocs' search plugin, shown in Sumi's command palette |
privacy, offline |
Not needed: fonts, icons and scripts are bundled, nothing is fetched at read time |
social |
Not built in. Sumi writes og:title, og:description and twitter:card, and writes og:image only when social_image or a page image: is set, so a social card plugin can add its own image without duplicates |
Test against Sumi#
- Build a small site with
theme: sumiandplugins: [search, sumi, your-plugin], usingmkdocs build --strict. - Check the output in both palettes and on a narrow screen.
- Open
/print/, the print edition, if your plugin adds content to pages.
Get listed#
Open an issue with your plugin's name and a screenshot of it in Sumi. Plugins that work are added to the migration guide. If something in Sumi stands in your way, say so in the same issue: a missing block or token is easier for us to add than for you to work around.
Plugins Sumi styles#
These plugins write HTML that Sumi styles to match the rest of the site.
mkdocstrings#
API pages from mkdocstrings get Sumi's code font and colours: signatures, symbol badges (mod, class, func, meth, attr, type, param), labels, nested members, section tables and lists, parameter headings, backlinks and a quiet "Source code" panel. These options suit Sumi well:
plugins:
- search
- sumi
- mkdocstrings:
handlers:
python:
options:
show_root_heading: true
show_symbol_type_heading: true
show_symbol_type_toc: true
separate_signature: true
show_signature_annotations: true
signature_crossrefs: true
Docs for people and agents#
More and more readers of documentation are not people. Coding agents, assistants and search tools read your pages to answer questions about your project, and they read HTML badly: navigation, scripts and styling are noise to them. Sumi publishes every page twice — as a page for people and as plain Markdown for programs.
What Sumi publishes#
| Output | Where | For |
|---|---|---|
| HTML page | guide/install/ |
People |
| Markdown copy | guide/install.md |
Agents reading one page |
llms.txt |
site root | Agents discovering the site |
llms-full.txt |
site root | Agents loading everything at once (opt-in) |
| Atom feed | blog/feed.xml |
Feed readers |
| Print edition | print/ |
Paper, PDF, offline reading |
The Markdown copy and llms.txt come from the llms_txt plugin that ships with MkDocs NG. Enable it next to sumi:
site_url: https://example.com/docs/ # needed for absolute links in llms.txt
plugins:
- search
- llms_txt:
full_output: true # also write llms-full.txt
- sumi
Try it on this site: open llms.txt, or choose View as Markdown in the menu at the top of this page.
Discovery#
Each HTML page announces its Markdown copy in <head>:
<link rel="alternate" type="text/markdown" href="ai.md" title="Markdown">
<link rel="alternate" type="text/plain" href="../llms.txt" title="llms.txt">
An agent that lands on any page can find the clean version without guessing URLs.
Copy as Markdown#
The Copy as Markdown button at the top of each page copies the page source — the same text an agent reads — so you can paste it into a chat. The menu next to it can also open the Markdown copy, print the page and, if you enable it, open the page in an assistant:
plugins:
- sumi:
assistant_links: [claude, chatgpt]
Note
Assistant links are off by default: they send the page address to a third-party service when clicked.
Why native Markdown matters here#
Components written in a private syntax — MDX, shortcodes, HTML widgets — reach an agent either as raw markup it has to interpret or not at all. Sumi's components are ordinary Markdown with an attribute line, so the Markdown copy reads naturally: a callout is a quote, steps are a numbered list, tabs are consecutive code blocks with labels.
Landing pages are the one place where content lives in front matter. For those pages, Sumi writes a Markdown copy that lists the hero, features, steps and questions as headings and lists.
Design principles#
Sumi (墨) is the ink of East Asian painting and calligraphy. One ink, ground to different strengths, produces every shade on the page. The theme follows the same idea: warm paper, black ink, a single vermilion accent like a seal, and nothing that does not carry information.
Content stays Markdown#
Documentation outlives the tools that render it. A page written for Sumi must still make sense on GitHub, in an editor and in a terminal, so every component is written in Markdown's own vocabulary — blockquotes, lists, tables, images and fences — with at most one {…} line of attributes. If you leave Sumi tomorrow, your content leaves with you.
This rules out shortcodes, MDX and inline HTML widgets, and it is why the component set is small. Each component exists because technical documentation needs it often: warnings, procedures, alternatives, references, layouts and diagrams.
Everything is local#
A built Sumi site makes no network requests that you did not configure. Fonts (Inter and JetBrains Mono), 2 000 icons, the search index, Mermaid and KaTeX are part of the theme and copied into your site. That means:
- the site works behind a firewall, on an intranet and from a USB stick;
- readers are not tracked by a font or script CDN;
- a build today and a build in five years produce the same page.
Pay only for what you use#
The core of the theme is one stylesheet and one script with no framework. Heavier runtimes are loaded on demand: Mermaid only when a diagram nears the viewport, and only the code for that diagram type, KaTeX only on pages with a formula, the search index only when the palette is opened. Vendor files are copied into the site only if some page uses them.
Degrade gracefully#
Every page is readable without JavaScript: navigation is links, collapsible sections are <details>, tabs show one after another, file trees are nested lists. JavaScript adds convenience — search, tab sync, copy buttons, scroll spy — never content.
People and agents are both readers#
Every page is published as HTML for people and as Markdown for programs, and each HTML page links to its Markdown twin. See Docs for people and agents.
One design language#
Docs, blog, landing page, release downloads and print share one set of tokens: type scale, spacing, colours and components. A project site built with Sumi looks like one product, not a docs theme with a marketing page bolted on.
Credits#
Sumi's principles were inspired by Oink, a documentation distribution for Hugo by Feng Ruohang, and by the long tradition of MkDocs themes — especially Material for MkDocs, whose configuration Sumi reads so that existing sites can switch. Sumi's code and design are its own.
Releases#
Install the latest release from PyPI:
pip install --upgrade mkdocs-sumi
Sumi follows semantic versioning. Until 1.0, minor versions may change template blocks; configuration keys and Markdown syntax stay compatible.
Blog#
Releases, design notes and guides from the Sumi project.