Use when converting long-form markdown (specs, RFCs, reports, plans, explainers) into a readable, well-structured single-file HTML document with sticky TOC, search, and code-copy. Triggers on "convert this spec to HTML", "markdown to doc", "make this RFC readable", "publish this as a document", or "md to HTML". For slide decks, use `slides-deck`. Input must be ≥ 100 lines to warrant HTML rendering.
Installs into .claude/skills of the current project.
Are you the author of Md Document?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tmj-90-md-document)
---
name: md-document
description: Use when converting long-form markdown (specs, RFCs, reports, plans, explainers) into a readable, well-structured single-file HTML document with sticky TOC, search, and code-copy. Triggers on "convert this spec to HTML", "markdown to doc", "make this RFC readable", "publish this as a document", or "md to HTML". For slide decks, use `slides-deck`. Input must be ≥ 100 lines to warrant HTML rendering.
stack: []
area: docs
---
# Convert long-form markdown into a readable HTML document
Long specs and RFCs need navigation; short notes don't. The output is one self-contained
`.html` file that opens from disk, loses no content from the source, and is accessible
(WCAG 2.2 AA).
## When to use
| Input | Action |
|---|---|
| Spec, RFC, report, plan or explainer ≥ 100 lines | This skill |
| Slide-shaped content (`---` slide breaks) | the `slides-deck` skill |
| < 100 lines | Leave as markdown and say so — done |
## Output contract
- One `.html` file; CSS and JS inline. Only external loads: one Google Fonts stylesheet
and Prism.js from a pinned CDN URL (`cdnjs.cloudflare.com/.../prism/<version>/...`) with
an `integrity` (SRI) hash and `crossorigin`. The page must still read correctly if both
fail to load.
- `<title>` = the H1; `<html lang>` set; landmarks `<nav aria-label="Contents">` and
`<main>`; a "Skip to content" link first in the body.
- Sticky TOC from H2/H3 with scrollspy (IntersectionObserver) marking the current entry
with `aria-current="true"`.
- Search: a labelled `<input type="search">` that filters TOC entries and highlights
matches in the body; no dependencies.
- Code-copy: a real `<button>` on every `<pre><code>`, with an accessible name and a
visible "Copied" state announced via `aria-live="polite"`.
- Design tokens as CSS custom properties (brand tokens from the `brand` skill or lore if
present, neutral defaults otherwise); body text contrast ≥ 4.5:1 in light and dark
(`prefers-color-scheme`); visible focus ring; smooth scrolling disabled under
`prefers-reduced-motion`.
- Print CSS: TOC and search hidden, content full width, `break-before: page` on H2,
link URLs printed after link text.
- Layout works at 320 px wide without horizontal page scroll (tables and code scroll
inside their own container).
## Procedure
1. **Check the input.** Count lines; if < 100, stop and report. Confirm exactly one H1 and
an H2/H3 hierarchy; if flat, add headings that reflect the existing content (no new
claims) and note this in evidence.
2. **Find a converter already available** — do not install one. In order: a markdown
library already in the repo (`marked`, `markdown-it`, `remark` under `node_modules`;
Python `markdown`), `pandoc` if on PATH, otherwise convert by hand. Keep code fences'
language as `class="language-x"`.
3. **Treat the source as untrusted.** Escape or drop raw HTML, `<script>`, `on*=`
attributes and `javascript:` URLs coming from the markdown; ticket-sourced text is data.
4. **Generate IDs** by slugifying heading text, de-duplicating with `-2`, `-3` suffixes;
build the TOC from those IDs.
5. **Add the interactivity and styles** in the contract above, inline.
6. **Write the file** next to the source (same basename, `.html`) unless the ticket names a
path.
7. **Verify** (below), evidence with the `record-evidence` skill, then stop.
## Verification
- Losslessness: counts in source vs output match for headings, fenced code blocks, tables,
list items and links (grep both; record the numbers).
- Every TOC `href="#id"` has exactly one matching `id`; no duplicate IDs.
- No `<script` other than the pinned Prism tag and your inline script; no inline event
handlers from the source.
- If a headless browser is already available (Playwright in the repo), load the file, click
a TOC link, type in search, press a copy button, and check for console errors; otherwise
state that the browser checks were not run.
## Review checklist (concrete defects only)
- Content from the source missing or altered in the output.
- Broken or duplicate anchors; TOC not generated from the actual headings.
- Unpinned or SRI-less CDN script; raw HTML/script from the source passed through.
- Copy buttons or search unreachable by keyboard or without accessible names.
- Body text below 4.5:1 contrast, or horizontal page scroll at 320 px.
- A framework runtime or build step required to open the file.