md-document — Long-form Markdown to HTML skill
Converts long-form markdown (specs, RFCs, reports, plans, explainers) into a single-file, lightly-interactive HTML document with sticky TOC, scrollspy, search filter, code-copy buttons, and design-system-driven brand tokens.
by alirezarezvani·MIT license·★ 26,349 Stars on the repo·GitHub ↗
npx degit alirezarezvani/claude-skills/markdown-html/skills/md-document#main ~/.claude/skills/md-documentChecked ·commit main
Files of md-document — Long-form Markdown to HTML
Show the full text106 lines
md-document — Long-form Markdown to HTML
The general-purpose converter — handles the 90% case Shihipar describes (specs, plans, RFCs, reports, explainers). Three stdlib tools pipeline together:
markdown_parser.py → html_renderer.py → interactivity_injector.py
(md → JSON AST) (AST + tokens → HTML) (HTML + JS behavior)
Output is one .html file with sticky TOC, search filter, scrollspy, code-copy buttons, and the user's 12 derived brand tokens. Externals limited to Google Fonts CSS + Prism.js CDN.
When to invoke
| Symptom | Action |
|---|---|
markdown-html-orchestrator routes input as DOCUMENT |
Invoke this skill |
User runs /cs:md-document <path>.md directly |
Invoke this skill |
| User says "convert this spec/report/RFC/plan to HTML" | Invoke this skill |
Input is a code review (has ```diff blocks) |
Route to md-review instead |
Input is a slide deck (clear --- boundaries) |
Route to md-slides instead |
| Input is < 100 lines | Refuse (Shihipar threshold — markdown still wins) |
| Design-system not onboarded | Refuse, surface /cs:design-system |
Pipeline
# 1. Parse markdown → JSON AST
python3 markdown-html/skills/md-document/scripts/markdown_parser.py \
--input <path>.md --output sections.json
# 2. Render AST + design-system config → single-file HTML
python3 markdown-html/skills/md-document/scripts/html_renderer.py \
--sections sections.json --output document.html
# 3. Inject lightweight JS (search, copycode, smoothscroll, scrollspy)
python3 markdown-html/skills/md-document/scripts/interactivity_injector.py \
--file document.html \
--features search,copycode,smoothscroll,scrollspy
Or all-in-one (sample render):
python3 markdown-html/skills/md-document/scripts/html_renderer.py --sample \
| python3 markdown-html/skills/md-document/scripts/interactivity_injector.py \
--file /dev/stdin --output document.html
What gets rendered
CommonMark subset sufficient for agent-generated artifacts:
- Headings H1-H6 (every H2+ gets an anchor id and TOC entry)
- Paragraphs with inline bold / italic /
code/ links / - Fenced code blocks (
```python) with Prism.js highlighting on demand - GFM tables with per-column alignment
- GFM callouts (
> [!NOTE],> [!TIP],> [!IMPORTANT],> [!WARNING],> [!CAUTION]) - Blockquotes, ordered + unordered lists (single-level), horizontal rules
Out of scope: nested lists, HTML inlines, footnotes, definition lists, task list checkboxes (rendered as plain text), reference-style links.
Hard rules
- Refuses input < 100 lines. Markdown wins below the threshold (Shihipar).
- Refuses without onboarding.
config_loader.setup_completed()must returnTrue. Otherwise surface/cs:design-system. - Single-file output. All CSS + JS inline. Only externals are
fonts.googleapis.comandcdn.jsdelivr.net(Prism). Anything else is a regression. - Customization must change behavior.
design_style=editorialproduces 720px-wide layout with 1.75 line-height;playfulrounds the callouts and adds shadow;technicalis dense with 0.875rem code. Smoke-tested. - WCAG-compliant tokens. Inherits the design-system's WCAG AA palette — body text ≥ 4.5:1 contrast, links iteratively walked to 4.5:1.
- Idempotent injection. Re-injecting interactivity is a no-op (marker check). Re-rendering with a different design_style works cleanly.
Forcing-question library (Matt Pocock grill discipline)
- What's the document for — skim, decide, or deep-read? Recommended: name it; density follows. Canon: Shihipar; Tufte Envisioning Information.
- Sticky-sidebar TOC or collapsible-top? Recommended: sticky-sidebar for > 800 words / 4+ H2s; collapsible-top for shorter mobile-first docs. Canon: NN/g TOC Best Practices (2023).
- All four interactive features, or a subset? Recommended: all four — none of them cost more than ~1 KB. Canon: Wattenberger Why React isn't great for actually building websites.
- Code theme — light, dark, or auto? Recommended: auto (follows OS
prefers-color-scheme). Canon: WCAG 2.2 §1.4.3. - Does the document have a clear H1 title? Recommended: yes — H1 becomes the page
<title>and is excluded from the TOC.
Distinct from
md-review— that converter renders diff blocks + severity-tagged margin annotations. This one renders prose + tables + code + callouts.md-slides— that converter splits on---boundaries into slides. This one renders one continuous document.marketing/landing/— that generates landing pages from scratch (no markdown input). This converts existing markdown.
Output artifact
{default_output_dir}/doc-{slug}.html (path resolved by orchestrator's output_path_resolver.py; collision suffix -2, -3, … by default).
References
- Shihipar — Claude Code HTML output (Medium, 2026)
- Tufte — Envisioning Information (1990), ch. 2 "Micro/Macro Readings"
- NN/g — Table of Contents Best Practices (2023)
- WCAG 2.2 — §1.4.3 contrast, §2.4.5 multiple ways
- Wattenberger — Why React isn't great for actually building websites
- See
references/for full citations
| 1 | |
| 2 | name md-document |
| 3 | description Converts long-form markdown (specs, RFCs, reports, plans, explainers) into a single-file, lightly-interactive HTML document with sticky TOC, scrollspy, search filter, code-copy buttons, and design-system-driven brand tokens. Triggers when the markdown-html-orchestrator classifies an input as DOCUMENT, or when invoked directly via /cs:md-document. Reads the design-system config via config_loader.py and inlines the user's 12 derived CSS custom properties; refuses to render if onboarding hasn't run. Single-file output — Google Fonts + Prism.js CDN are the only externals; no framework runtime, no build step. Use after orchestrator routing or after design-system onboarding is confirmed. |
| 4 | version 2.10.1 |
| 5 | author Alireza Rezvani |
| 6 | license MIT |
| 7 | tags [markdown, html, documentation, single-file, toc, scrollspy, search, code-copy, design-system] |
| 8 | compatible_tools [claude-code, codex-cli, cursor, antigravity, opencode, gemini-cli] |
| 9 | |
| 10 | |
| 11 | # md-document — Long-form Markdown to HTML |
| 12 | |
| 13 | The general-purpose converter — handles the 90% case Shihipar describes (specs, plans, RFCs, reports, explainers). Three stdlib tools pipeline together: |
| 14 | |
| 15 | |
| 16 | markdown_parser.py → html_renderer.py → interactivity_injector.py |
| 17 | (md → JSON AST) (AST + tokens → HTML) (HTML + JS behavior) |
| 18 | |
| 19 | |
| 20 | Output is one `.html` file with sticky TOC, search filter, scrollspy, code-copy buttons, and the user's 12 derived brand tokens. Externals limited to Google Fonts CSS + Prism.js CDN. |
| 21 | |
| 22 | ## When to invoke |
| 23 | |
| 24 | | Symptom | Action | |
| 25 | |---|---| |
| 26 | | `markdown-html-orchestrator` routes input as DOCUMENT | Invoke this skill | |
| 27 | | User runs `/cs:md-document <path>.md` directly | Invoke this skill | |
| 28 | | User says "convert this spec/report/RFC/plan to HTML" | Invoke this skill | |
| 29 | | Input is a code review (has ` ```diff ` blocks) | Route to `md-review` instead | |
| 30 | | Input is a slide deck (clear `---` boundaries) | Route to `md-slides` instead | |
| 31 | | Input is < 100 lines | Refuse (Shihipar threshold — markdown still wins) | |
| 32 | | Design-system not onboarded | Refuse, surface `/cs:design-system` | |
| 33 | |
| 34 | ## Pipeline |
| 35 | |
| 36 | |
| 37 | # 1. Parse markdown → JSON AST |
| 38 | python3 markdown-html/skills/md-document/scripts/markdown_parser.py \ |
| 39 | --input <path>.md --output sections.json |
| 40 | |
| 41 | # 2. Render AST + design-system config → single-file HTML |
| 42 | python3 markdown-html/skills/md-document/scripts/html_renderer.py \ |
| 43 | --sections sections.json --output document.html |
| 44 | |
| 45 | # 3. Inject lightweight JS (search, copycode, smoothscroll, scrollspy) |
| 46 | python3 markdown-html/skills/md-document/scripts/interactivity_injector.py \ |
| 47 | --file document.html \ |
| 48 | --features search,copycode,smoothscroll,scrollspy |
| 49 | |
| 50 | |
| 51 | Or all-in-one (sample render): |
| 52 | |
| 53 | |
| 54 | python3 markdown-html/skills/md-document/scripts/html_renderer.py --sample \ |
| 55 | | python3 markdown-html/skills/md-document/scripts/interactivity_injector.py \ |
| 56 | --file /dev/stdin --output document.html |
| 57 | |
| 58 | |
| 59 | ## What gets rendered |
| 60 | |
| 61 | CommonMark subset sufficient for agent-generated artifacts: |
| 62 | Headings H1-H6 (every H2+ gets an anchor id and TOC entry) |
| 63 | Paragraphs with inline **bold** / *italic* / `code` / [links] / ![images] |
| 64 | Fenced code blocks (` ```python `) with Prism.js highlighting on demand |
| 65 | GFM tables with per-column alignment |
| 66 | GFM callouts (`> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`, `> [!CAUTION]`) |
| 67 | Blockquotes, ordered + unordered lists (single-level), horizontal rules |
| 68 | |
| 69 | Out of scope: nested lists, HTML inlines, footnotes, definition lists, task list checkboxes (rendered as plain text), reference-style links. |
| 70 | |
| 71 | ## Hard rules |
| 72 | |
| 73 | **Refuses input < 100 lines.** Markdown wins below the threshold (Shihipar). |
| 74 | **Refuses without onboarding.** `config_loader.setup_completed()` must return `True`. Otherwise surface `/cs:design-system`. |
| 75 | **Single-file output.** All CSS + JS inline. Only externals are `fonts.googleapis.com` and `cdn.jsdelivr.net` (Prism). Anything else is a regression. |
| 76 | **Customization must change behavior.** `design_style=editorial` produces 720px-wide layout with 1.75 line-height; `playful` rounds the callouts and adds shadow; `technical` is dense with 0.875rem code. Smoke-tested. |
| 77 | **WCAG-compliant tokens.** Inherits the design-system's WCAG AA palette — body text ≥ 4.5:1 contrast, links iteratively walked to 4.5:1. |
| 78 | **Idempotent injection.** Re-injecting interactivity is a no-op (marker check). Re-rendering with a different design_style works cleanly. |
| 79 | |
| 80 | ## Forcing-question library (Matt Pocock grill discipline) |
| 81 | |
| 82 | **What's the document for — skim, decide, or deep-read?** Recommended: name it; density follows. Canon: Shihipar; Tufte *Envisioning Information*. |
| 83 | **Sticky-sidebar TOC or collapsible-top?** Recommended: sticky-sidebar for > 800 words / 4+ H2s; collapsible-top for shorter mobile-first docs. Canon: NN/g *TOC Best Practices* (2023). |
| 84 | **All four interactive features, or a subset?** Recommended: all four — none of them cost more than ~1 KB. Canon: Wattenberger *Why React isn't great for actually building websites*. |
| 85 | **Code theme — light, dark, or auto?** Recommended: auto (follows OS `prefers-color-scheme`). Canon: WCAG 2.2 §1.4.3. |
| 86 | **Does the document have a clear H1 title?** Recommended: yes — H1 becomes the page `<title>` and is excluded from the TOC. |
| 87 | |
| 88 | ## Distinct from |
| 89 | |
| 90 | **`md-review`** — that converter renders diff blocks + severity-tagged margin annotations. This one renders prose + tables + code + callouts. |
| 91 | **`md-slides`** — that converter splits on `---` boundaries into slides. This one renders one continuous document. |
| 92 | **`marketing/landing/`** — that generates landing pages from scratch (no markdown input). This converts existing markdown. |
| 93 | |
| 94 | ## Output artifact |
| 95 | |
| 96 | `{default_output_dir}/doc-{slug}.html` (path resolved by orchestrator's `output_path_resolver.py`; collision suffix `-2`, `-3`, … by default). |
| 97 | |
| 98 | ## References |
| 99 | |
| 100 | Shihipar — *Claude Code HTML output* (Medium, 2026) |
| 101 | Tufte — *Envisioning Information* (1990), ch. 2 "Micro/Macro Readings" |
| 102 | NN/g — *Table of Contents Best Practices* (2023) |
| 103 | WCAG 2.2 — §1.4.3 contrast, §2.4.5 multiple ways |
| 104 | Wattenberger — *Why React isn't great for actually building websites* |
| 105 | See `references/` for full citations |
| 106 |
Discussion
Browse more free Claude skills.