oku

Python CLI · Custom Elements · one HTML file per page · MIT

Markdown in. One visual HTML file out.

Charts, tables, diagrams and step flows are typed fences in an ordinary Markdown file. The browser renders them with Custom Elements; one command builds a multi-page site with search — and a single-file copy of every page that opens from file://, follows the OS theme and loads nothing from a CDN.

14block kinds, from chart to annotated code
53chart types behind one primitive
1file per page — theme, fonts, Mermaid and Prism inside
The charts reference rendered by oku: the Contents drawer pinned on the left with the site tree and the on-page TOC, a grid of pie, donut, waffle, treemap and polar-area previews in the reading column, and the display menu open at the top right with text size, column width, theme and language

Documentation tooling usually makes you choose: a static-site generator that needs a build and a server before anyone can read a chart, or a document that travels as a file and cannot show one. oku keeps the source an ordinary Markdown file — GitHub, Obsidian and VS Code still render the prose, the tables and the Mermaid, and an oku-* fence shows there as readable JSON — and makes the rendered page self-contained, so the reader gets one file that opens from a mail attachment and looks the same next year.

Typed fences, one JSON object each

chart, table, diagram, step-flow, compare-grid, kpi-grid, timeline, annotated-code, live-snippet, example, insight, info-tip, copy, chart-grid. Every payload shape is printed by oku spec, read from the code.

53 chart types, grouped by intent

Cartesian, categorical, part-to-whole, distribution, trend, flow, network, multivariate, goal, geographic. One primitive, a shared tooltip with click-to-pin, labels fitted after the font lands, colours from tokens so light and dark both hold.

The reader gets one file

dist/standalone/ holds a self-contained HTML per page: theme, fonts, Mermaid and Prism are inside, nothing loads from a CDN. It opens from file://, from a mail attachment, from a USB stick.

A site, with search and a reading rail

dist/site/ is the multi-page build: one drawer for the site tree and the on-page TOC, full-text search (Pagefind), a landmark minimap, one menu for text size, column width, theme and language — and an llms.txt for the readers that are models.

Checks that catch what a reviewer would

oku check runs schema, structure and presentation rules — every block in a section ends at the same right edge, nothing waits for a click, colour comes from tokens — and the build refuses a page that fails them. Each rule is a browser test.

Translations side by side

page.md and page.tr.md live next to each other; the manifest pairs them and the language switch appears only where there is somewhere to go. Tables sort, filter and copy as TSV or Markdown in every locale.

From a fence to a page

A table is fifteen lines of JSON in a ```oku-table fence. The same file reads as plain Markdown on GitHub; built, it is the table on the right — sortable, filterable, with the author's own status words mapped to colour.

The tables reference: on the left the JSON of a table fence with headers, a status map and four rows; on the right the rendered table with sortable columns and done, in-progress and queued as coloured chips
  1. Write the fence

    Front-matter, a GFM body, and one compact JSON object per oku-* fence. [term](#g/id) is a glossary tooltip, [label](#f/path) a file chip with a preview.

  2. oku check

    Schema, structure and presentation lint; --strict fails on warnings, --fix applies the mechanical rewrites. What passes here renders finished.

  3. oku build

    dist/standalone/ — one self-contained HTML per page; dist/site/ — the multi-page site with its search index and llms.txt. oku serve is the same with live reload.

  4. Ship either

    Attach the single file, or publish the site folder anywhere static — this documentation is docs/ of the repository, built by the kit itself on every push.

Install

Python 3.12 and uv. The repository is the package; ./ctl deploy puts oku on the PATH and verifies the installed kit build.

Install

git clone git@github.com:mmdemirbas/oku.git && cd oku
./ctl deploy      # `oku` on PATH

Or run it in place: uv run bin/oku serve from the repository root.

A docs tree

mkdir docs && cd docs
oku init          # this directory becomes the docs root
oku serve         # http://localhost:9876, live reload
oku build         # dist/standalone/ + dist/site/

Pages written as JSON before the Markdown format existed keep rendering; oku migrate converts them.

Command line

One executable, eight verbs. Without installing, uv run bin/oku from the repository does the same.

oku init             # _oku symlink + index.html stub in the current directory
oku check            # schema + structural + presentation lint
oku check --strict   # exit 1 on warnings too;  --fix applies the mechanical rewrites
oku build            # dist/standalone/ + dist/site/ + search index
oku serve            # local HTTP with live reload
oku spec [kind]      # print a fence's payload shape, read from the code
oku migrate [path]   # convert page-JSON (v1/v2) sources to .md
oku clean            # remove dist/

Rules the kit enforces

Every block in a section ends at the same right edge. The default renders finished; nothing waits for a click. Opening the drawer moves nothing on the page. A label is fitted to the space it has, measured after the font lands. Colour comes from tokens, so a hand-styled Mermaid node or SVG is a lint warning. Each rule is a browser test; the reasoning behind each is in PRESENTATION-RULES.md.