Install
openclaw skills install @mebusw/marp-slide-expertopenclaw skills install @mebusw/marp-slide-expertConvert content into Marp slide decks that render cleanly via marp --pdf --allow-local-files. The constraints below are non-obvious pitfalls confirmed against marpit.marp.app — read once, apply always.
These break rendering silently if violated:
 for images. Marp does NOT support ![w:60% center], ![width:200px], or any inline image sizing. If you need an image, it must be a background.<...>: . Without the brackets, marp silently treats the space as URL terminator.--html. Marp escapes HTML by default, so a <svg> diagram renders as a page of visible source code. Any deck containing inline SVG must be rendered with marp deck.marp.md --html --pdf --allow-local-files. See "Smart drawing" below.这是两种完全不同的东西,用错工具是最常见的返工原因:
| 页级版式(slide layout) | 图级版式(diagram) | |
|---|---|---|
| 排的是 | 一页的内容块——文字、表格、图 | 一张图内部的节点和连线 |
| 手段 | CSS grid + <div> / marp 语法 | 内联 <svg> |
要不要 --html | 不需要 | 需要 |
| 文件 | references/layout-patterns.md | references/infographics-svg/ |
| 例子 | 三栏对比、双栏各带表、上下分区 | 阶梯、箭头串、2×2、桑基、泳道、便当格 |
判断:这一页要表达「内容之间的结构关系」吗?
| Scenario | Pattern |
|---|---|
| Cover | <!-- _class: cover --> + # Title + ## Subtitle |
| Major section break | <!-- _class: divider --> + # Section Name |
| Text only / text + table | Plain markdown, default content slide |
| Two columns | <div class="cols cols-2"> — 并列论点、两套方案 |
| Three columns | <div class="cols cols-3"> — 演进/挑战/对策,每栏 ≤ 5 行 |
| Main + side | <div class="cols cols-main"> (2:1) — 左要点右佐证,最常用 |
| Top / bottom split | <div class="split-h"> — 上结论下证据 |
| Single image + text |  — image left, text right |
| Multi-image, no text |   — horizontal row by default |
| Text + 2 images |  +  — img(a) right column, img(b) fills left, content overlays left |
| Portrait multi-image | Add vertical to each:  etc. |
| Infographic / 关系图 | <!-- _class: diagram --> + inline <svg> — see below |
15-line cap is per slide, not per column — a 3-column slide gives each column only ~5 lines. Splitting into three pages usually reads better than cramming one.
For full syntax and orientation logic, read references/image-syntax.md. For column layout details and the CSS block, read references/layout-patterns.md.
Infographics and relation diagrams (staircase, arrow chain, 2×2 matrix, value tree, sankey, funnel, swimlanes, bento grid, argument map, pie, radar, balance wheel) are drawn as inline SVG written directly in the .md — not as images. A PNG has to be redrawn to change one number; SVG travels with the file, keeps its text searchable, stays vector in the PDF, and recolours with one hex value.
# 含内联 SVG 的 deck 必须带 --html,否则 SVG 被转义成一整页源码
marp deck.marp.md --html --pdf --allow-local-files
The diagram module is a four-layer pipeline. Work through them in order — each answers exactly one question:
| Layer | Question | File |
|---|---|---|
| ① structures 选题 | 这份内容能画哪几张图? | structures.md |
| ② skeletons 体裁 | 用什么图形承载这个关系? | skeletons/INDEX.md |
| ③ metaphors 语气 | 在同一副骨架上换什么叙事外壳? | metaphors.md |
| ④ styles 配色 | 什么颜色、什么字阶? | styles/INDEX.md |
| craft 笔法 | 画布多大?线怎么连?字怎么排? | craft/ |
Pick the skeleton in two steps: relationship first, then metaphors. Relationship families (hierarchy / sequence / cycle / comparison / matrix / framework / strategy / mapping / growth / composition / profile / balance) route to a skeleton. The narrative layer — staircase, pyramid, flywheel, iceberg, funnel, bridge, mountain — swaps the shell on that same skeleton without changing the routing. That separation is what lets the skeleton library grow without the metaphors table ever changing.
| Relationship | Answers | Skeleton |
|---|---|---|
| Sequence | what happens first, then next | Arrow chain |
| Hierarchy (depth 3+) | where value comes from | Value tree |
| Matrix (2 independent dimensions) | who gets the money, who doesn't | 2×2 quadrant |
| Growth / evolution | where we are, what's next | Staircase |
| Flow with magnitude | where does the volume go | Sankey |
| Parallel tracks | who did what, when | Swimlanes |
| Argument | what backs this claim | Toulmin map |
| Composition | part-to-whole shares, 1–3 pies | Pie / donut |
| Profile | multi-dimension score shape | Radar |
| Balance | one object, current vs target | Balance wheel |
Non-negotiables when writing SVG into markdown:
marker / clipPath ids per diagram (a1, a2, …) — in a merged single-HTML export every SVG shares one DOM, so duplicate ids make all arrowheads resolve to the first definition.--- inside the SVG — it is still parsed as a page break.font-size attribute — the deck's style: block already defines svg .t / .tb / .sm / .qt / .lbl (see references/style-bootstrap.md), so one edit re-sizes every diagram.<!-- _class: diagram --> when diagram content reaches the bottom of the viewBox (axis titles, legends, footnotes) — the CSS hides the footer on those slides so they don't collide.Before shipping, run both gates — the first catches what is certainly wrong, the second catches what merely looks wrong:
node scripts/svg-lint.mjs deck.marp.md # 6 deterministic checks
marp deck.marp.md --html --images png -o check # render and actually look
Trust the rendered PNG, never the SVG source.
Full templates with coordinate formulas, connector-semantics table, visual-hierarchy rules, and QA checklist: references/infographics-svg/. Every skeleton, metaphors and style, one per slide: examples/infographic-gallery.marp.md.
Every slide renders at most 15 lines of content. Count them before shipping. HTML comments are excluded.
Count each of these as one line:
| Element | How to count |
|---|---|
| Paragraph | One visual line as wrapped, ≈ 38 CJK chars or ≈ 75 Latin chars per line at default font. A 4-sentence paragraph ≈ 3 lines. |
| Bullet / numbered item | 1 line per item, plus 1 more if it wraps |
| Table | 1 line per row, including the header row. A 4-column × 4-row table = 5 lines. |
| Code block | 1 line per code line — no exceptions, no partial credit |
Quote / tip / warn box (>) | Every wrapped line inside the box |
| Heading | Count it — ## costs 1 line before the body starts |
Speaker note / any HTML comment <!-- --> | 0 lines — comments never render, so they never count |
A slide is a budget of 15 total, not 15 per element. ## title (1) + table 6 rows (6) + 2 bullets (2) + closing paragraph (2 lines) = 11 → fine. Add a 5-line code block and you're at 16 → split.
Speaker notes and other HTML comments do not count — they never render. So an overflowing slide can often be fixed by moving detail into a note rather than cutting it.
Corollary limits that follow from the 15-line cap:
Counting rule of thumb: CJK ≈ 1 line per 38 characters; Latin ≈ 1 line per 75 characters. A 600-character Chinese paragraph is already ~16 lines on its own.
For splitting heuristics, read references/slide-density.md.
When converting external image URLs (HTML course content, scraped diagrams):
assests/ (or assets/) directory.optimize=True.L1_1.1-01_section-overview.png instead of raw URL slugs.For full prep workflow, read references/asset-prep.md.
All UPerform / openclaw marp decks share one palette (red + deep navy-black theme). The complete style block lives in references/style-bootstrap.md. The palette is calibrated against openclaw_lesson01.md and the fde_lesson*.marp.md family.
It includes:
#1a1a2e → red #c0392b, white text)#2c3e50 background + red #e74c3c heading)#c0392b header + #f5f5f5 alternating rows) — plus the display: table !important fix, without which marp's default theme makes every table half-width#1a1a2e background + green #2ecc71 text)svg sizing, CJK font for svg text, the .t / .tb / .sm / .qt / .lbl type scale, and section.cover/divider/diagram footer { display: none }.cols-2 / .cols-3 / .cols-main / .split-h grids, plus in-column h3/p/ul/table scalingCopy the style: |- block verbatim into a new deck's frontmatter.
Different audiences need different things. Anything only the speaker needs — background, transitions, backup numbers, Q&A prep — goes in an HTML comment <!-- ... -->:
## Three deployment modes
- Public cloud: fastest to launch
- On-premise: compliance first
- Hybrid: best cost
<!--
Speaker notes:
- "Hybrid" is the newest option; long-time customers ask about it most.
- Case: a bank went on-premise, 8M contract.
- If asked about pricing, jump to the table on the next slide.
-->
Rules:
.md source, which is where the speaker reads them.--- separator.--- inside a note — it is still parsed as a page break and will split the slide.<!-- TODO: add real pricing data -->.<!-- _class: ... -->, <!-- _paginate: -->) also use <!-- --> but start with _ and stay active. Speaker notes must not start with _.## section → divider slide.### topic → content slide titled ## Topic.<!-- --> note.cols-* / split-h classes in references/layout-patterns.md — remember the 15-line cap is per slide, so a 3-column slide gets ~5 lines per column.<!-- _class: diagram -->.node scripts/svg-lint.mjs deck.marp.md, then marp deck.marp.md --pdf --allow-local-files (add --html if the deck contains inline SVG) and visually check first 5 pages plus a sample from middle/end. For a deck with diagrams, render every page to PNG (--images png) — SVG geometry is not verifiable from the source.| Symptom | Likely cause |
|---|---|
| Image shows as markdown text instead of background | Path has unescaped spaces — wrap in <...> |
| Two images stack weirdly | Missing vertical keyword for portrait images |
| Content cut off at bottom | Slide has > 15 rendered lines — split, or move speaker-only detail into a <!-- --> note |
| Table rows truncated horizontally | Table too wide — keep ≤ 4 columns or shrink font in CSS |
| Table renders at half width | marp's default theme sets table { display: block } — the block box stretches but the inner anonymous table shrink-wraps. Fix with display: table !important (already in style-bootstrap) |
| Whole slide shows SVG source code | Missing --html at render time (columns do not need it — only inline SVG) |
| Table inside a column is half width | The display: table !important fix was dropped from the style: block |
| All styling gone — default theme, colours lost, columns collapsed | An HTML comment <!-- --> was placed inside the YAML frontmatter, breaking the YAML parse so the whole style: block is silently dropped. Comments go after the closing --- |
| Table inside a column overflows sideways | Column count too high for the narrower column — cut columns or add font-size: 0.66em |
| Column bottoms look ragged | Missing align-items: start on the grid |
| 3-column slide is an unreadable wall of text | The 15-line cap is per slide; a 3-column slide gets ~5 lines per column — split into separate pages |
| SVG fine in Obsidian reading mode but exports as source in PDF | Obsidian's built-in exporter flattens inline HTML/SVG — export with Marp CLI --html --pdf, or use the Enhanced PDF Export plugin. The deck is not at fault; see references/infographics-svg/craft/marp-compat.md §4 |
| Obsidian's Marp plugin exports SVG as source | Its export command omits --html; patch main.js (2 sites in the il() function) — see references/infographics-svg/craft/marp-compat.md §4.1 |
| SVG diagram disappeared entirely | A blank line inside the SVG block split the HTML block — remove it |
| One slide split into two | A --- inside the SVG or inside an HTML comment |
| All diagrams' arrowheads look identical | Duplicate marker id across SVGs in one merged HTML export — suffix per diagram |
| White text invisible on an arrow shape | Drawn as a hollow chevron — use a 5-point path (rectangle + right tip) and centre text on the rectangle |
| Chinese label overflows its node | SVG <text> does not wrap — shorten the label or split it across <tspan> lines |
| Funnel doesn't look like a funnel | Bands were drawn layer-by-layer with drifting slopes, or the rim/spout is missing — use the single-generatrix template (s=0.9, no gaps) in flow-cycle |
| SVG text ignores the deck's CSS classes | A font-size attribute was hardcoded, or the class was declared in an SVG-internal <style> instead of the frontmatter style: block |
| Axis title / legend overlaps the footer | Add <!-- _class: diagram --> to that slide |
| Slide split awkwardly | Cut mid-bullet or mid-table-row — re-split at paragraph boundaries |
| Speaker note text visible in the PDF | It's not inside an HTML comment — wrap it in <!-- ... --> |
| One slide became two / empty slide appeared | A --- inside an HTML comment is still a page break — remove it |
| Cover slide has wrong background | Missing <!-- _class: cover --> directive |
| PDF render fails silently on images | Path doesn't resolve from .md file location — invoke marp from a directory where the relative path is valid |
For deeper marpit syntax reference, consult https://marpit.marp.app/ — particularly the image-syntax and slide-layouts pages.