Design Docs Site (Unified Documentation Website)#
The markdown files and hand-written html files under
docs/are served as a single static documentation site with one consistent style: left-side directory tree · top search bar · right-side per-page anchors · light/dark dual themes. Changing the skin in one place keeps the whole site consistent. Embedded dynamic animations render verbatim — they are part of the page content, not an afterthought.
1. Goals and Non-Goals#
Goals#
- One shell, consistent across the site: navigation, color scheme, typography, and code-block styling are defined once and reused by every doc.
- Zero runtime framework: the output is pure static html/css/js, which can be served directly by the worker (single-port route) or any static server, without introducing a Vite/Vue/React runtime.
- Light/dark dual themes: one set of CSS variables driving two color schemes, following the system + manual toggle + remembered preference (localStorage).
- Automatic navigation: the left-side directory tree is generated automatically from the
docs/directory structure; group titles come from the top-level heading of each level'sREADME.md. - Per-page anchors: the right-side "On this page" is generated automatically from each doc's
##/###headings, highlighting the current section while scrolling. - Full-text search: a lightweight search index (titles + body) is generated at build time, with pure-JS search on the front end and no backend.
- Dynamic animations render verbatim:
<script>/<canvas>/<svg>/<style>embedded in md is passed through verbatim; charts from the 11 hand-written html files can be embedded into the new shell as whole blocks without loss.
Non-Goals#
- No editor / CMS; the docs are still authoritative as source files (md/html), and the site is a read-only output.
- No multi-language switching framework (the docs themselves mix Chinese and English; i18n is not enforced).
- No replacement of
docs/slides/(slides are a separate format and stay as-is).
2. Why a Custom Script Instead of VitePress / MkDocs#
| Dimension | Custom script | VitePress | MkDocs Material |
|---|---|---|---|
| Adding custom dynamic animations later | Highest: templates/CSS/JS are all our own, native html/js written directly | High, but must be written as Vue components | Low, the theme is closed and fights with raw html |
| Runtime dependencies | None (pure static) | Vite/Vue | None (but heavy at build time) |
| Consistency with the single-port hosting route | Yes | Needs extra build-output integration | Yes |
| Embedding the 11 hand-written html files | Direct passthrough | Must be rewritten as components | Hard |
| Cost of standard features (sidebar/search/anchors) | Write it once yourself | Out of the box | Out of the box |
The requirement is a unified docs site and the freedom to add dynamic animations. A framework either limits animations (MkDocs) or forces a migration into a component system (VitePress). The hand-written html files already contain custom charts and animations, so what the site needs is a shell that allows native html/js. Writing the sidebar, search, and anchors once buys that freedom permanently.
3. Technology Choices#
- Build language: Python. The repo's primary language is Python, the worker is already Python, and there's no need to add a Node toolchain.
- Markdown rendering:
markdown-it-py+ plugins (mdit-py-plugins: anchors, footnote, deflist, tasklists). Reason: it preserves raw html passthrough (html=True), which is what lets embedded animations render at all; GitHub-flavored tables/code fences are fully supported. - Code highlighting: Pygments (rendered at build time into class-tagged spans, with zero runtime cost; one Pygments stylesheet for each of the light/dark themes).
- Search: generate
search-index.jsonat build time, with a minimal inverted-index/substring match on the front end. A corpus of a few hundred docs does not need a heavy library like lunr or flexsearch. - Math formulas: a KaTeX integration point exists, disabled by default.
Dependency control: only three pure-Python packages are added — markdown-it-py, mdit-py-plugins, Pygments — placed in a separate optional docs-build dependency group so the main dependencies aren't polluted.
4. Directory and Output Layout#
docs/ ← source files (untouched)
design/runtime/dag/rendering.md
design/proactive/event-layer.html ← hand-written html
...
tools/docs_site/ ← new: build script (one small module)
build.py entry point: scan docs/ → render → write _site/
template.py html shell template (shell + injection points)
nav.py generate navigation data from directory tree + README
search.py generate search-index.json
assets/
site.css site-wide styles + light/dark dual-theme variables
site.js theme toggle + anchor highlighting + search + mobile drawer
pygments-light.css
pygments-dark.css
docs/_site/ ← build output
index.html
design/runtime/dag/rendering.html
search-index.json
assets/...
Build command: python -m tools.docs_site.build.
5. Page Skeleton (Three Columns)#
┌────────────────────────────────────────────────────────────┐
│ OpenProgram Docs [🔍 search ⌘K] [☀/🌙] │ top bar, fixed
├──────────────┬───────────────────────────────┬─────────────┤
│ dir tree │ # page title │ On this page │
│ Design │ body… │ · 1. Goals │
│ Runtime │ ```code``` │ · 2. … │
│ > current │ <canvas> animation passthrough│ · 3. … │
│ Context │ │ highlight │
│ (collapsible) │ │ current sec. │
└──────────────┴───────────────────────────────┴─────────────┘
left col collapsible/remembered expand state body max-width≈820px right col hidden on narrow screens
Narrow screens (< 900px): the left column collapses into a drawer (toggled by a hamburger button), and the right column is hidden.
6. Light/Dark Dual Themes#
One set of CSS variables, with :root as the light default and [data-theme="dark"] overriding it for dark. Toggle logic:
- On first visit, read
prefers-color-schemeto follow the system. - User clicks toggle → write
localStorage.theme→ set<html data-theme>. - Anti-flicker: inline a small synchronous script in
<head>that fixes the theme before the DOM renders.
Color palette:
| Role | Light | Dark |
|---|---|---|
| Background | #ffffff / sidebar #f7f7f5 |
#16181d / sidebar #1b1e24 |
| Body text | #1f2328 |
#d8dae0 |
| Secondary text | #656d76 |
#8b929c |
| Accent | #3b82f6 (blue) |
#5aa2ff |
| Code background | #f6f8fa |
#21262d |
| Border | #d0d7de |
#30363d |
Style baseline: light-first, aligned with the restrained, professional feel of technical docs like Stripe/Vercel/Linear; dark is not pure black, to avoid eye strain.
7. Dynamic Animations as a First-Class Concern (Key Design)#
This is the biggest difference from an ordinary docs site, so the implementation mechanism is described separately:
- md embedded passthrough:
markdown-it-pyruns withhtml=True, so<canvas>,<svg>,<script>,<style>blocks written in md go into the output verbatim, without being escaped. If an author wants to add an interactive demo to a given doc, they just write it inside that md. - Page-level extra resources: by convention, an md file can declare
scripts: [foo.js]/styles: [foo.css]in its frontmatter; at build time these files are copied to the output and<script>/<link>tags are injected into that page. Complex animations are split into separate js so the body isn't polluted. - Handling hand-written html (preserving content embedded into the new shell): the 11 hand-written html files go through a dedicated pipeline — extract their
<body>content + collect their<style>(adding a page-level scoping prefix to avoid conflicts with site-wide styles), and stuff the whole thing into the body area of the unified shell, preserving the original charts/animations. Their own<script>is preserved as well. This pipeline is separate from the markdown one and each file is verified for no visual regression. - Theme-aware animations: a global
documentThemeChangeevent lets animation scripts adapt to light/dark. Listening is optional.
8. Navigation Generation Rules#
- Scan all
*.mdunderdocs/plus the 11 hand-written*.htmlfiles. - Directory = group:
docs/reference/design/runtime/→ group "Runtime"; the group title prefers the top-level heading of that directory'sREADME.md, falling back to a prettified directory name if absent. - Ordering within a group:
README.mdfirst, the rest by filename. - Exclusions:
docs/_site/,docs/images/,docs/slides/, and directories whose name starts with an underscore. - Top-level loose pages (
docs/*.mdsuch as GETTING_STARTED, install) go into the "Guides" group.
9. Hosting and Output#
The build output docs/_site/ is committed to git and served by the worker's single-port route /docs, so the docs need no separate server or deployment step.
Appendix: Implementation Status#
The site is built and served. Two residual items:
docs/reference/design/proactive/_research_archive/still holds three files (evaluation.md,replay.md,threat-model.md). They are excluded from the site by the leading-underscore rule rather than by deletion.- The build has no
--watchmode; every rebuild is a full run ofpython -m tools.docs_site.build.