# Xhs Card Renderer

> 将 Markdown 正文渲染为小红书风格的高清图片卡片（1080×1440，2x DPI）。当用户需要"出图""渲染卡片""生成小红书图片""把文章变成图片"时触发。覆盖：风格规范、HTML 生成、Playwright 高清导出。

- Skill: `cyrus-tt/xhs-card-renderer` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add cyrus-tt/xhs-card-renderer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cyrus-tt/xhs-card-renderer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: cyrus-tt (https://skillmd.com/u/cyrus-tt)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cyrus-tt/xhs-card-renderer

---


# 小红书图文卡片渲染 Skill

将写好的帖子正文转化为可直接上传小红书的高清 PNG 图片。

> **About**
> - 一个 Claude Code / Agent skill，把 Markdown 正文渲染成小红书风格的高清卡片。
> - 引擎：Playwright + Chromium，2x DPI，输出 1080×1440 卡片。
> - 设计哲学：像书页，不像海报。零装饰、强排版、反 AI 味。
> - `{{AUTHOR}}` 是占位符——首次安装后全局替换成你的账号名（见 README「自定义」）。

---

## 触发条件

用户说类似：
- "帮我出图 / 渲染成图片"
- "把这篇帖子变成小红书卡片"
- "生成小红书图片"
- "渲染一下"

---

## 风格铁律：像书页，不像海报

风格参考：夸克扫描王系列、migeai、Decoder Only 这类「文档感」博主。
核心感受：读者应该觉得"在读一篇文章"，而不是"在看一张设计作品"。

### 背景

- 奶白色 `#f5f0eb`，不是纯白
- **零装饰**：无边框、无阴影、无圆角、无渐变、无网格、无纹理
- 不加任何 emoji 装饰

### 字体

```css
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 处）

```css
.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 或纯文本），按以下规则分页：

1. 计算每页文字量，确保 180-280 字/页
2. 将稀疏页面合并
3. 第 1 页带作者头像 + 名字 + 日期
4. 最后一页放 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 模板结构：

```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 图片：

```bash
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`

