Offline Docs Site Builder
On this page
Offline Docs Site Builder is a single script that turns a project's Markdown documentation into a small static website: a menu of every page, a table of contents that follows the reader, tables that never cut their cells short, and code blocks with a copy button. The site opens straight from the folder — no web server, no internet connection — and looks like the product it documents.
Documentation in Markdown is pleasant to write and awkward to read: on a server without a browser-friendly viewer, a team gets raw text, long tables that wrap into noise, and links that point at .md files. The builder keeps Markdown as the one source and gives the team pages they actually read.
At a glance
| Form | A single command-line script, with a page template and a small browser script |
|---|---|
| Language | Python 3 with markdown-it-py; Tailwind CSS compiled locally; plain JavaScript in the page |
| Status | Stable, in use |
| Source code | Private |
What it does
A. Builds a site that opens without a server
Every page is assembled at build time from one template: the menu, the table of contents, the content, and the footer. Fonts and styles are stored next to the pages, so opening index.html from a copied folder or a USB stick shows the same site as a web server would. The project's README becomes the home page, and every numbered file such as 01-architecture.md becomes a page, titled by its first heading.
B. Keeps Markdown the only source
The HTML is output, never edited by hand. Every run rebuilds every page, so a page cannot fall behind its Markdown, and a --list option shows the pages it found and the state of each.
C. Makes tables and code usable
Tables sit in a scrolling panel with column widths left to the content and cells that are never truncated, because a cell in operational documentation can be 190 characters long and hiding it costs the reader. Every column except the last can be widened by dragging its edge, down to a minimum width, without selecting text on the way. Code blocks get a panel with the language named and a Copy button that copies the exact text, indentation included, even when the page was opened from a file.
D. Keeps every link working
Headings receive the same anchors GitHub would give them, so links already written in Markdown still land in the right place. Links between Markdown files are rewritten to the matching HTML pages, including links from the README and links written with a folder prefix. A link to a page whose source is missing is turned into plain, disabled text rather than a dead link, and the menu shows that page greyed out.
E. Follows the reader through long pages
A table of contents is built from the second- and third-level headings and highlights the section currently on screen as the reader scrolls. Its entries are kept as plain text until the last step, so a heading with an ampersand is not escaped twice.
How I use it
| In Markdown | On the page |
|---|---|
| A heading | an anchor matching GitHub's, and an entry in the table of contents |
| A table | a scrolling panel with full cells and columns that can be widened |
| A fenced code block | a panel with its language and a Copy button |
A link to another .md file | a link to its HTML page, or disabled text when the source is missing |
| The README | the home page |
Design decisions
The site has to open from a folder, so it is generated rather than assembled in the browser. Static HTML has no include, and a page opened from a file is not allowed to fetch another file, so a shared header and menu can only exist if they are written into every page at build time. Fonts and the compiled stylesheet live beside the pages for the same reason, and the build uses a Markdown library that was already installed — CommonMark with tables and strikethrough covers every element the documentation uses — so the tool adds no new dependency. When a template change introduces new style classes, the build prints the one command that recompiles the stylesheet.
Markdown stays the only source, and partial builds are gone. The documentation is written and reviewed as Markdown, next to the code; the HTML is a view of it. An earlier version built only some pages while their design was being judged, and that left other pages silently stale, so every run now rebuilds all of them.
Reading comfort comes from the details of the theme, not from a plugin. The interface the documentation belongs to does not use a typography plugin, so the builder gives each element its classes itself, and the pages share the product's colours, fonts, and dark surfaces. Paragraphs are justified with automatic hyphenation tied to the page's Indonesian language, because long technical terms that cannot be broken leave wide gaps between words. Tables never truncate, columns can be widened, code can be copied with its indentation intact, and the table of contents keeps the reader oriented in long pages.
Links must survive the conversion. Anchors follow GitHub's rules exactly, including two details that once broke them: HTML entities are decoded before a heading becomes an anchor, so an ampersand does not turn into the letters of its entity, and punctuation is removed rather than replaced, so a dash between spaces leaves a double hyphen just as GitHub does. Links to Markdown files become links to their pages, and a link that would lead nowhere is disabled instead of producing a 404.
Quiet rendering mistakes are caught in the build, not by readers. The Markdown library writes horizontal rules as self-closing tags and aligned table cells with a style attribute, and both would have slipped past the styling and appeared in the browser's defaults — a bright line across a dark page, a cell without its padding — so both are handled explicitly. A trailing rule that only separated the closing block in Markdown is removed, and the first heading on a page does not carry a top margin inside its card. A missing library or template stops the build with the command that fixes it rather than a traceback.
Specifications
| Inputs | The project README and numbered Markdown files in the documentation folder |
|---|---|
| Outputs | One HTML page per source, with a menu, a table of contents, and the shared template; written into the documentation's HTML folder |
| Template | Placeholders for the title, menu, table of contents, content, and year |
| Works offline | Yes: fonts, stylesheet, and script are local, and pages open from the file system |
| Requirements | Python 3 with markdown-it-py; the Tailwind command-line tool only when styles change |
Related
The script follows the shape I give every small tool: one file, one job, and the same order of sections every time.
Why documentation is written for the next reader, and lives beside the code it explains.
The same idea from a different source: turning text written for tools into pages people read.
Terms
- Static site
- A set of ready-made HTML pages that need no program running on a server to be shown.
- Markdown
- A plain-text format whose light markup, such as
#for headings, turns into formatted text. - CommonMark
- A precise specification of Markdown that different tools can render the same way.
- Anchor
- An identifier on a heading that a link can point to, written after a
#in the address. - Hyphenation
- Breaking a long word across two lines at a valid point, so justified text does not leave wide gaps.
Technologies covered
- Python