
Rainman Card
- 1 installs
- Updated March 25, 2026
- deusyu/rainman-card
Transforms content into PNG visuals across six molds: long reading card, infographic, multi-card, sketchnote, comic, and whiteboard.
About
Casts text content into PNG visuals in multiple layouts such as reading cards, infographics, sketchnotes, comics, and whiteboards. A developer uses it to turn content into shareable images saved to Downloads.
- Six molds: long card, infograph, multi-card, sketchnote, comic, whiteboard
- Outputs PNG files to ~/Downloads/
Rainman Card by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,200 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/deusyu/rainman-card --skill rainman-cardAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| Last updated | March 25, 2026 |
| Repository | deusyu/rainman-card ↗ |
What it does
Transforms content into PNG visuals across six molds: long reading card, infographic, multi-card, sketchnote, comic, and whiteboard.
Files
rainman-card: 铸
Forked from ljg-card by @lijigang.
将内容铸成可见的形态。内容进去,PNG 出来。模具决定形状。
参数
| 参数 | 模具 | 尺寸 | 说明 |
|---|---|---|---|
-l(默认) | 长图 | 1080 x auto | 单张阅读卡,内容自动撑高 |
-i | 信息图 | 1080 x auto | 内容驱动的自适应视觉布局 |
-m | 多卡 | 1080 x 1440 | 自动切分为多张阅读卡片 |
-v | 视觉笔记 | 1080 x auto | 手绘风格 sketchnote,动态选择风格路线 |
-c | 漫画 | 1080 x auto | 日式黑白漫画风格,动态选择漫画家视觉语言 |
-w | 白板 | 1080 x auto | 白板马克笔风格,结构化框图+箭头+彩色标记 |
约束
本 skill 输出为视觉文件(PNG),不适用 L0 中的 Org-mode、Denote 和 ASCII-only 规范。
共享基础
获取内容
- URL --> WebFetch 获取
- 粘贴文本 --> 直接使用
- 文件路径 --> Read 获取
文件命名
从内容提取标题或核心思想作为 {name}(中文直接用,去标点,≤ 20 字符)。
截图工具
node ~/.claude/skills/rainman-card/assets/capture.js <html> <png> <width> <height> [fullpage]依赖:~/.claude/skills/rainman-card/node_modules/ 中的 playwright。如报错:
cd ~/.claude/skills/rainman-card && npm install playwright && npx playwright install chromiumarxiv 检测
内容来源为 arxiv 论文时(URL 含 arxiv.org、文件名含 paper 标签、或内容中出现 arxiv ID),提取 arxiv ID(格式 XXXX.XXXXX),在卡片 footer 右侧显示。适用于 -l 和 -i 模具(-m 多卡无 footer,不适用)。
交付
1. 报告文件路径
品味准则
所有模具共享。执行任何模具前,先 Read references/taste.md,作为视觉质量底线贯穿全流程。
核心:反 AI 生成痕迹——禁 Inter 字体、禁纯黑、禁三等分卡片、禁居中 Hero、禁 AI 文案腔、禁假数据。
执行
根据参数选择模具,Read references/taste.md + 对应的 mode 文件,按步骤执行:
-l(默认):长图
Read references/mode-long.md,按其步骤执行。
模板:assets/long_template.html
-i:信息图
Read references/mode-infograph.md,按其步骤执行。
模板:assets/infograph_template.html
-m:多卡
Read references/mode-poster.md,按其步骤执行。
模板:assets/poster_template.html
-v:视觉笔记
Read references/mode-sketchnote.md,按其步骤执行。
模板:assets/sketchnote_template.html
-c:漫画
Read references/mode-comic.md,按其步骤执行。
模板:assets/comic_template.html
-w:白板
Read references/mode-whiteboard.md,按其步骤执行。
模板:assets/whiteboard_template.html
node_modules/
#!/usr/bin/env node
const path = require('path');
async function main() {
const args = process.argv.slice(2);
const htmlPath = args[0];
const outputPath = args[1];
const width = parseInt(args[2]) || 1200;
const height = parseInt(args[3]) || 1600;
const fullpage = args[4] === 'fullpage';
if (!htmlPath || !outputPath) {
console.error('Usage: node capture.js <html> <png> [width] [height] [fullpage]');
process.exit(1);
}
let chromium;
try {
chromium = require('playwright').chromium;
} catch {
console.error('Playwright not found. Run: npx playwright install chromium');
process.exit(1);
}
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setViewportSize({ width, height: fullpage ? 800 : height });
const fileUrl = 'file://' + path.resolve(htmlPath);
await page.goto(fileUrl, { waitUntil: 'networkidle' });
await page.waitForTimeout(500);
if (fullpage) {
const bodyHeight = await page.evaluate(() => document.body.scrollHeight);
await page.setViewportSize({ width, height: bodyHeight });
await page.waitForTimeout(300);
await page.screenshot({
path: path.resolve(outputPath),
type: 'png',
clip: { x: 0, y: 0, width, height: bodyHeight }
});
} else {
await page.screenshot({
path: path.resolve(outputPath),
type: 'png',
clip: { x: 0, y: 0, width, height }
});
}
await browser.close();
console.log('OK: ' + path.resolve(outputPath));
}
main().catch(err => {
console.error(err.message);
process.exit(1);
});
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700;900&family=DM+Sans:wght@400;500;700&display=swap');
:root {
--bg: #FAFAFA;
--ink: #1a1a1a;
--ink-mid: #555;
--ink-light: #999;
--white: #FAFAFA;
--accent: #1a1a1a;
--tone: #E8E8E8;
--serif: 'Noto Serif SC', 'KingHwa_OldSong', Georgia, serif;
--sans: 'DM Sans', 'PingFang SC', system-ui, sans-serif;
--mono: 'SF Mono', 'Menlo', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1080px; background: var(--bg); }
.page {
width: 1080px;
background: var(--bg);
position: relative;
}
/* ink spatter texture */
.page > .grain {
position: absolute;
inset: 0;
filter: url(#inkgrain);
opacity: 0.03;
pointer-events: none;
z-index: 100;
}
.colophon {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 28px 60px 36px;
border-top: 3px solid var(--ink);
}
.colophon .who {
display: flex;
align-items: center;
gap: 14px;
}
.colophon .who img {
width: 40px; height: 40px;
border-radius: 50%; object-fit: cover;
}
.colophon .who span {
font: 400 24px/1 var(--sans);
color: var(--ink-mid);
}
.colophon .arxiv {
font: 400 22px/1 var(--mono);
color: var(--ink-light);
}
{{CUSTOM_CSS}}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute">
<!-- ink grain filter -->
<filter id="inkgrain">
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="4" stitchTiles="stitch"/>
<feColorMatrix type="saturate" values="0"/>
</filter>
<!-- halftone dot pattern -->
<filter id="halftone">
<feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="1"/>
<feColorMatrix type="saturate" values="0"/>
<feComponentTransfer>
<feFuncA type="discrete" tableValues="0 1"/>
</feComponentTransfer>
</filter>
<!-- rough ink edge -->
<filter id="roughen">
<feTurbulence type="turbulence" baseFrequency="0.04" numOctaves="4" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" xChannelSelector="R" yChannelSelector="G"/>
</filter>
</svg>
<div class="page">
<div class="grain"></div>
{{CONTENT_HTML}}
<div class="colophon">
<div class="who">
<img src="file:///Users/daniel/.claude/skills/rainman-card/assets/logo.png" alt="">
<span>{{SOURCE}}</span>
</div>
{{ARXIV_LINE}}
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=DM+Sans:wght@400;500;700&display=swap');
:root {
--bg: #F2F2F2;
--green: #B8D8BE;
--pink: #E91E63;
--yellow: #FFF200;
--ink: #2D2926;
--ink-light: #5C5350;
--white: #FFFFFF;
--serif: 'DM Serif Display', 'KingHwa_OldSong', Georgia, 'Noto Serif SC', serif;
--sans: 'DM Sans', 'KingHwa_OldSong', -apple-system, 'PingFang SC', system-ui, sans-serif;
--mono: 'SF Mono', 'Menlo', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1080px; background: var(--bg); }
.page {
width: 1080px;
background: var(--bg);
position: relative;
}
.page > .grain {
position: absolute;
inset: 0;
filter: url(#noise);
opacity: 0.04;
pointer-events: none;
z-index: 100;
}
mark { background: rgba(255,242,0,0.45); color: inherit; padding: 4px 8px; }
.colophon {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28px 60px 36px;
border-top: 1px solid rgba(45,41,38,0.08);
}
.colophon .who {
display: flex;
align-items: center;
gap: 14px;
}
.colophon .who img {
width: 40px; height: 40px;
border-radius: 50%; object-fit: cover;
}
.colophon .who span {
font: 400 24px/1 var(--sans);
color: var(--ink-light);
}
.colophon .arxiv {
font: 400 22px/1 var(--mono);
color: var(--ink-light);
}
{{CUSTOM_CSS}}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute">
<filter id="noise">
<feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
<feColorMatrix type="saturate" values="0"/>
</filter>
</svg>
<div class="page">
<div class="grain"></div>
{{CONTENT_HTML}}
<div class="colophon">
<div class="who">
<img src="file:///Users/daniel/.claude/skills/rainman-card/assets/logo.png" alt="">
<span>{{SOURCE}}</span>
</div>
{{ARXIV_LINE}}
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
:root {
--bg: {{BG_COLOR}};
--text: #1D1D1F;
--text-mid: #6E6E73;
--text-dim: #ACACB0;
--accent: {{ACCENT_COLOR}};
--rule: #E5E5EA;
--font: 'KingHwa_OldSong', 'PingFang SC', system-ui, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 1080px;
background: var(--bg);
}
.card {
width: 1080px;
background: var(--bg);
padding: 64px 72px 52px;
display: flex;
flex-direction: column;
}
/* ── Title ── */
.title-area {
flex-shrink: 0;
margin-bottom: 52px;
}
.title-area h1 {
font: 700 84px/1.15 var(--font);
color: var(--text);
letter-spacing: -0.03em;
margin-bottom: 22px;
}
.title-area::after {
content: '';
display: block;
width: 52px;
height: 3px;
background: var(--accent);
}
/* ── Content ── */
.content {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.content p {
font: 400 36px/1.7 var(--font);
color: var(--text);
margin-bottom: 28px;
}
.content .highlight {
font: 500 40px/1.55 var(--font);
color: var(--text);
padding: 14px 0 14px 26px;
border-left: 3px solid var(--accent);
margin: 36px 0;
}
.content h2 {
font: 600 42px/1.4 var(--font);
color: var(--text);
margin: 44px 0 22px;
letter-spacing: -0.02em;
}
.content h2:first-child {
margin-top: 0;
}
.content .subtitle {
font: 400 22px/2 var(--font);
color: var(--text-dim);
letter-spacing: 0.15em;
text-transform: uppercase;
margin-bottom: 28px;
}
.content .item {
margin-bottom: 36px;
}
.content .item:last-child {
margin-bottom: 0;
}
.content .item .label {
font: 500 36px/1.5 var(--font);
color: var(--text);
margin-bottom: 6px;
}
.content .item p {
font: 400 32px/1.65 var(--font);
color: var(--text-mid);
margin-bottom: 0;
}
.content blockquote {
margin: 0 0 28px;
padding-left: 26px;
border-left: 3px solid var(--rule);
}
.content blockquote p {
font: 300 36px/1.7 var(--font);
color: var(--text-mid);
margin-bottom: 6px;
}
.content strong {
font-weight: 600;
color: var(--text);
}
.content .divider {
height: 1px;
background: var(--rule);
margin: 36px 0;
}
.content ul {
list-style: none;
margin-bottom: 28px;
}
.content ul li {
font: 400 36px/1.7 var(--font);
color: var(--text);
padding: 4px 0 4px 28px;
position: relative;
}
.content ul li::before {
content: '·';
position: absolute;
left: 0;
color: var(--text-mid);
}
/* ── Drop Cap ── */
.content .dropcap::first-letter {
font: 700 128px/0.82 'KingHwa_OldSong', Georgia, serif;
float: left;
margin: 4px 16px 0 -4px;
color: var(--accent);
}
/* ── End Mark ── */
.content::after {
content: '∎';
display: block;
text-align: right;
font-size: 16px;
color: var(--accent);
opacity: 0.4;
margin-top: 40px;
}
/* ── Footer ── */
.footer {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 52px;
padding-top: 22px;
border-top: 1px solid var(--rule);
}
.footer .author {
display: flex;
align-items: center;
gap: 14px;
}
.footer .avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.footer .author-name {
font: 400 24px/1.5 var(--font);
color: var(--text-mid);
letter-spacing: 0.03em;
}
.footer .arxiv {
font: 400 22px/1.5 'Menlo', 'SF Mono', monospace;
color: var(--text-dim);
letter-spacing: 0.02em;
}
</style>
</head>
<body>
<div class="card">
{{TITLE_BLOCK}}
<div class="content">
{{BODY_HTML}}
</div>
<div class="footer">
<div class="author">
<img class="avatar" src="file:///Users/daniel/.claude/skills/rainman-card/assets/logo.png" alt="">
<span class="author-name">{{SOURCE}}</span>
</div>
{{ARXIV_LINE}}
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
:root {
--bg: {{BG_COLOR}};
--text: #1D1D1F;
--text-mid: #6E6E73;
--text-dim: #ACACB0;
--accent: {{ACCENT_COLOR}};
--rule: #E5E5EA;
--font: 'KingHwa_OldSong', 'PingFang SC', system-ui, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 1080px;
height: 1440px;
overflow: hidden;
background: var(--bg);
}
.card {
width: 1080px;
height: 1440px;
background: var(--bg);
padding: 64px 72px 52px;
display: flex;
flex-direction: column;
}
/* ── Running title (continuation cards) ── */
.header {
flex-shrink: 0;
margin-bottom: 40px;
}
.header .running-title {
font: 400 24px/1.5 var(--font);
color: var(--text-dim);
letter-spacing: 0.08em;
}
/* ── Title ── */
.title-area {
flex-shrink: 0;
margin-bottom: 52px;
}
.title-area h1 {
font: 700 84px/1.15 var(--font);
color: var(--text);
letter-spacing: -0.03em;
margin-bottom: 22px;
}
.title-area::after {
content: '';
display: block;
width: 52px;
height: 3px;
background: var(--accent);
}
/* ── Content ── */
.content {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.content p {
font: 400 36px/1.7 var(--font);
color: var(--text);
margin-bottom: 28px;
}
.content .highlight {
font: 500 40px/1.55 var(--font);
color: var(--text);
padding: 14px 0 14px 26px;
border-left: 3px solid var(--accent);
margin: 36px 0;
}
.content h2 {
font: 600 42px/1.4 var(--font);
color: var(--text);
margin: 44px 0 22px;
letter-spacing: -0.02em;
}
.content h2:first-child {
margin-top: 0;
}
.content .subtitle {
font: 400 22px/2 var(--font);
color: var(--text-dim);
letter-spacing: 0.15em;
text-transform: uppercase;
margin-bottom: 28px;
}
.content .item {
margin-bottom: 36px;
}
.content .item:last-child {
margin-bottom: 0;
}
.content .item .label {
font: 500 36px/1.5 var(--font);
color: var(--text);
margin-bottom: 6px;
}
.content .item p {
font: 400 32px/1.65 var(--font);
color: var(--text-mid);
margin-bottom: 0;
}
.content blockquote {
margin: 0 0 28px;
padding-left: 26px;
border-left: 3px solid var(--rule);
}
.content blockquote p {
font: 300 36px/1.7 var(--font);
color: var(--text-mid);
margin-bottom: 6px;
}
.content strong {
font-weight: 600;
color: var(--text);
}
.content .divider {
height: 1px;
background: var(--rule);
margin: 36px 0;
}
.content ul {
list-style: none;
margin-bottom: 28px;
}
.content ul li {
font: 400 36px/1.7 var(--font);
color: var(--text);
padding: 4px 0 4px 28px;
position: relative;
}
.content ul li::before {
content: '·';
position: absolute;
left: 0;
color: var(--text-mid);
}
/* ── Footer ── */
.footer {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto;
padding-top: 22px;
border-top: 1px solid var(--rule);
}
.footer .author {
display: flex;
align-items: center;
gap: 14px;
}
.footer .avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.footer .author-name {
font: 400 24px/1.5 var(--font);
color: var(--text-mid);
letter-spacing: 0.03em;
}
.footer .page-info {
font: 400 20px/1.5 var(--font);
color: var(--text-dim);
letter-spacing: 0.03em;
}
</style>
</head>
<body>
<div class="card">
{{HEADER_BLOCK}}
{{TITLE_BLOCK}}
<div class="content">
{{BODY_HTML}}
</div>
<div class="footer">
<div class="author">
<img class="avatar" src="file:///Users/daniel/.claude/skills/rainman-card/assets/logo.png" alt="">
<span class="author-name">{{SOURCE}}</span>
</div>
<span class="page-info">{{PAGE_INFO}}</span>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;600;700&family=Architects+Daughter&family=Patrick+Hand&family=DM+Sans:wght@400;500;700&display=swap');
:root {
--bg: #FFFEF9;
--ink: #2B2B2B;
--ink-light: #6B6B6B;
--accent: #E85D3A;
--marker: #FFE066;
--block: #E8F4EC;
--hand: 'Caveat', 'PingFang SC', cursive;
--hand-alt: 'Architects Daughter', 'Patrick Hand', cursive;
--sans: 'DM Sans', 'PingFang SC', system-ui, sans-serif;
--mono: 'SF Mono', 'Menlo', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1080px; background: var(--bg); }
.page {
width: 1080px;
background: var(--bg);
position: relative;
}
.page > .grain {
position: absolute;
inset: 0;
filter: url(#noise);
opacity: 0.03;
pointer-events: none;
z-index: 100;
}
/* subtle dot grid like notebook paper */
.page > .dotgrid {
position: absolute;
inset: 0;
background-image: radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px);
background-size: 28px 28px;
pointer-events: none;
z-index: 0;
}
.colophon {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 28px 60px 36px;
border-top: 2px dashed rgba(0,0,0,0.08);
}
.colophon .who {
display: flex;
align-items: center;
gap: 14px;
}
.colophon .who img {
width: 40px; height: 40px;
border-radius: 50%; object-fit: cover;
}
.colophon .who span {
font: 400 24px/1 var(--sans);
color: var(--ink-light);
}
.colophon .arxiv {
font: 400 22px/1 var(--mono);
color: var(--ink-light);
}
{{CUSTOM_CSS}}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute">
<filter id="noise">
<feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
<feColorMatrix type="saturate" values="0"/>
</filter>
<!-- reusable arrow marker -->
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--ink)" />
</marker>
</defs>
</svg>
<div class="page">
<div class="grain"></div>
<div class="dotgrid"></div>
{{CONTENT_HTML}}
<div class="colophon">
<div class="who">
<img src="file:///Users/daniel/.claude/skills/rainman-card/assets/logo.png" alt="">
<span>{{SOURCE}}</span>
</div>
{{ARXIV_LINE}}
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Kalam:wght@400;700&family=DM+Sans:wght@400;500;700&display=swap');
:root {
--bg: #FAFBFC;
--board: #F2F3F5;
--ink: #1A1A1A;
--ink-light: #555;
--red: #D1495B;
--blue: #2B6CB0;
--green: #2D8659;
--orange: #D97706;
--marker-bg: rgba(255, 237, 74, 0.35);
--hand: 'Kalam', 'PingFang SC', cursive;
--marker: 'Permanent Marker', 'PingFang SC', cursive;
--sans: 'DM Sans', 'PingFang SC', system-ui, sans-serif;
--mono: 'SF Mono', 'Menlo', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1080px; background: var(--bg); }
.board {
width: 1080px;
background: var(--board);
position: relative;
border-radius: 8px;
overflow: hidden;
}
/* subtle whiteboard surface texture - fine crosshatch */
.board > .surface {
position: absolute;
inset: 0;
background-image:
linear-gradient(90deg, rgba(0,0,0,0.012) 1px, transparent 1px),
linear-gradient(rgba(0,0,0,0.012) 1px, transparent 1px);
background-size: 48px 48px;
pointer-events: none;
z-index: 0;
}
/* whiteboard frame edge */
.board::before {
content: '';
position: absolute;
inset: 0;
border: 3px solid #E0E0E0;
border-radius: 8px;
pointer-events: none;
z-index: 50;
}
.colophon {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 24px 60px 32px;
border-top: 2px solid rgba(0,0,0,0.06);
}
.colophon .who {
display: flex;
align-items: center;
gap: 14px;
}
.colophon .who img {
width: 40px; height: 40px;
border-radius: 50%; object-fit: cover;
}
.colophon .who span {
font: 400 24px/1 var(--sans);
color: var(--ink-light);
}
.colophon .arxiv {
font: 400 22px/1 var(--mono);
color: var(--ink-light);
}
{{CUSTOM_CSS}}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute">
<defs>
<marker id="arrow-r" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--red)" />
</marker>
<marker id="arrow-b" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--blue)" />
</marker>
<marker id="arrow-k" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--ink)" />
</marker>
<marker id="arrow-g" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--green)" />
</marker>
</defs>
</svg>
<div class="board">
<div class="surface"></div>
{{CONTENT_HTML}}
<div class="colophon">
<div class="who">
<img src="file:///Users/daniel/.claude/skills/rainman-card/assets/logo.png" alt="">
<span>{{SOURCE}}</span>
</div>
{{ARXIV_LINE}}
</div>
</div>
</body>
</html>
{
"name": "ljg-card",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"playwright": "^1.58.2"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}
{
"dependencies": {
"playwright": "^1.58.2"
}
}
rainman-card
English | 中文
Claude Code Skill by Rainman. Forked from ljg-card by @lijigang.
Content in, PNG out. Six molds, one command.
Molds
| Flag | Mold | Size | Description |
|---|---|---|---|
-l (default) | Long card | 1080 x auto | Single reading card, auto-height |
-i | Infograph | 1080 x auto | Content-driven adaptive visual layout |
-m | Multi-card | 1080 x 1440 | Auto-split into multiple reading cards |
-v | Sketchnote | 1080 x auto | Hand-drawn style visual note |
-c | Comic | 1080 x auto | Manga-style B&W comic |
-w | Whiteboard | 1080 x auto | Marker-style board layout |
Installation
Option 1: Manual
git clone https://github.com/deusyu/rainman-card.git
cp -r rainman-card ~/.claude/skills/rainman-card
cd ~/.claude/skills/rainman-card && npm install playwright && npx playwright install chromiumOption 2: Symlink
git clone https://github.com/deusyu/rainman-card.git ~/path/to/rainman-card
ln -s ~/path/to/rainman-card ~/.claude/skills/rainman-card
cd ~/.claude/skills/rainman-card && npm install playwright && npx playwright install chromiumRestart Claude Code after installation.
Usage
/rainman-card -i 这段内容做成信息图/rainman-card -l 这篇文章做成长图/rainman-card -v 这个概念做成视觉笔记/rainman-card -c 这个故事画成漫画You can also paste text, provide a URL, or give a file path — the skill will fetch and process the content automatically.
Output
All cards are saved to ~/Downloads/ as PNG files.
Design Philosophy
Every card follows a strict anti-AI-slop taste guideline:
- No Inter font
- No pure black (
#000000) - No three-equal-column cards
- No centered hero layouts
- No AI buzzwords ("empower", "seamless", "unleash")
- No fake data (
99.99%,John Doe) - Color-matched shadows, not grey defaults
- Max/min element ratio ≥ 10:1
- Mobile-readable: body ≥ 36px, annotations ≥ 24px
What Changed from ljg-card
- Logo/avatar: replaced with Rainman's
- Default signature:
Rainman @deusyu - All template paths updated to
~/.claude/skills/rainman-card/ - Everything else preserved — same molds, same taste guidelines, same quality
Credits
Original skill by @lijigang. The design system, taste guidelines, and six-mold architecture are entirely his work. This fork only changes branding and paths.
License
MIT
rainman-card
English | 中文
Rainman 的 Claude Code Skill。Fork 自 @lijigang 的 ljg-card。
内容进去,PNG 出来。六种模具,一条命令。
模具
| 参数 | 模具 | 尺寸 | 说明 |
|---|---|---|---|
-l(默认) | 长图 | 1080 x auto | 单张阅读卡,内容自动撑高 |
-i | 信息图 | 1080 x auto | 内容驱动的自适应视觉布局 |
-m | 多卡 | 1080 x 1440 | 自动切分为多张阅读卡片 |
-v | 视觉笔记 | 1080 x auto | 手绘风格 sketchnote |
-c | 漫画 | 1080 x auto | 日式黑白漫画 |
-w | 白板 | 1080 x auto | 白板马克笔风格 |
安装
git clone https://github.com/deusyu/rainman-card.git
cp -r rainman-card ~/.claude/skills/rainman-card
cd ~/.claude/skills/rainman-card && npm install playwright && npx playwright install chromium重启 Claude Code 生效。
使用
/rainman-card -i 这段内容做成信息图/rainman-card -l 这篇文章做成长图支持粘贴文本、URL、文件路径,自动获取内容。
产出
所有卡片保存至 ~/Downloads/,PNG 格式。
与 ljg-card 的区别
仅改了品牌相关内容:头像、默认署名(Rainman @deusyu)、模板路径。六种模具、品味准则、设计体系完全保留原版。
致谢
原版 skill 由 @lijigang 设计和开发。设计体系、品味准则和六模具架构均为其原创。本 fork 仅修改了品牌和路径。
许可
MIT
模具:漫画(-c)
核心信条
黑与白之间的张力,就是全部。
漫画模具用日式黑白漫画的视觉语言讲故事。不是加了漫画边框的排版——是用分格节奏、黑白对比、集中线和留白来制造戏剧性。颜色几乎不用,最多一个灰色调(网点灰)。力量来自墨色的浓淡和构图的紧张感。
步骤 1:读取模板
Read ~/.claude/skills/ljg-card/assets/comic_template.html
模板提供:
- 字体加载(Noto Serif SC + DM Sans)
- CSS 变量(
--bg,--ink,--ink-mid,--ink-light,--white,--accent,--tone) - SVG 滤镜:
#inkgrain(墨迹纹理)、#halftone(网点)、#roughen(毛边) .colophon署名栏{{CUSTOM_CSS}}和{{CONTENT_HTML}}插槽
步骤 2:理解内容,选择风格
2.1 提取叙事要素
从内容中提取:
- 核心冲突/张力:这段内容里什么和什么在对抗?
- 3-5 个关键时刻:可以变成"格子"的场景或概念
- 情绪弧线:从什么状态到什么状态?
- 视觉锚点:最有画面感的那个概念
2.2 选择漫画风格
| 风格 | 视觉特征 | 触发信号 | CSS 变量覆盖 |
|---|---|---|---|
| 大友克洋 — 精密废墟 | 极细线条密集排列、机械/建筑细节、灰阶丰富、透视精确 | 技术/系统/架构/复杂机制、信息密度高 | --tone: #D0D0D0 |
| 井上雄彦 — 水墨留白 | 大面积留白、墨色浓淡渐变、笔触可见、极简构图 | 哲学/沉思/美学/人文、需要呼吸感 | --tone: #E8E0D8 |
| 三浦建太郎 — 暗黑压迫 | 大面积纯黑、极高对比、密集纹理、压迫感强 | 冲突/困境/黑暗面/挣扎、情绪强烈 | --bg: #F0F0F0; --tone: #C0C0C0 |
| 松本大洋 — 生猛粗线 | 粗细不均的线条、不规则构图、能量感、看似粗糙实则精确 | 运动/能量/创意/突破、有冲击力 | --tone: #E0E0E0 |
| 谷口治郎 — 静谧精描 | 建筑级精细线条、克制表情、银盐照片般的灰阶、安静 | 日常/观察/细节/安静的力量 | --tone: #E5E5E5 |
选择原则:
- 默认用「井上雄彦」——最通用的黑白美学
- 内容有技术/系统复杂度 → 大友克洋
- 内容有强烈冲突或黑暗面 → 三浦建太郎
- 内容有能量/突破/运动感 → 松本大洋
- 内容是安静的观察/日常 → 谷口治郎
系列一致性规则(同源多卡):当从同一个文本源(同一篇文章、同一本书、同一份报告)生成多张漫画卡片时,必须对整个文本源做一次整体判断,选定一个风格,所有卡片统一使用。不要按单个概念逐张选风格——风格跳跃会破坏阅读连续感。判断依据是文本源的整体气质,不是某个局部章节的情绪。
步骤 3:设计画面
3.1 漫画视觉元素工具箱
所有元素用 CSS + SVG 实现。黑白为主,灰色网点为辅,不用彩色。
分格系统(Panel Layout)
漫画的核心是分格。用 CSS Grid 实现不规则分格:
/* 基础分格 */
.panel {
border: 3px solid var(--ink);
background: var(--white);
position: relative;
overflow: hidden;
padding: 28px 32px;
}
/* 出血格(内容冲破边框) */
.panel-bleed {
border: none;
margin: -3px;
z-index: 2;
}
/* 斜切分格 */
.panel-slanted {
clip-path: polygon(0 0, 100% 8%, 100% 100%, 0 92%);
}集中线(Focus Lines)
用于强调关键概念,制造"震撼"感:
.focus-lines {
position: relative;
}
.focus-lines::before {
content: '';
position: absolute;
inset: 0;
background: repeating-conic-gradient(
var(--ink) 0deg 0.5deg,
transparent 0.5deg 5deg
) center/100% 100%;
opacity: 0.06;
pointer-events: none;
}速度线(Speed Lines)
表现动态、变化、冲击:
.speed-lines {
background-image: repeating-linear-gradient(
90deg,
transparent,
transparent 4px,
var(--ink) 4px,
var(--ink) 4.5px
);
opacity: 0.08;
}对话泡 / 思想泡
.speech-bubble {
background: var(--white);
border: 2.5px solid var(--ink);
border-radius: 20px;
padding: 16px 22px;
position: relative;
font: 700 32px/1.4 var(--serif);
}
.speech-bubble::after {
content: '';
position: absolute;
bottom: -16px;
left: 40px;
border: 8px solid transparent;
border-top-color: var(--ink);
}
/* 思想泡(圆形尾巴) */
.thought-bubble {
border-radius: 50% 50% 50% 50% / 40% 40% 60% 60%;
}
.thought-bubble::after {
width: 12px; height: 12px;
border-radius: 50%;
background: var(--ink);
border: none;
bottom: -20px;
}
/* 吼叫泡(锯齿边缘) */
.shout-bubble {
clip-path: polygon(
0% 20%, 5% 0%, 15% 15%, 25% 0%, 35% 10%,
50% 0%, 65% 10%, 75% 0%, 85% 15%, 95% 0%,
100% 20%, 95% 35%, 100% 50%, 95% 65%, 100% 80%,
95% 100%, 85% 85%, 75% 100%, 65% 90%, 50% 100%,
35% 90%, 25% 100%, 15% 85%, 5% 100%, 0% 80%,
5% 65%, 0% 50%, 5% 35%
);
background: var(--white);
padding: 28px 36px;
font: 900 38px/1.3 var(--serif);
}网点灰(Screentone)
漫画中灰色不是灰色,是网点:
.screentone {
background-image: radial-gradient(circle, var(--ink) 1px, transparent 1px);
background-size: 5px 5px;
opacity: 0.15;
}
/* 渐变网点 */
.screentone-gradient {
background-image: radial-gradient(circle, var(--ink) 1px, transparent 1px);
background-size: 5px 5px;
-webkit-mask-image: linear-gradient(to bottom, black, transparent);
mask-image: linear-gradient(to bottom, black, transparent);
opacity: 0.2;
}拟声词 / 效果字(Onomatopoeia)
大号倾斜的效果字,漫画的灵魂:
.sfx {
font: 900 80px/1 var(--serif);
color: var(--ink);
transform: rotate(-8deg) skewX(-5deg);
letter-spacing: -3px;
text-shadow: 3px 3px 0 var(--tone);
-webkit-text-stroke: 1px var(--ink);
}墨色浓淡(Ink Wash)
井上雄彦路线的核心——用 CSS gradient 模拟水墨:
.ink-wash {
background: linear-gradient(
135deg,
var(--ink) 0%,
rgba(26,26,26,0.6) 20%,
rgba(26,26,26,0.15) 50%,
transparent 70%
);
}3.2 排版原则
漫画排版不是文章排版。
- 文字竖排可选:日式漫画原本竖排阅读。关键文字可以用
writing-mode: vertical-rl制造漫画感 - 字号对比极端:标题 120px+、正文 32px、旁注 20px,比例 ≥ 6:1
- 粗体 = 重音:漫画中加粗是"说话声音变大了"
- 留白 = 沉默:大面积空白不是没画完,是刻意的停顿
- 黑色块 = 重压:纯黑区域制造压迫感和戏剧性
3.3 分格构图(按风格)
大友克洋 — 精密废墟
+------------------+--------+
| | 细节 |
| 全景大格 | 小格 |
| (技术架构图) +--------+
| | 数据 |
+--------+---------+ 小格 |
| 文字格 | 文字格 | |
+--------+---------+--------+特征:格子方正、线条精密、信息密度高
井上雄彦 — 水墨留白
+-------------------------+
| |
| 大面积留白 |
| 核心概念 |
| (墨色渐变背景) |
| |
+------------+------------+
| 窄长格 | 窄长格 |
| 观点 A | 观点 B |
+------------+------------+特征:大量留白、格子少、每格信息少但重
三浦建太郎 — 暗黑压迫
+--+--------------------+--+
|黑| |黑|
|边| 核心冲突大格 |边|
| | (大量纯黑+白字) | |
+--+---------+----------+--+
| 密集格 | 密集格 |
| (网点灰) | (纯黑白) |
+------------+-----------+特征:大面积纯黑、白字反转、压迫感
松本大洋 — 生猛粗线
+-------+
| 歪斜格 \
/ +--------+
+ 核心概念 |
| (大号粗字) |
+-----+ +------+
| 不规则 |
| 小格 |
+---------+特征:格子歪斜、边框粗细不均、充满动能
谷口治郎 — 静谧精描
+-------------------------+
| 精细场景描写格 |
| (长横格,电影宽银幕) |
+------------+------------+
| | |
| 正方格 | 正方格 |
| 细节 A | 细节 B |
| | |
+------------+------------+
| 底部叙事格 |
+-------------------------+特征:规整格子、宽银幕横格、安静均匀
步骤 4:写 CSS + HTML
所有 CSS 写入 {{CUSTOM_CSS}}。所有 HTML 写入 {{CONTENT_HTML}}。
CSS 从零写——class 名反映内容,不是通用名。
核心约束:
- 配色限于黑(
--ink)、白(--white/--bg)、灰(--tone)三值,偶尔用--ink-mid - 如需强调,用纯黑反白(白字黑底),不用彩色
- 边框统一 2.5-3px,模拟笔触
- 至少一个格子要"出血"(内容冲破常规边距)制造漫画感
替换变量:
| 变量 | 内容 |
|---|---|
{{CUSTOM_CSS}} | 全部 CSS(包括 :root 覆盖) |
{{CONTENT_HTML}} | 全部 HTML |
{{SOURCE}} | 署名 |
{{ARXIV_LINE}} | arxiv 时填入,否则空 |
写入:/tmp/ljg_cast_comic_{name}.html
步骤 5:自检
- [ ] 一眼看上去像漫画页面吗?如果看着像"加了边框的普通排版",重做
- [ ] 有没有至少 3 个分格?漫画没有分格就不是漫画
- [ ] 黑白对比是否强烈?有没有纯黑区域?
- [ ] 有没有至少 1 个漫画特有元素(集中线/速度线/对话泡/效果字/网点灰)?
- [ ] 格子大小是否有对比?(一个大格 + 几个小格,不是全部等大)
- [ ] 是否避免了彩色?灰色只用网点灰
--tone - [ ] 正文字号 ≥ 32px?标题 ≥ 72px?
- [ ] 有没有一个格子让人第一眼被吸进去?
- [ ] 是否避免了等分、对称、均匀间距?
步骤 6:截图
node ~/.claude/skills/ljg-card/assets/capture.js /tmp/ljg_cast_comic_{name}.html ~/Downloads/{name}.png 1080 800 fullpage模具:信息图(-i)
核心信条
样式为思想而服务。
不存在"默认布局"。每一张信息图的视觉形式,都从这个思想的形状中生长出来。模板只提供画布材质(字体、颜色、噪点、署名)。构图、排版、布局——全部由你根据内容从零设计。
步骤 1:读取模板
Read ~/.claude/skills/ljg-card/assets/infograph_template.html
模板极简,只提供:
- 字体加载(DM Serif Display + DM Sans + KingHwa_OldSong)
- CSS 变量(
--bg,--green,--pink,--yellow,--ink,--ink-light,--white,--serif,--sans,--mono) - SVG 噪点纹理(自动叠加)
.colophon署名栏{{CUSTOM_CSS}}和{{CONTENT_HTML}}插槽
没有 header、没有 canvas、没有 utility class。 所有 CSS 都写在 {{CUSTOM_CSS}},所有 HTML 都写在 {{CONTENT_HTML}}。
步骤 2:理解思想
2.1 提取元信息
- 标题:≤ 15 字
- 副标题:一句话核心 ≤ 30 字
- 来源:用户提供则用,否则
Rainman @deusyu - REF 编码:
REF—{领域} / {主题}(写在画面上的位置由你决定)
2.2 三个维度
对内容做三个判断:
密度(决定画面的呼吸节奏):
| 密度 | 核心内容量 | 画面特征 |
|---|---|---|
| 稀 | ≤ 50 字可说清 | 一个巨大元素统治画面。留白 ≥ 60%。震撼来自克制。 |
| 中 | 50-200 字 | 有结构的布局。2-3 个主要区块。留白 30-50%。 |
| 密 | 200+ 字 | 多区块密集排布。标注、网格、分层。留白 ≤ 30%。实验室手册感。 |
结构(决定画面的几何):
| 结构 | 信号 | 视觉几何 |
|---|---|---|
| 单点 | 一个核心概念 | 一个锚点占据重心,其余退后 |
| 对比 | A vs B、旧 vs 新 | 分裂、对立、两极 |
| 层级 | 底层支撑上层 | 金字塔、阶梯、嵌套 |
| 流程 | 先后顺序 | 纵向瀑布、时间轴、管道 |
| 辐射 | 核心 + 衍生 | 中心放射、hub-spoke |
| 并列 | 多个并行概念 | 非对称网格(禁止等分) |
情绪(决定画面的温度):
| 情绪 | 排版风格 |
|---|---|
| 沉思的 | 大量留白,serif 主导,低对比 |
| 锐利的 | 强对比,大字,粉色弹点 |
| 温暖的 | 绿色为主,圆润布局,手写感 |
| 技术的 | mono 标注,网格底纹,数据密集 |
2.3 输出判断
密度:[稀/中/密]
结构:[单点/对比/层级/流程/辐射/并列]
情绪:[沉思/锐利/温暖/技术]
色调:[沉思/锐利/温暖/技术/科研/创意/商业/默认]
锚点:[画面中最大的那个元素是什么?放在哪里?]2.4 色调选择
根据内容主题选择最匹配的色调。色调决定三个核心变量,在 {{CUSTOM_CSS}} 中覆盖模板默认值。
| 色调 | --bg | --green(结构色) | --pink(弹点色) | 触发信号 |
|---|---|---|---|---|
| 沉思 | #F5F2ED | #C4B5A0 | #8B5E3C | 哲学、认知、本质、意义、存在 |
| 锐利 | #EDEDF0 | #6E6E80 | #D93025 | 批判、解构、争议、对立、辩论 |
| 温暖 | #F7F4EF | #C8B898 | #C17F4E | 人文、情感、生活、故事、成长 |
| 技术 | #F0F3F7 | #8EAAB8 | #1A936F | 架构、系统、算法、代码、工程 |
| 科研 | #F2F6F4 | #7DAE96 | #D68C45 | 论文、实验、数据、研究、发现 |
| 创意 | #F6F3F2 | #C0A89C | #B8432F | 艺术、设计、创作、美学、灵感 |
| 商业 | #F4F3F0 | #A8A498 | #2D6A4F | 商业、金融、市场、投资、战略 |
| 默认 | #F2F2F2 | #B8D8BE | #E91E63 | 无法归类时 |
密度修正(在基准色上微调):
- 稀:弹点色可提升 10-15% 饱和度。画面空旷,弹点需要更强存在感
- 中:使用基准值
- 密:结构色降低 10-15% 饱和度,避免密集排版时的视觉疲劳
选择原则:
- 扫描内容高频关键词和主题,匹配最贴近的色调
- 情绪维度和色调可以不同——「沉思的情绪 + 技术的色调」完全合法
- 宁可用默认也不要错配——错误的色调比无色调更糟
- 色调一旦选定,整张图统一使用
步骤 3:设计画面
3.1 材质系统
字体与墨色(所有色调共享):
| 变量 | 值 | 用途 |
|---|---|---|
--serif | DM Serif Display → KingHwa_OldSong | 标题、大字、金句 |
--sans | DM Sans → KingHwa_OldSong | 正文、标签 |
--mono | SF Mono | 数据标注、REF 编码 |
--ink | #2D2926 | 主文字色 |
--ink-light | #5C5350 | 次要文字 |
动态色调(由步骤 2.4 决定):
| 变量 | 角色 | 90/8/2 |
|---|---|---|
--bg | 画布底色 | 90% 中性面 |
--green | 结构色——色块、边框、分区 | 8% 结构 |
--pink | 弹点——整张图 1-2 处精确命中 | 2% 强调 |
覆盖方法——在 {{CUSTOM_CSS}} 最前面写:
:root {
--bg: #F0F3F7; /* 步骤 2.4 选定值 */
--green: #8EAAB8;
--pink: #1A936F;
}3.2 设计自由度
以下所有决策由你根据内容做出,没有默认值:
锚点位置 — 标题/核心元素可以在:
- 左上(传统)
- 正中(碑刻感)
- 右侧纵排(东亚美学)
- 底部(悬念揭示)
- 作为背景幽灵字(地形)
画面分割 — 可以是:
- 全画面不分割(稀密度)
- 横向分割(上下两个世界)
- 纵向分割(左右对比)
- 不规则分割(clip-path 斜切)
- 网格(密密度的实验室感)
文字大小 — 为手机端阅读优化(1080px 画布在手机上缩放约 2.8 倍):
- 最大元素和最小元素的比例 ≥ 10:1
- 可以用到 400px 的字(它不再是"文字",是"地形")
- 最小可读标注不低于 24px(手机上约 8.7px)
- 正文不低于 40px(手机上约 14.4px)
- 装饰性文字(REF 编码等不需细读的)最小 22px
色彩 — 90/8/2 法则(色值来自步骤 2.4 色调选择):
- 90% 中性色(
--bg+--white+--ink文字) - 8% 结构色(
--green一个区块) - 2% 弹点(
--pink一处精确命中)
3.3 密度指导
稀(≤ 50 字)
画面上 一个元素压倒一切。可能是:
- 一个 300-420px 的汉字/单词
- 一行金句横跨全宽
- 一个公式,周围是沉默
其余信息(词源、解释)以 24-28px 安静地待在角落或底部。不争夺注意力。
参考构图:
┌─────────────────────────┐
│ ref-code 22px │
│ │
│ │
│ 坐 │
│ 400px serif │
│ │
│ subtitle 36px │
│ │
│ ─── quote ─── │
│ 44px serif │
│ │
│ [colophon] │
└─────────────────────────┘中(50-200 字)
2-3 个区块,有主有次。锚点元素 120-180px,正文 40-44px,副标题 34-40px。
关键:不要把区块均匀排列。一个大的占 60%,其余挤在一起。或者一个全宽条带打断节奏。
参考构图(仅供启发,不是固定模板):
┌─────────────────────────┐
│ 标题 140px │
│ subtitle 36px │
├───────────┬─────────────│
│ │ │
│ 核心解释 │ 词源/数据 │
│ 40px │ 32px │
│ 2fr │ 1fr │
│ │ │
├───────────┴─────────────│
│ ██████ 深色全宽条带 ██████│
│ 核心公式 / 一句话 44px │
├─────────────────────────│
│ │
│ 金句 48px serif │
│ │
│ [colophon] │
└─────────────────────────┘密(200+ 字)
画面密集但有序。多个小区块。标注层、网格线可见。实验室手册感。
关键:密不等于挤。密是信息多但每条信息各就各位。用线条、编号、色块区分层级。正文 36-40px,标注 28-32px,标题 80-108px。
参考构图:
┌──────────┬──────────────┐
│ 标题 84px│ ref-code 22px│
│ sub 34px │ ×数据 28px │
├──────────┴──────────────│
│ ┌────┐ ┌────┐ ┌──────┐ │
│ │ 01 │ │ 02 │ │ │ │
│ │概念 │ │概念 │ │ 03 │ │
│ │36px│ │36px│ │ 大概念 │ │
│ └────┘ └────┘ │ 40px │ │
│ └──────┘ │
├─────────────────────────│
│ 标注区 · 28px mono │
│ 引用 · 数据来源 · 28px │
│ [colophon] │
└─────────────────────────┘3.4 反死亡清单
| 如果你发现自己在做这个... | 停下来 |
|---|---|
写了 .header { padding: 56px } | 你在用旧模板思维。从内容开始,不是从 header 开始。 |
| 每个区块都是白色背景 | 至少一个用 --green 或 --ink |
| 三列等宽 | 禁止。2fr 1fr、1fr 340px、一大两小。 |
| 标题居中 | 除非密度=稀且结构=单点。否则左对齐或非传统位置。 |
| 每张图都有"公式条带" | 这不是必须的。有些思想没有公式。 |
| 弹点色用了 3 处以上 | 回到 2 处。弹点是子弹。 |
| 没有一个元素超过 100px | 找到值得放大的那个。 |
| 所有文字都在 30-44px | 你没有制造张力。需要 ≥ 10:1 比例。 |
| 正文字号小于 36px | 手机上会不可读。最小正文 36px,最小标注 24px。 |
| 区块之间间距都一样 | 有意识地做疏密交替。 |
用 max-width 压短了本该一行放下的文字 | 画布 1080px。一句话能放一行就放一行。只在正文段落用 max-width 控制行宽(≤ 56ch),标题和金句不要限宽——让它自然呼吸到该停的地方。 |
步骤 4:写 CSS + HTML
所有 CSS 写入 {{CUSTOM_CSS}}。所有 HTML 写入 {{CONTENT_HTML}}。
CSS 从零写——不要复制之前任何版本的 class 名称或结构。每张图的 class 名应该反映这张图的内容(.etymology、.core-split、.timeline),不是通用名(.section、.panel、.label)。
替换变量:
| 变量 | 内容 |
|---|---|
{{CUSTOM_CSS}} | 这张图的全部 CSS |
{{CONTENT_HTML}} | 这张图的全部 HTML |
{{SOURCE}} | 署名 |
{{ARXIV_LINE}} | arxiv 时 <span class="arxiv">arxiv: XXXX.XXXXX</span>,否则空 |
写入:/tmp/ljg_cast_infograph_{name}.html
步骤 5:自检
唯一不变的检查:
- [ ] 这张图的视觉形式,是从内容的形状中生长出来的吗?
- [ ] 如果换一段完全不同的内容,这个布局还说得通吗?如果是——你做的是模板,不是设计。
- [ ] 最大元素和最小元素的比例 ≥ 10:1?
- [ ] 弹点色 ≤ 2 处?
- [ ] 色调是否与内容主题匹配?换个主题,这组颜色还合适吗?
- [ ] 有没有一个元素让人第一眼就被抓住?
- [ ] 留白是有意为之,还是剩下来的?
- [ ] 如果告诉别人"这是 AI 做的",他们会立刻相信吗?如果会——重做。
- [ ] 手机端阅读检查:正文 ≥36px?标注 ≥24px?行高 ≥1.6?在手机上缩放 2.8 倍后文字仍可舒适阅读?
步骤 6:截图
node ~/.claude/skills/ljg-card/assets/capture.js /tmp/ljg_cast_infograph_{name}.html ~/Downloads/{name}.png 1080 800 fullpage模具:长图(-l / 默认)
步骤 1:读取模板
Read ~/.claude/skills/ljg-card/assets/long_template.html
步骤 2:内容预处理
- 识别标题行(
#/##/###开头,或独立短行) - 识别引用块(
>开头) - 识别加粗(
**text**) - 识别金句:独立成段的短句(通常 < 25 字),承载核心洞察,用
.highlight渲染 - 按空行分割为段落列表
- 不做切分:所有内容放在一张卡内
步骤 2.5:色调感知
根据内容气质选择一组背景底色 + 强调色,让每张卡片和内容产生共振:
| 内容气质 | {{BG_COLOR}} | {{ACCENT_COLOR}} | 触发信号 |
|---|---|---|---|
| 思辨/哲学 | #FAF8F4 | #7C6853 | 认知、思维、本质、意义、哲学 |
| 技术/工程 | #F5F7FA | #3D5A80 | 架构、模型、算法、系统、代码 |
| 文学/叙事 | #FBF9F1 | #6B4E3D | 故事、人物、写作、文字、诗 |
| 科学/研究 | #F4F8F6 | #2D6A4F | 实验、数据、发现、论文、研究 |
| 默认 | #FAFAF8 | #4A4A4A | 无法归类时 |
判断依据:扫描内容中的高频关键词和主题,匹配最贴近的一组。不需要精确——宁可用默认也不要错配。
步骤 3:格式化为 HTML
基础元素:
- 普通段落 →
<p>文本</p> - 章节标题(##/### 级别) →
<h2>标题</h2> - 引用 →
<blockquote><p>引用</p></blockquote> - 加粗 →
<strong>文本</strong> - 列表 →
<ul><li>...</li></ul>
金句(独立成段的核心洞察短句,视觉突出):
<p class="highlight">金句文本</p>判断标准:独立成段、< 25 字、承载关键洞察。用 .highlight 而非 <p><strong>。
首字下沉(第一个正文段落): 第一个普通段落(非 .subtitle、.highlight、.item)添加 dropcap 类:
<p class="dropcap">段落正文...</p>仅首个正文段落使用,营造经典编辑排版的开篇仪式感。
条目组(有标题+正文的并列条目):
<div class="item">
<p class="label">条目标题</p>
<p>条目正文</p>
</div>副标题标签:
<p class="subtitle">标签文字</p>分割线(章节之间):
<div class="divider"></div>步骤 4:渲染模板
替换模板变量:
| 变量 | 规则 |
|---|---|
{{BG_COLOR}} | 步骤 2.5 确定的背景底色 |
{{ACCENT_COLOR}} | 步骤 2.5 确定的强调色 |
{{TITLE_BLOCK}} | 有标题时:<div class="title-area"><h1>标题</h1></div>;无标题时:空字符串 |
{{BODY_HTML}} | 步骤 3 生成的全部 HTML |
{{SOURCE}} | 来源/作者信息(用户提供则填入,否则 Rainman @deusyu) |
{{ARXIV_LINE}} | 内容来自 arxiv 论文时:<span class="arxiv">arxiv: XXXX.XXXXX</span>;否则空字符串 |
写入:/tmp/ljg_cast_long_{name}.html
步骤 5:截图
node ~/.claude/skills/ljg-card/assets/capture.js /tmp/ljg_cast_long_{name}.html ~/Downloads/{name}.png 1080 800 fullpage模具:多卡(-c)
步骤 1:读取模板
Read ~/.claude/skills/ljg-card/assets/poster_template.html
步骤 1.5:色调感知
与长图模具共享同一套色调系统。根据内容气质选择 {{BG_COLOR}} 和 {{ACCENT_COLOR}}:
| 内容气质 | {{BG_COLOR}} | {{ACCENT_COLOR}} | 触发信号 |
|---|---|---|---|
| 思辨/哲学 | #FAF8F4 | #7C6853 | 认知、思维、本质、意义、哲学 |
| 技术/工程 | #F5F7FA | #3D5A80 | 架构、模型、算法、系统、代码 |
| 文学/叙事 | #FBF9F1 | #6B4E3D | 故事、人物、写作、文字、诗 |
| 科学/研究 | #F4F8F6 | #2D6A4F | 实验、数据、发现、论文、研究 |
| 默认 | #FAFAF8 | #4A4A4A | 无法归类时 |
步骤 2:内容预处理
- 识别标题行(
#/##/###开头,或独立短行) - 识别引用块(
>开头) - 识别加粗(
**text**) - 识别金句:独立成段的短句(通常 < 25 字),承载核心洞察,用
.highlight渲染 - 按空行分割为段落列表
步骤 3:计算视觉重量
模板在 1080x1440 全分辨率渲染,正文 36px,行高 1.7。
- 普通段落:字符数 × 1.4
- 标题行(h1 首卡 84px):字符数 × 6.0
- 金句(
.highlight40px + 左边框 + 上下留白):字符数 × 3.0 .item条目组(label + 正文):字符数 × 1.8- 引用块:字符数 × 1.7
- 分割线(divider):固定 60 权重
- 代码块:字符数 × 2.2
- Running title(续页头部):固定 70 权重
步骤 4:贪心切分
- 阈值:每卡约 380 字符等价视觉重量
- 逐段累加,超过阈值时在当前段之前切分
- 切分规则:
- 绝不在句子中间切
- 优先在段落/条目/章节边界切
- 标题不落单(必须跟至少一个内容元素在同一卡)
- 超长单段在句号处强制切
- 一个章节(h2 + 3 items)通常刚好一卡
特殊情况:
- 只有一张卡:不显示页码
- 多张卡:显示
1 / N格式页码
步骤 5:格式化为 HTML
基础元素:
- 普通段落 →
<p>文本</p> - 章节标题(##/### 级别) →
<h2>标题</h2> - 引用 →
<blockquote><p>引用</p></blockquote> - 加粗 →
<strong>文本</strong> - 列表 →
<ul><li>...</li></ul>
金句(独立成段的核心洞察短句,视觉突出):
<p class="highlight">金句文本</p>判断标准:独立成段、< 25 字、承载关键洞察。用 .highlight 而非 <p><strong>。
条目组(有标题+正文的并列条目):
<div class="item">
<p class="label">条目标题</p>
<p>条目正文</p>
</div>副标题标签:
<p class="subtitle">标签文字</p>分割线(章节之间):
<div class="divider"></div>步骤 6:渲染模板
对每张卡片,替换模板变量:
| 变量 | 规则 |
|---|---|
{{BG_COLOR}} | 步骤 1.5 确定的背景底色 |
{{ACCENT_COLOR}} | 步骤 1.5 确定的强调色 |
{{HEADER_BLOCK}} | 续页卡:<div class="header"><span class="running-title">文章标题</span></div>;首卡或单卡:空字符串 |
{{TITLE_BLOCK}} | 首卡有标题时:<div class="title-area"><h1>标题</h1></div>;续页卡或无标题时:空字符串 |
{{BODY_HTML}} | 步骤 5 生成的 HTML |
{{SOURCE}} | 来源/作者信息(用户提供则填入,否则 Rainman @deusyu) |
{{PAGE_INFO}} | 多卡时 1 / 3,单卡时空字符串 |
结尾标记:仅在最后一张卡的 {{BODY_HTML}} 末尾追加 <p style="text-align:right;font-size:16px;color:#ACACB0;margin-top:40px;">∎</p>。非末页不加。
写入:/tmp/ljg_cast_poster_{name}_{N}.html
步骤 7:截图
node ~/.claude/skills/ljg-card/assets/capture.js /tmp/ljg_cast_poster_{name}_{N}.html ~/Downloads/{name}_{N}.png 1080 1440多张卡片可并行截图。
交付时报告卡片数量 + 每张摘要(前 30 字)。
模具:视觉笔记(-v)
核心信条
像人手画出来的,不是排版出来的。
Sketchnote 的灵魂是"一边想一边画"的痕迹感。不是整齐的排版加了手写字体——是概念之间用箭头连起来、关键词被圈出来、旁边有小简笔画的那种东西。
步骤 1:读取模板
Read ~/.claude/skills/ljg-card/assets/sketchnote_template.html
模板提供:
- 手写字体加载(Caveat + Architects Daughter + Patrick Hand)
- CSS 变量(
--bg,--ink,--accent,--marker,--block,--hand,--hand-alt) - 点阵笔记本底纹
- SVG 噪点纹理(纸张质感)
- SVG 箭头 marker 定义
.colophon署名栏{{CUSTOM_CSS}}和{{CONTENT_HTML}}插槽
步骤 2:理解内容,选择风格
2.1 提取关键概念
从内容中提取:
- 核心命题:一句话说清这段内容在讲什么
- 3-7 个关键概念:可以画成"节点"的东西
- 概念关系:哪些概念之间有因果/对比/递进/包含关系
- 一个视觉锚点:最适合用简笔画表达的那个概念
2.2 选择风格路线
根据内容类型匹配风格。不是模仿某个人,是借用他们的视觉语言。
| 风格 | 视觉特征 | 触发信号 | 色调 |
|---|---|---|---|
| 火柴人叙事 (Tim Urban 路线) | 大号手写标题 + 火柴人 SVG + 大色块分区 + 幽默标注 | 抽象概念需要拟人化、有故事线、有情绪转折 | --bg: #FFFEF9 --accent: #E85D3A --marker: #FFE066 |
| 概念地图 (Mike Rohde 路线) | 混合字号手写 + 图标化概念 + 箭头/连线网络 + 编号 | 多概念并行、有结构关系、知识整理型 | --bg: #FAFAF5 --accent: #2D6A4F --marker: #D4EDDA |
| 餐巾纸草图 (Dan Roam 路线) | 极简线条 + 大量留白 + 一个核心图解 + 最少文字 | 一个核心洞见、商业/策略类、可以用一张图说清 | --bg: #FFFFFF --accent: #3D5A80 --marker: #DBEAFE |
| 概念拼贴 (Christoph Niemann 路线) | 一个主视觉占半幅 + 文字围绕 + 意想不到的视觉隐喻 | 有强烈视觉隐喻可能、艺术/设计/创意类 | --bg: #F8F6F2 --accent: #8B4513 --marker: #FDE8D0 |
选择原则:
- 默认用「概念地图」——最通用的 sketchnote 风格
- 内容有明确故事线/角色 → 火柴人叙事
- 内容可以浓缩成一张图 → 餐巾纸草图
- 内容有强视觉隐喻 → 概念拼贴
步骤 3:设计画面
3.1 手绘元素工具箱
所有视觉元素用 CSS + SVG 实现,不用外部图片。
手写文字层级
| 层级 | 字体 | 字号 | 用途 |
|---|---|---|---|
| 大标题 | --hand (Caveat) 700 | 72-96px | 页面主标题,可以微微旋转 (transform: rotate(-1deg ~ 2deg)) |
| 概念标签 | --hand (Caveat) 600 | 44-56px | 关键概念词,被圈/框/高亮 |
| 正文注释 | --hand (Caveat) 400 | 32-40px | 解释性文字 |
| 小标注 | --hand-alt (Architects Daughter) | 24-28px | 旁注、补充、数据 |
中文回退:手写字体对中文无效时自动回退到 PingFang SC。中文标题可用 Caveat 数字/英文 + PingFang SC 中文混排。纯中文标题用 PingFang SC 但加上手绘装饰(下划线波浪、圈、箭头)来保持手绘感。
连接元素(CSS 实现)
/* 手绘风格下划线 */
.underline-hand {
text-decoration: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='8'%3E%3Cpath d='M0,5 Q50,0 100,5 T200,5' stroke='%23E85D3A' fill='none' stroke-width='2'/%3E%3C/svg%3E");
background-repeat: repeat-x;
background-position: bottom;
background-size: 200px 8px;
padding-bottom: 6px;
}
/* 手绘圆圈(强调某个词) */
.circled {
border: 2.5px solid var(--accent);
border-radius: 50% 45% 55% 48%;
padding: 4px 14px;
display: inline-block;
transform: rotate(-1deg);
}
/* 荧光笔标记 */
.marked {
background: linear-gradient(transparent 55%, var(--marker) 55%, var(--marker) 90%, transparent 90%);
padding: 0 4px;
}
/* 手绘箭头(用 SVG inline) */
/* <svg width="120" height="40"><path d="M5,20 Q60,5 115,20" stroke="#2B2B2B" fill="none" stroke-width="2" marker-end="url(#arrowhead)"/></svg> */
/* 色块便签 */
.sticky {
background: var(--marker);
padding: 18px 22px;
transform: rotate(-1.5deg);
box-shadow: 2px 3px 6px rgba(0,0,0,0.08);
font: 500 36px/1.5 var(--hand);
}
/* 虚线连接框 */
.dashed-box {
border: 2px dashed var(--ink-light);
border-radius: 8px;
padding: 20px 24px;
}
/* 编号圆点 */
.num-circle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px; height: 40px;
border-radius: 50%;
background: var(--accent);
color: white;
font: 700 22px var(--hand);
}简笔图标(SVG inline)
用简单的 SVG path 画概念图标。每个图标控制在 3-5 个 path 以内,线宽 2-3px,只用 stroke 不用 fill(线条画风格)。
常用图标参考:
- 灯泡(洞见):圆 + 底座 + 三条放射线
- 箭头(流程):弯曲 path + arrowhead marker
- 人(角色):圆头 + 线身体 + 两条腿
- 大脑(思考):半圆 + 波浪线
- 书本(知识):两个长方形微张开
- 齿轮(机制):圆 + 齿纹
- 放大镜(分析):圆 + 斜线把手
- 闪电(突变/能量):Z 字形
不需要画得精细。线条微微抖动更好——用 CSS filter 或微小的 path 不规则来实现。
3.2 布局原则
Sketchnote 不是网格排版。 它是概念在一张纸上"生长"出来的样子。
- 概念节点散布在画面上,位置由关系决定,不由网格决定
- 箭头/连线连接有关系的节点
- 大小代表重要性:核心概念大,衍生概念小
- 留白是呼吸,不是浪费——但不是均匀留白,是"有些地方密、有些地方空"
- 微旋转:标题、便签、图标都可以有 -3deg 到 3deg 的旋转,制造"手放上去的"感觉
3.3 画面构成(按风格)
火柴人叙事
标题(大号手写,顶部偏左,微旋转)
|
v
[场景1: 火柴人SVG + 对话泡泡] --箭头--> [场景2] --箭头--> [场景3]
|
v
[结论色块]
旁边散落小标注和关键词圈概念地图
[核心概念] <-- 大号、被圈出
/ | \
/ | \
[概念A] [概念B] [概念C] <-- 中号、色块背景
| | |
注释... 注释... 注释... <-- 小号手写
\ | /
\ | /
--> [总结] <-- <-- 底部便签色块餐巾纸草图
[一个大图解占据 60% 画面]
旁边 3-5 行手写注释
底部一句话总结概念拼贴
[主视觉隐喻 SVG,占据上半幅]
---- 分隔 ----
文字区:2-3 个关键点
每个点有小图标 + 手写说明步骤 4:写 CSS + HTML
所有 CSS 写入 {{CUSTOM_CSS}}。所有 HTML 写入 {{CONTENT_HTML}}。
CSS 从零写——class 名反映内容(.phase-warmup、.core-insight),不是通用名。
手绘感 CSS 技巧清单:
transform: rotate(Xdeg)微旋转,X 在 -3 到 3 之间随机border-radius: 50% 45% 55% 48%不规则圆角font-family: var(--hand)手写字体- 波浪下划线用 SVG background-image
- 箭头用 inline SVG + marker-end
- 色块用
var(--marker)半透明叠加 - 线条用
border: 2px solid var(--ink)而非 1px,模拟笔触
替换变量:
| 变量 | 内容 |
|---|---|
{{CUSTOM_CSS}} | 这张图的全部 CSS(包括覆盖 :root 变量) |
{{CONTENT_HTML}} | 这张图的全部 HTML |
{{SOURCE}} | 署名 |
{{ARXIV_LINE}} | arxiv 时填入,否则空 |
写入:/tmp/ljg_cast_sketchnote_{name}.html
步骤 5:自检
- [ ] 一眼看上去像手绘的吗?如果看着像"普通排版 + 手写字体",重做
- [ ] 有没有箭头/连线连接概念?Sketchnote 没有连线就不是 sketchnote
- [ ] 有没有至少 2 个手绘元素(圈、箭头、简笔画、便签、荧光笔标记)?
- [ ] 标题和概念标签有微旋转吗?
- [ ] 点阵底纹可见吗?(笔记本感)
- [ ] 色块 ≤ 3 种颜色(ink + accent + marker)?
- [ ] 正文手写字体 ≥ 32px?标注 ≥ 24px?
- [ ] 有没有一个元素让人第一眼被抓住?
- [ ] 是否避免了三等分、居中对称、等间距等"排版感"布局?
步骤 6:截图
node ~/.claude/skills/ljg-card/assets/capture.js /tmp/ljg_cast_sketchnote_{name}.html ~/Downloads/{name}.png 1080 800 fullpage模具:白板(-w)
核心信条
像站在白板前讲解一样,不是排版出来的。
Whiteboard 的灵魂是"一个人拿着马克笔在白板上边讲边写"的现场感。标题大而粗、关键词用彩色笔圈出来、逻辑用箭头和框图串联、旁边有快速标注。和 Sketchnote 的区别:白板更结构化、更"讲解"、更干净,没有笔记本的私人感。
步骤 1:读取模板
Read ~/.claude/skills/ljg-card/assets/whiteboard_template.html
模板提供:
- 马克笔字体加载(Permanent Marker + Kalam)
- CSS 变量(
--bg,--board,--ink,--red,--blue,--green,--orange,--marker-bg) - 微栅格底纹(白板磨砂面)
- 白板边框
- SVG 箭头 marker 定义(红/蓝/黑/绿四色)
.colophon署名栏{{CUSTOM_CSS}}和{{CONTENT_HTML}}插槽
步骤 2:理解内容,选择风格
2.1 提取结构
从内容中提取:
- 核心论点:一句话总结
- 3-8 个关键模块:可以画成"区块"的东西
- 模块关系:层级、因果、对比、并列
- 数据亮点:适合大号数字展示的关键数据
2.2 选择风格路线
| 风格 | 视觉特征 | 触发信号 | 主色 |
|---|---|---|---|
| 架构图 | 大标题居左 + 层级方框 + 箭头连接 + 编号 | 系统/流程/步骤/架构类内容 | --blue 主导 |
| 脑暴墙 | 核心词居中 + 放射状分支 + 便签贴色块 + 关键词散落 | 发散型/多观点/创意/头脑风暴 | --orange 主导 |
| Sprint 回顾 | 纵向时间线 + 里程碑节点 + 旁注 + 对比色标记 | 时间线/进程/回顾/对比类 | --green 主导 |
| 矩阵分析 | 2x2 或多格矩阵 + 象限标签 + 要素散布 | 分类/对比/评估/决策框架 | --red 主导 |
选择原则:
- 默认用「架构图」——最通用的白板风格
- 内容有多个并列观点/意见 → 脑暴墙
- 内容有时间/阶段维度 → Sprint 回顾
- 内容涉及分类/象限/对比 → 矩阵分析
步骤 3:设计画面
3.1 白板元素工具箱
所有视觉元素用 CSS + SVG 实现,不用外部图片。
文字层级
| 层级 | 字体 | 字号 | 用途 |
|---|---|---|---|
| 主标题 | --marker (Permanent Marker) | 72-84px | 白板顶部大标题 |
| 模块标题 | --hand (Kalam) 700 | 48-56px | 各区块标题 |
| 正文 | --hand (Kalam) 400 | 34-40px | 说明文字 |
| 标注 | --sans (DM Sans) | 24-28px | 小字注释、数据 |
| 大号数字 | --marker | 72-120px | 数据亮点,抓眼球 |
中文回退:Permanent Marker 和 Kalam 对中文无效,自动回退到 PingFang SC。中文标题保持加粗,通过彩色马克笔效果(色块背景、粗下划线)维持白板感。
马克笔效果(CSS 实现)
/* 马克笔粗下划线 */
.underline-marker {
border-bottom: 4px solid var(--red);
padding-bottom: 2px;
}
/* 马克笔圈(强调关键词) */
.circled {
border: 3px solid var(--red);
border-radius: 45% 55% 50% 48%;
padding: 4px 16px;
display: inline-block;
}
/* 荧光笔高亮 */
.highlighted {
background: var(--marker-bg);
padding: 2px 8px;
}
/* 方框区块(白板上的框图) */
.box {
border: 3px solid var(--ink);
border-radius: 4px;
padding: 20px 24px;
}
.box-blue { border-color: var(--blue); }
.box-red { border-color: var(--red); }
.box-green { border-color: var(--green); }
/* 虚线框 */
.box-dashed {
border: 3px dashed var(--ink-light);
border-radius: 4px;
padding: 20px 24px;
}
/* 便签贴 */
.sticky {
background: #FFF3BF;
padding: 16px 20px;
transform: rotate(-1deg);
box-shadow: 2px 3px 8px rgba(0,0,0,0.06);
}
.sticky-blue { background: #DBEAFE; }
.sticky-green { background: #D1FAE5; }
.sticky-pink { background: #FCE7F3; }
/* 编号圆点 */
.num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px; height: 44px;
border-radius: 50%;
background: var(--blue);
color: white;
font: 700 24px var(--marker);
}连接箭头(SVG inline)
用 SVG path 画箭头。粗线(stroke-width: 3),搭配四色 marker-end。
3.2 布局原则
白板是结构化的——不像 sketchnote 那样自由散落,但也不是严格网格。
- 区块化:内容分成 3-6 个明确区块,每块有框或色块底
- 层级清晰:标题→子标题→正文,用字号+颜色拉开
- 箭头连接:区块之间用箭头标注逻辑关系
- 留白适度:比 sketchnote 更规整,但比长图更松散
- 色彩分区:不同模块用不同马克笔颜色标记
3.3 画面构成(按风格)
架构图
[大标题 - 蓝色粗体] 居左
═══════════════════════
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐
│ │模块1 │→│模块2 │→│模块3 │
│ │ │ │ │ │ │
│ └──────┘ └──────┘ └──────┘
│ ↓ ↓ ↓
│ 说明文字 说明文字 说明文字
│
│ [底部总结 - 红色框]脑暴墙
[便签1] [便签2]
\ /
[便签3] → [核心词] ← [便签4]
/ \
[便签5] [便签6]
底部:关键 takeaway,红色下划线Sprint 回顾
[标题]
●──────●──────●──────●──────●
阶段1 阶段2 阶段3 阶段4 阶段5
│ │ │ │ │
注释 注释 注释 注释 注释矩阵分析
[标题]
│ 维度A高
────────┼────────
象限1 │ 象限2
│
────────┼────────
象限3 │ 象限4
│ 维度A低
维度B低 维度B高步骤 4:写 CSS + HTML
所有 CSS 写入 {{CUSTOM_CSS}}。所有 HTML 写入 {{CONTENT_HTML}}。
CSS 从零写——class 名反映内容语义,不是通用名。
替换变量:
| 变量 | 内容 |
|---|---|
{{CUSTOM_CSS}} | 这张图的全部 CSS(包括覆盖 :root 变量) |
{{CONTENT_HTML}} | 这张图的全部 HTML |
{{SOURCE}} | 署名 |
{{ARXIV_LINE}} | arxiv 时填入,否则空 |
写入:/tmp/ljg_cast_whiteboard_{name}.html
步骤 5:自检
- [ ] 一眼看上去像白板上写的吗?如果像普通排版,重做
- [ ] 有没有至少用到 2 种马克笔颜色?
- [ ] 有没有方框/箭头连接不同区块?
- [ ] 标题是否够大够粗(≥ 72px)?
- [ ] 和 sketchnote 放在一起能一眼区分吗?(无点阵、无手绘简笔画、更结构化)
- [ ] 正文手写字体 ≥ 34px?标注 ≥ 24px?
- [ ] 内容区块是否有明确的视觉边界?
- [ ] 数据/关键数字是否用大号或彩色突出?
步骤 6:截图
node ~/.claude/skills/ljg-card/assets/capture.js /tmp/ljg_cast_whiteboard_{name}.html ~/Downloads/{name}.png 1080 800 fullpage设计品味准则(全模具通用)
所有模具生成 HTML 前,必须经过本准则校验。这是视觉质量的底线。
1. 基线参数
| 维度 | 默认值 | 含义 |
|---|---|---|
| DESIGN_VARIANCE | 8 | 1=完美对称,10=艺术混沌 |
| VISUAL_DENSITY | 4 | 1=画廊留白,10=驾驶舱信息密度 |
根据模具自动调整:
-l长图:DESIGN_VARIANCE=5, VISUAL_DENSITY=3(阅读舒适优先)。变化通过色调感知实现——不同内容气质对应不同背景底色和强调色(见 mode-long.md 步骤 2.5)-i信息图:DESIGN_VARIANCE=7, VISUAL_DENSITY=8(数据密度优先)。变化通过动态 REF 编码和内容驱动的自定义布局实现-c海报:DESIGN_VARIANCE=9, VISUAL_DENSITY=2(视觉冲击优先)。与长图共享色调系统,结尾标记仅在末页出现
2. 排版工程
标题
- 大标题:
tracking-tighter(字间距紧凑),leading-none(行高极小) - 禁用 Inter 字体。长图/海报用衬线体(Noto Serif SC),信息图用等宽+无衬线混排
- 仪表盘/技术类场景严禁衬线体——只用高端无衬线(Geist、Satoshi、Cabinet Grotesk)
正文
- 默认:
text-base、leading-relaxed、最大行宽65ch -i信息图:正文 ≥36px、行高 ≥1.6、标注 ≥24px(手机端 1080px→390px 缩放 2.8 倍后需可读)- 段落文本颜色避免纯黑,用
#333或#4a4a4a等深灰
数字
- 当 VISUAL_DENSITY > 7(信息图模式),所有数字用等宽字体(
font-family: monospace)
3. 色彩校准
硬性规则
- 最多 1 个强调色,饱和度 < 80%
- 禁止「AI 紫蓝」:紫色按钮光晕、霓虹渐变一律禁止
- 同一张图内严格统一冷暖调——不在暖灰和冷灰之间摇摆
- 禁止纯黑
#000000:用 Off-Black(#1a1a1a)、Zinc-950 或炭灰
渐变约束
- 不要对大标题使用渐变填充文字
- 背景渐变仅限微妙过渡,避免色彩跳跃
4. 布局多样化
DESIGN_VARIANCE > 4 时
- 禁止居中 Hero:标题不要默认居中。用左对齐、分屏、非对称留白
- 禁止「三等分卡片」:3 列等宽并排是 AI 生成的头号标志。用 2 列锯齿、非对称网格、或横向滚动替代
DESIGN_VARIANCE ≥ 8 时
- 使用 CSS Grid 分数单位(如
grid-template-columns: 2fr 1fr 1fr) - 允许大面积留白(
padding-left: 20vw级别的空间感) - 允许 Masonry 式错落布局
卡片与容器
- 卡片仅在层级关系(elevation)有功能需求时使用
- 数据指标让它们「呼吸」——用
border-top、divide-y或纯留白分组,而非一个个方盒子 - 阴影必须染色(与背景色调一致),不要灰色默认阴影
5. AI 生成禁忌清单
生成任何视觉内容前,逐项排查以下 AI 典型痕迹:
视觉 & CSS
- 禁止外发光:不要
box-shadow默认光晕。用内边框或染色阴影 - 禁止过饱和强调色:强调色必须与中性色优雅融合
- 禁止自定义鼠标指针(静态图不涉及,但生成 HTML 时也不要加)
排版
- 禁止 Inter 字体:用 Geist、Outfit、Cabinet Grotesk 或 Satoshi
- 禁止 H1 尖叫:标题不要靠单纯放大来建立层级。用字重和颜色控制
内容 & 数据(「Jane Doe 效应」)
- 禁止通用人名:John Doe、Sarah Chan、Jack Su 禁止出现。用有创意的真实名字
- 禁止假数据:不要
99.99%、50%、1234567。用有机的「脏」数据(47.2%、+1 (312) 847-1928) - 禁止创业烂名:Acme、Nexus、SmartFlow 禁止。发明有品味的品牌名
- 禁止 AI 文案腔:「赋能」「无缝」「释放」「下一代」禁止。用具体动词
- 禁止 Unsplash 链接:如需占位图,用
https://picsum.photos/seed/{随机字符串}/800/600或 SVG
间距 & 对齐
- padding 和 margin 必须数学精确,不留尴尬间隙
- 相邻元素严格对齐,视觉线条贯通
6. 材质与表面
玻璃态(Glassmorphism)
如需毛玻璃效果,不要只用 backdrop-blur。必须叠加:
- 1px 内边框:
border: 1px solid rgba(255,255,255,0.1) - 微妙内阴影:
box-shadow: inset 0 1px 0 rgba(255,255,255,0.1)
模拟物理边缘折射。
圆角
- 主容器用大圆角(
border-radius: 2.5rem) - 扩散阴影(极淡、大范围):
box-shadow: 0 20px 40px -15px rgba(0,0,0,0.05)
7. 出厂自检
生成 HTML 后、截图前,逐项确认:
- [ ] 是否避免了居中 Hero(DESIGN_VARIANCE > 4 时)?
- [ ] 是否避免了三等分等宽卡片?
- [ ] 标题是否用了非 Inter 字体?
- [ ] 颜色是否统一冷暖调,无纯黑?
- [ ] 强调色是否 ≤ 1 个且饱和度 < 80%?
- [ ] 数据是否真实感(非 99.99% 式假数据)?
- [ ] 文案是否去除了 AI 腔(赋能/无缝/释放)?
- [ ] 间距是否数学精确,无尴尬留白?
- [ ] 阴影是否染色(非灰色默认)?