Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.
Documents
Marp Slide Expert
Try itMarp / marpit slide syntax, layout, and asset prep
What it does
Convert content into Marp slide decks that render cleanly via . The constraints below are non-obvious pitfalls confirmed against marpit.marp.app — read once, apply always.
The skill document
Marp Expert
Convert 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.
Hard rules
These break rendering silently if violated:
- Only
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. - Wrap paths containing spaces in
<...>:![bg](). Without the brackets, marp silently treats the space as URL terminator. - Inline SVG requires
--html. Marp escapes HTML by default, so a `` diagram renders as a page of visible source code. Any deck containing inline SVG must be rendered withmarp deck.marp.md --html --pdf --allow-local-files. See "Smart drawing" below. - Slides overflow when content is too dense. Marp has no auto-shrink. Split proactively.
- Max 15 visible lines per slide. Count every rendered line — paragraph lines, bullet items, table rows (including the header row), code block lines, quote/callout-box lines. Over 15 and the bottom of the slide gets cut off in the PDF. See "15-line hard cap" below.
先分清:页级版式 vs 图级版式
这是两种完全不同的东西,用错工具是最常见的返工原因:
| 页级版式(slide layout) | 图级版式(diagram) | |
|---|---|---|
| 排的是 | 一页的内容块——文字、表格、图 | 一张图内部的节点和连线 |
| 手段 | CSS grid + `` / marp 语法 | 内联 `` |
要不要 --html | 不需要 | 需要 |
| 文件 | references/layout-patterns.md | references/infographics-svg/ |
| 例子 | 三栏对比、双栏各带表、上下分区 | 阶梯、箭头串、2×2、桑基、泳道、便当格 |
判断:这一页要表达「内容之间的结构关系」吗?
- 是(要看谁依赖谁、怎么演进、卡在哪)→ 画图
- 否(只是内容多,并排放不下)→ 排版式
Layout patterns (cheat sheet)
| 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 | `` — 并列论点、两套方案 |
| Three columns | `` — 演进/挑战/对策,每栏 ≤ 5 行 |
| Main + side | `` (2:1) — 左要点右佐证,最常用 |
| Top / bottom split | `` — 上结论下证据 |
| Single image + text | ![bg left contain opacity:.9]() — image left, text right |
| Multi-image, no text | ![bg]() ![bg]() — horizontal row by default |
| Text + 2 images | ![bg right vertical]() + ![bg]() — img(a) right column, img(b) fills left, content overlays left |
| Portrait multi-image | Add vertical to each: ![bg vertical]() etc. |
| Infographic / 关系图 | <!-- _class: diagram --> + inline `` — 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 & relation diagrams — 内联 SVG
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:
- No blank lines inside the SVG block — markdown cuts the HTML block at the first blank line and the diagram vanishes without an error.
- Unique
marker/clipPathids 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. - No
---inside the SVG — it is still parsed as a page break. - Font sizes come from CSS classes, never a
font-sizeattribute — the deck'sstyle:block already definessvg .t / .tb / .sm / .qt / .lbl(see references/style-bootstrap.md), so one edit re-sizes every diagram. - Add
<!-- _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. - Keep nodes ≤ 7 per diagram. Past that, split the page or fall back to a table.
- One colour family + one accent. Three or more hues turn muddy under projection.
- Geometry must not contradict the data. A funnel drawn layer-by-layer with drifting slopes reads as a stack of plates, and casual band widths imply proportions the numbers don't support. Draw one generatrix through all bands, add rim + spout, and label the diagram "band width is schematic" — exact proportions belong in a sankey. See flow-cycle.
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.
Content density — the 15-line hard cap
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:
- Title + 1 image → ≤ 8 body lines. Images eat vertical space even though they don't count as lines.
- Text only → up to 15 lines, but aim for 10–12; long lines are hard to read from the back row.
- Code blocks → ≤ 10 lines per slide. More than that, split at logical boundaries (function def, between classes) or move to an appendix slide.
- Tables → ≤ 8 rows incl. header, ≤ 4 columns. Over 4 columns, the table overflows horizontally instead of vertically.
- Bulleted lists → ≤ 10 items. Past that, split around a logical midpoint or convert to a table.
- Never split a bullet or a table row across slides — a bullet cut in half, or a table with a dangling header, reads as broken.
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.
Image asset prep
When converting external image URLs (HTML course content, scraped diagrams):
- Download all unique images to a local
assests/(orassets/) directory. - Compress each to ≤ 100 KB using Pillow + sips. Convert complex diagrams with many colors to JPEG q=82; keep simple line art as PNG with
optimize=True. - Rename with a chapter prefix so paths are stable across slides:
L1_1.1-01_section-overview.pnginstead of raw URL slugs. - Build a URL → local-name mapping before generating slides so replacements are deterministic.
For full prep workflow, read references/asset-prep.md.
CSS bootstrap
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:
- CJK font stack (PingFang SC / Microsoft YaHei / Noto Sans CJK SC)
- Cover gradient (deep navy-black
#1a1a2e→ red#c0392b, white text) - Divider slides (deep blue-grey
#2c3e50background + red#e74c3cheading) - Tables (red
#c0392bheader +#f5f5f5alternating rows) — plus thedisplay: table !importantfix, without which marp's default theme makes every table half-width - H1/H2/H3 color scale (red / deep blue-grey / bright red)
- Code blocks (dark
#1a1a2ebackground + green#2ecc71text) - SVG helpers:
svgsizing, CJK font forsvg text, the.t / .tb / .sm / .qt / .lbltype scale, andsection.cover/divider/diagram footer { display: none } - Page-level column layouts:
.cols-2/.cols-3/.cols-main/.split-hgrids, plus in-columnh3/p/ul/tablescaling
Copy the style: |- block verbatim into a new deck's frontmatter.
Speaker notes (HTML comments — invisible in the PDF)
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:
- Notes are never rendered: absent from the generated HTML, PDF, and PPTX. They exist only in the
.mdsource, which is where the speaker reads them. - Notes do not count toward the 15-line cap — they cost zero lines and can be as long as needed.
- One note block per slide, placed after that slide's content and before the
---separator. - Never write
---inside a note — it is still parsed as a page break and will split the slide. - Also useful for draft markers that must not ship:
<!-- TODO: add real pricing data -->. - Marpit directives (
<!-- _class: ... -->,<!-- _paginate: -->) also use<!-- -->but start with_and stay active. Speaker notes must not start with_. - First fix for an over-15-line slide: ask "is this for the audience or for me?" If it's for you, move the whole thing into a comment — it costs no lines. Only content the audience genuinely needs to read is worth a new slide.
Workflow
- Read source markdown; identify cover, dividers (H1/H2), and content sections (H3+).
- Prep image assets (see asset-prep above).
- Generate slides with these defaults:
- One
## section→ divider slide. - One
### topic→ content slide titled## Topic. - Split so no slide exceeds 15 rendered lines (see the count table above). When a section is long, decide per-paragraph: audience-facing content splits onto a new slide; speaker-only context moves into a
<!-- -->note. - Never split mid-sentence, mid-bullet, or mid-table-row. Cut at paragraph boundaries.
- Image placement per the cheat sheet above.
- For column layouts, use the
cols-*/split-hclasses in references/layout-patterns.md — remember the 15-line cap is per slide, so a 3-column slide gets ~5 lines per column. - For relationship diagrams, route by relationship type → pick the skeleton in references/infographics-svg/skeletons/INDEX.md → optionally swap the shell via metaphors.md → mark the slide
<!-- _class: diagram -->.
- One
- Count lines on every generated slide before rendering. Any slide > 15 → fix now.
- Lint and render:
node scripts/svg-lint.mjs deck.marp.md, thenmarp deck.marp.md --pdf --allow-local-files(add--htmlif 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. - Iterate on overflow / awkward layout.
Quick troubleshooting
| 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 does not wrap — shorten the label or split it across 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 `` 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.
Related skills
Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.
Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.
Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.
Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.
Write, debug, and tune Playwright specs with locator strategy, trace diagnosis, and CI-aware timeouts.
More from mebusw
Browse all skillsManage scheduled Zoom meetings, cloud recordings, and account users from agent workflows via Server-to-Server OAuth.
Use when user asks to "generate workshop outline", "create training agenda", "design course structure", "build workshop schedule", or requests help planning...
Generate engaging short video scripts (30s-3min) for social media platforms like WeChat Channels, Douyin, Xiaohongshu, TikTok, and Reels. Use when user asks...
Use when user asks to "write WeChat article", "create 公众号文章", "write media content", or needs engaging articles for WeChat Official Accounts. Applies Pyramid...
Use Playwright via Bash `exec` tool (not the built-in browser tool) to fetch GasBuddy fuel prices for a target city/area (especially Toronto/North York), and...
Audit and improve existing Agent Skills (SKILL.md files) against the agentskills.io standard. Use this skill whenever the user wants to optimize, polish, imp...