md-to-scrolldeck
Turns one Markdown document into a single self-contained vertical scroll-snap deck โ scrollytelling for the leader who will scroll it themselves or project it in a review.
Hard requirements
Nine rules the skill does not bend. Most exist because a specific failure
happened once and is documented in the skill's references/.
Never write the HTML from scratch
Copy scroll-deck-skeleton.md and replace its
YOUR ... HERE tokens. The chrome is load-bearing and the
shared verifier greps for most of it.
One Write call, no chat echo
The whole file goes out in a single Write. The generated
HTML is never echoed into chat โ summary plus file:// URL
only. This is what prevents Bedrock's
Truncated event message received.
No menu, no theme toggle, no PNG export
A deliberate divergence from visuals:visualize. A scroll
deck is one designed light-paper theme; dark slides are editorial
contrast, not a user setting. Do not add .viz-menu back.
Never invent content
Every figure, name, and quote comes from the source document โ never placeholder text or invented figures. A slide with no source line range is a slide that gets cut.
CriticalNo base64 font by default
The webfont comes from a CDN. Embedding it is a multi-megabyte single
line that trips the same Bedrock truncation.
--offline-font exists but warns first.
Not a horizontal carousel
No translateX slide transitions, no click-zone
navigation, no Reveal.js. Vertical scroll-snap only.
One file in, one file out
No multi-file batching, and the input Markdown is never modified. Existing output is overwritten silently but always reported.
Verifier must exit 0
Run the shared verifier with --profile deck against the
file just written. Every [FAIL] is fixed and rewritten
first โ never report a broken dot rail as [OK]. Waived
checklist items are [WARN], never silent.
xdg-open, never wslview
xdg-open on Linux and WSL, open on macOS.
wslview errors on HTML files.
Core principles
The markup is the easy part. What makes a hand-made deck beat a generated one is that someone decided what the document is about, and cut the rest.
Editorial compression
Not a heading-to-slide map(). Sections merge, split, or
get cut. Headings mark where an author changed topic; slides mark
where a presenter changes what the audience should be thinking about.
Read the whole document first
You need the ending to know what the opening should set up. No outlining after the first screenful.
Fixed skeleton
Progress bar, phase header, dot rail, observer, print styles โ all
copied, never improvised. references/checklist.md
enumerates every piece and the verifier checks each one is still wired.
Shape, not just sequence
Three to five structural phases โ WHY to
WHAT to NOW to NEXT to
START โ become data-phase values that the
header reads live.
Resilient by default
Content is visible without JavaScript, motion respects
prefers-reduced-motion, snap turns off under 767px, and
print gives every slide its own page.
Vary the treatment
No three consecutive slides look identical. The skeleton ships a variant rotation to rotate through.
Arguments and flags
Exactly one positional. Two or more input files is a hard stop โ
[FAIL] one Markdown file per run.
- <input.md>Required. Path to the source Markdown file, one per run. A missing path fails; a non-
.mdextension only warns. - --out <path>Explicit output path. Default is the input's directory and basename with an
.htmlextension โdocs/vision.mdbecomesdocs/vision.html. - --slides <n>Force a slide count instead of the curated estimate. Range 5 to 20.
- --outline-onlyPrint the nav-dot outline and stop. No HTML is written.
- --lang <code>Override
<html lang>, e.g.enorko. Inferred from the source when omitted. - --offline-fontEmbed the webfont as base64 instead of linking a CDN. Warned and discouraged.
- --no-openSkip the
xdg-open/openstep after writing.
Process
Three steps. Step 2 is where the quality is decided.
Parse arguments and validate the input
Resolve the output path, confirm the source is readable, and stop early on a missing file or a second positional.
Read the whole document, then outline
Compress the source into 5 to 20 narrative beats and print the
nav-dot outline with source line ranges and the cut list. Fewer than
5 beats warns that a visuals:visualize one-pager fits
better; more than 20 proposes a scope cut rather than silently
building 30 slides.
Build, write once, verify, deliver
Fill the skeleton, write the file in one call, then run the
skill-relative verifier, which must exit 0 โ every
[FAIL] gets fixed and rewritten before you report:
bash "$_skill/lib/vendor/verify-html.sh" --profile deck <out>.html
$_skill is resolved by the guard block atop
references/checklist.md (from
CLAUDE_PLUGIN_ROOT, else
HERMES_SKILL_DIR). Then the judgement items in that
checklist, open the file, and report
slides=<n> with the file:// URL, the
slide-id list, and what was cut.
Curation: merge, split, cut
Target roughly one slide per 30 to 40 source lines, then adjust by judgement. Treat it as a starting estimate, never as arithmetic.
Merge
- Consecutive headings are facets of one concept
- A section is a short list that fits as a card grid inside its parent's slide
- A section is only a bridge sentence
Split
- One heading contains two distinct claims
- A heading holds a long process the audience must follow step by step
- A table has more than about 5 rows and its rows are the actual content
Cut
- Reference lists and link dumps
- A caveat only the author cares about
- Anything repeating a point already made
| Source length | What that usually means | Typical deck |
|---|---|---|
| under 150 lines | Often a better one-pager than a deck | 5–8 slides |
| 150–400 lines | The comfortable range | 8–13 slides |
| 400–800 lines | Cutting starts to matter a lot | 12–18 slides |
| over 800 lines | Ask whether to split the document first | 15–20 slides |
Measured on the reference deck: 39 headings over 535 source lines became 15 slides. One section's four subsections collapsed into a single slide because they were four facets of one idea; the reference list at the end became zero slides. The ratio is not the point โ the judgement is.
What every deck ships with
Load-bearing chrome from the skeleton. The shared verifier greps for most of these by name.
01 / NN counter
Right-edge dot rail
scroll-snap-type: y mandatory
IntersectionObserver tracking
Arrow-key navigation
PageUp / PageDown / Home / End / Space
Per-slide scroll cues
Print-ready, one slide per page
prefers-reduced-motion
Visible without JavaScript
aria-labelledby per slide
Snap disabled under 767px
Korean word-break: keep-all
Pre-flight checklist
Five groups, verified against the written file rather than from memory.
The skill's vendored lib/vendor/verify-html.sh --profile deck
greps most of them; the rest are judgement calls.
Curation
Outline shown before any HTML; 5 to 20 slides matching that outline; narrative beats rather than a heading copy; cuts reported; every slide traceable to source lines; headlines read as a story; one idea per slide; treatments alternate.
Scroll chrome
Snap type and align present; dot count equals slide count in the same
order; progress bar wired to updateProgress(); counter
reads the real total; observer sets the active slide; keys navigate;
every slide but the last has a cue pointing at the next one.
Accessibility
Reduced-motion media query disables animation; content visible without
JS; every slide has aria-labelledby matching its own
heading id; <html lang> matches the source; no
horizontal overflow at 375px.
Snap turned off; progress bar, header, dot rail and cues hidden; every
slide breaking onto its own page; reveal animations forced visible;
dark slides setting print-color-adjust: exact.
Out of scope
Fails if present: a .viz-menu hamburger, a theme toggle or
.theme-dark class, PNG export or the
html-to-image CDN script, or a base64
@font-face the user did not ask for.
The common bug
Getting the cue chain off by one. The verifier cross-checks what a
plain grep cannot: every dot href resolving to a real
slide id in slide order, and each .next-cue pointing at
the next slide rather than any slide.
Related skills
The boundary is the deliverable, never the topic.
visuals:visualize
The general-purpose sibling for dashboards, infographics, posters, and horizontal decks; this skill owns the vertical scroll deck only. Use it when the output is not a vertical scroll deck โ including when the source is too short to be one.
visuals:excalidraw-diagram
When the deliverable is a diagram rather than a deck โ an
.excalidraw file rendered to PNG and audited against a
23-item checklist.