name: xhs-card-renderer description: 将 Markdown 正文渲染为小红书风格的高清图片卡片(1080×1440,2x DPI)。当用户需要"出图""渲染卡片""生成小红书图片""把文章变成图片"时触发。覆盖:风格规范、HTML 生成、Playwright 高清导出。
小红书图文卡片渲染 Skill
将写好的帖子正文转化为可直接上传小红书的高清 PNG 图片。
About
- 一个 Claude Code / Agent skill,把 Markdown 正文渲染成小红书风格的高清卡片。
- 引擎:Playwright + Chromium,2x DPI,输出 1080×1440 卡片。
- 设计哲学:像书页,不像海报。零装饰、强排版、反 AI 味。
{{AUTHOR}}是占位符——首次安装后全局替换成你的账号名(见 README「自定义」)。
触发条件
用户说类似:
- "帮我出图 / 渲染成图片"
- "把这篇帖子变成小红书卡片"
- "生成小红书图片"
- "渲染一下"
风格铁律:像书页,不像海报
风格参考:夸克扫描王系列、migeai、Decoder Only 这类「文档感」博主。 核心感受:读者应该觉得"在读一篇文章",而不是"在看一张设计作品"。
背景
- 奶白色
#f5f0eb,不是纯白 - 零装饰:无边框、无阴影、无圆角、无渐变、无网格、无纹理
- 不加任何 emoji 装饰
字体
font-family: 'PingFang SC', -apple-system, 'Noto Sans SC', sans-serif;
-webkit-font-smoothing: antialiased;
| 元素 | 字号 | 字重 | 颜色 |
|---|---|---|---|
| 正文 | 38px | 400 | #1a1a1a |
| 加粗强调 | 38px | 700 | #1a1a1a |
| 小字/补充 | 34px | 400 | #888 |
| 水印 | 22px | 400 | rgba(0,0,0,0.10) |
行高 1.85,段间距 margin-bottom: 40px,字间距 0.8px。
强调方式(只有两种,不允许发明新的)
1. 加粗(主力,无限制使用)
直接 font-weight: 700,不改颜色、不加背景。
2. 荧光笔划线(点睛,每页最多 1 处)
.hl {
background: linear-gradient(to top, rgba(255,183,77,0.35) 0%, rgba(255,183,77,0.35) 35%, transparent 35%);
font-weight: 700;
}
效果:文字下方 35% 有淡橙色色带,像荧光笔划过。只用于全页最核心的金句。
绝对禁止
- ❌ 彩色边框 / 左边框
- ❌ 背景色块(紫色、蓝色、任何颜色)
- ❌ 圆角卡片 / 阴影
- ❌ 分割线
<hr> - ❌ 不同颜色的文字
- ❌ Emoji 点缀
- ❌ 任何让人一眼看出"这是 AI 做的"的设计元素
内容密度规范
原则:宁满勿空
| 页面类型 | 建议字数 | 段落数 |
|---|---|---|
| 纯文字页 | 180-280 字 | 5-8 段 |
| 图文混合页 | 80-150 字 + 截图 | 截图占 30-50% |
| CTA 结尾页 | 120-200 字 | 可略少 |
如果一页不到 120 字,必须和相邻页合并。绝不允许一页只有 2-3 行。
截图嵌入
- 截图作为
<img>直接插入正文流 - 无边框、无阴影、无圆角:
width: 100%; margin: 24px 0 32px; - 截图前后要有文字解释
执行步骤
Step 1:分析正文,规划分页
读取用户提供的正文(Markdown 或纯文本),按以下规则分页:
- 计算每页文字量,确保 180-280 字/页
- 将稀疏页面合并
- 第 1 页带作者头像 + 名字 + 日期
- 最后一页放 CTA
输出分页方案给用户确认:
第 1 页(引入):xxx - xxx(约 220 字)
第 2 页(结构):xxx - xxx(约 250 字)
...
共 N 页,是否 OK?
Step 2:生成 HTML 文件
创建 HTML 文件,文件名格式:YYYYMMDD-标题关键词.html(建议放在一个 output/ 或工作目录下)。
生成前必做(防止印错日期):
- 先跑
date +%Y-%m-%d取当天真实日期,把模板里第 1 页的__DATE__占位符替换掉。绝不允许直接沿用模板里的旧日期。 - 文件名里的
YYYYMMDD同样用当天日期。 - 作者名 / 水印用你的账号名替换
{{AUTHOR}}(或安装时已全局替换好)。
HTML 模板结构:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'PingFang SC', -apple-system, 'Noto Sans SC', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
background: #ddd;
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
padding: 40px;
}
.slide {
width: 1080px;
height: 1440px;
background: #f5f0eb;
padding: 80px 88px 64px;
display: flex;
flex-direction: column;
justify-content: flex-start;
position: relative;
overflow: hidden;
}
.slide p {
font-size: 38px;
font-weight: 400;
color: #1a1a1a;
line-height: 1.85;
margin-bottom: 40px;
letter-spacing: 0.8px;
}
.slide p.bold, .slide strong { font-weight: 700; }
.slide p.muted { font-size: 34px; color: #888; }
.slide .hl {
background: linear-gradient(to top, rgba(255,183,77,0.35) 0%, rgba(255,183,77,0.35) 35%, transparent 35%);
font-weight: 700;
}
.slide img.screenshot { width: 100%; margin: 24px 0 32px; }
.slide .header { display: flex; align-items: center; gap: 18px; margin-bottom: 56px; }
.slide .avatar {
width: 68px; height: 68px; border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; align-items: center; justify-content: center;
font-size: 30px; color: #fff; font-weight: 700; flex-shrink: 0;
}
.slide .author-name { font-size: 32px; font-weight: 700; color: #1a1a1a; }
.slide .author-date { font-size: 24px; color: #aaa; margin-top: 2px; }
.slide .watermark {
position: absolute; bottom: 32px; right: 44px;
font-size: 22px; color: rgba(0,0,0,0.10);
}
</style>
</head>
<body>
<!-- 第 1 页 -->
<!-- __DATE__ 必须替换成当天日期(见 Step 2 说明),不要直接用模板里的占位值 -->
<div class="slide">
<div class="header">
<div class="avatar">C</div>
<div>
<div class="author-name">{{AUTHOR}}</div>
<div class="author-date">__DATE__</div>
</div>
</div>
<p>正文内容...</p>
<p class="bold">加粗内容...</p>
<span class="watermark">@{{AUTHOR}}</span>
</div>
<!-- 第 N 页 -->
<div class="slide">
<p>正文...</p>
<p><span class="hl">荧光笔高亮金句</span></p>
<span class="watermark">@{{AUTHOR}}</span>
</div>
</body>
</html>
Step 3:渲染高清 PNG
用本仓库的渲染脚本导出 2x DPI 图片:
test -f scripts/render-cards.js || echo "⚠️ 找不到渲染脚本,确认路径/工作目录"
node scripts/render-cards.js path/to/YYYYMMDD-xxx.html path/to/output-dir/
输出:slide-01.png ~ slide-0N.png,每张 2160×2880 实际像素。
把本 skill 装到 Claude Code(
~/.claude/skills/xhs-card-renderer/)时,记得让scripts/render-cards.js跟着一起,或在上面命令里写脚本的绝对路径。
Step 4:展示给用户确认
只展示第 1 张给用户看,等确认后再渲染剩余。
绝对不要一口气渲染所有页面。先出 1 张,用户说 OK 再继续。
质检清单(渲染前必查)
- 背景
#f5f0eb,无任何装饰 - 没有彩色色块、边框、阴影
- 每页内容 ≥ 120 字,不存在稀疏页
- 加粗是主力强调,划线高亮每页 ≤ 1 处
- 截图无边框无圆角
- 字体抗锯齿已开启
- 水印极淡
rgba(0,0,0,0.10) - 第 1 页日期 = 当天真实日期(模板里没残留
__DATE__或旧日期) - 作者名/水印已替换
{{AUTHOR}} - 整体看起来像"在读文章",不像"在看设计"
依赖
- Node.js
- Playwright(
npm install+npm run setup装 Chromium) - 渲染脚本:
scripts/render-cards.js