Turn Markdown into beautiful shareable cards — in one command.
CardMark renders your Markdown notes as polished social-ready images (PNG/SVG) with gorgeous themes, proper CJK typography, and emoji support. CLI, JavaScript library, and browser build in one zero-config package.
🚀 Try it online — no install: live editor on GitHub Pages
Most notes die in a .md file. CardMark turns them into images people actually share — for Weibo/Xiaohongshu posts, X/Twitter threads, Open Graph cards, or team wikis.
- 10 hand-tuned themes — aurora, midnight, paper, matcha, peach, noir, ocean, blossom, classic, ink
- 6 social size presets — X post, Open Graph, square, Xiaohongshu 3:4, Instagram 4:5, Story 9:16 (or any custom
WxH) - 9 font sets, CJK全覆盖 — Noto Sans/Serif SC, Noto Sans/Serif JP, Noto Sans/Serif KR, JetBrains Mono, Times New Roman, Georgia, Inter; downloaded on demand and cached. Chinese, Japanese, Korean all render correctly
- Emoji as art — every emoji (flags, ZWJ sequences, keycaps) renders as inline Twemoji vectors, so it survives PNG export everywhere
- Auto-fit layout — long notes shrink to fit short formats instead of clipping
- Split mode — one file with
---fences becomes a numbered card deck - Pure rendering — text becomes vector paths; no headless browser, no Puppeteer, no font installation
- Zero bloat — the browser build (
cardmark/browser) has no Node APIs and no native modules
# one-off, no install
npx cardmark note.md -t matcha -f png -o card.png
# or install globally
npm i -g cardmarkThat's it. note.md → card.png, sized for X/Twitter by default.
cardmark note.md [options]
cardmark < note.md > card.svg # stdin/stdout
cardmark deck.md --split # one card per --- fence → card-1.png, card-2.png …
Options:
-t, --theme <name> aurora | midnight | paper | matcha | peach | noir | ocean |
blossom | classic | ink (default: aurora)
-s, --size <preset> x | og | square | xiaohongshu | instagram | story | 1200x900
-f, --format <fmt> svg | png (default: svg)
-o, --out <file> output file (stdout for svg; required for png)
-b, --byline <text> footer text, e.g. @yourhandle
-p, --padding <sm|md|lg|xl>
--split split on --- / === fences into numbered cards
--no-footer
-F, --font <path> embed an extra .ttf/.otf/.woff (repeatable)
--font-set <name> override the theme's font set (see --list-font-sets)
--list-themes list themes and exit
--list-sizes list size presets and exit
--list-font-sets list font sets and exit
-h, --help show help
-v, --versionimport { renderPng, renderCard } from 'cardmark'
const png = await renderPng('# Hello 🌏\n\n中文 **and** english.', {
theme: 'midnight', // theme id, or a partial custom theme object
size: 'og', // preset id, or { width, height }
byline: '@you',
})
const { svg } = await renderCard('# Hello', { theme: 'matcha' })Fonts resolve automatically: the theme's font set is downloaded from pinned CDNs on
first use, then cached in ~/.cache/cardmark/fonts/ (override with
CARDMARK_CACHE_DIR). Offline? CardMark falls back to host system fonts.
Pick the typography that matches your content with --font-set (or fontSet in
renderCard options — it overrides the theme default):
| font set | faces | good for |
|---|---|---|
default |
Noto Sans SC + JetBrains Mono | 中文 (default) |
serif |
Noto Serif SC + JetBrains Mono | 中文正文/文艺风 |
latin |
Inter + Georgia + JetBrains Mono | English posts |
editorial |
Times New Roman + Noto Serif SC | 杂志/社论风 |
japanese |
Noto Sans JP + JetBrains Mono | 日本語 |
japanese-serif |
Noto Serif JP + JetBrains Mono | 日本語・明朝 |
korean |
Noto Sans KR + JetBrains Mono | 한국어 |
korean-serif |
Noto Serif KR + JetBrains Mono | 한국어·명조 |
cjk-all |
SC + JP + KR together | 混排(中日韩同屏) |
cardmark japanese.md --font-set japanese -f png -o card.pngAny theme field can be overridden; unspecified fields inherit from midnight:
import { renderCard } from 'cardmark'
await renderCard(md, {
theme: {
id: 'brand',
label: 'Brand',
appearance: 'dark',
accent: '#ff5c8a',
cardBackground: '#1a1025',
},
})The cardmark/browser entry point is platform-neutral — no node: imports, no
native rasterizer — so it bundles cleanly with Vite/webpack/esbuild:
import { renderCard, loadFontSet } from 'cardmark/browser'
const fonts = await loadFontSet('default') // cached in memory after first load
const { svg } = await renderCard(md, { theme: 'ocean', fonts })Rasterize to PNG by drawing the SVG onto a <canvas> — see
web/src/App.tsx for a complete working editor (~180 lines).
The editor in web/ is a ready-to-deploy Vite app — deployed live at
frankfu916.github.io/cardmark — with
live preview, theme/size/font-set switchers, PNG/SVG download, and
copy-to-clipboard. It redeploys automatically on every push to main via
GitHub Actions (see pages.yml).
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
classic and all sizes: see assets/. Regenerate everything with the
snippet in examples/.
| preset | px | use for |
|---|---|---|
x |
1200×1350 | X/Twitter image posts |
og |
1200×630 | Open Graph / link cards |
square |
1080×1080 | generic square posts |
xiaohongshu |
1080×1440 | 小红书 image notes |
instagram |
1080×1350 | Instagram portrait |
story |
1080×1920 | Stories / Reels / 短视频封面 |
Any custom size works too: -s 1200x900.
Markdown ──marked──▶ block model ──layout──▶ satori element tree
──satori──▶ SVG (text as vector paths, emoji as Twemoji)
──resvg──▶ PNG
No browser, no canvas, no system font setup. The npm package ships a vendored
satori build so npm i cardmark pulls exactly three dependencies.
npm install
npm test # 29 unit tests across parse/themes/render + web
npm run build # build core (tsc + satori vendoring)
npm run dev:web # local editor at localhost:5173- Multi-language font sets (JP / KR / CJK-all)
- Online editor (GitHub Pages)
- Syntax-highlighted code blocks
- Image embeds (
) - GitHub Action for rendering cards in CI
Issues and PRs welcome. Keep PRs focused; run npm test and npm run lint first.









