
Cover Design
- 181 installs
- 130 repo stars
- Updated June 19, 2026
- sugarforever/01coder-agent-skills
Helps with ai & agent building tasks during AI-assisted development.
About
cover-design is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- cover-design
- AI & Agent Building
- AI-coding skill
Cover Design by the numbers
- 181 all-time installs (skills.sh)
- +12 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #3,045 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sugarforever/01coder-agent-skills --skill cover-designAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 181 |
|---|---|
| repo stars | ★ 130 |
| Last updated | June 19, 2026 |
| Repository | sugarforever/01coder-agent-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Cover Design · 代码驱动的封面设计
用 HTML/CSS 写封面,然后用 Chrome DevTools MCP 截图成 PNG。字体精确可控,适合视频缩略图、文章题图、社交分享卡这类对文字位置敏感的场景。
跟 cover-image 的分工
cover-image | cover-design (本 skill) | |
|---|---|---|
| 渲染方式 | AI 生图 (Replicate gpt-image-2) | HTML/CSS + 浏览器截图 |
| 视觉风格 | 手绘 / 插画 / 编辑设计 | 排版驱动 / typography hero |
| 文字精度 | AI 渲染的文字常常模糊 / 错字 | 100% 精确 |
| 适合 | 文章题图 / 抽象概念 / 美术导向 | 视频缩略图 / 大标题 / 品牌一致性 |
| 风格扩展 | 通过 prompt 调整 | 通过新增 HTML 模板扩展 |
需要 thumbnail 在 160px 缩略图也读得出大字 - 用本 skill。 需要插画美感和氛围 - 用 cover-image。
适用场景
- 横屏视频缩略图 (YouTube / Bilibili)
- 竖屏短视频封面 (抖音 / 视频号)
- 文章 hero 题图 / 社交分享卡 (X Card / Open Graph)
- 系列内容 (品牌一致性)
输出比例(4 套核心)
封面要覆盖横屏长视频和竖屏短视频,去重后是 4 种核心长宽比,分两个模板族生成。完整规格、平台落点像素、安全区见 references/platform-specs.md。
| 比例 | 朝向 | 设计画布 | 模板族 | 主要平台 |
|---|---|---|---|---|
| 16:9 | 横 | 1920×1080 | hero-typography | YouTube、横屏短视频 |
| 16:10 | 横 | 1920×1200 | hero-typography | Bilibili(封面位是 16:10,不是 16:9) |
| 9:16 | 竖 | 1080×1920 | hero-typography-vertical | 抖音、视频号 |
| 3:4 | 竖 | 1080×1440 | hero-typography-vertical | 视频号官方封面、抖音九宫格 |
一期视频通常做 1 个横屏设计 + 1 个竖屏设计,各导出 2 个比例,共 4 张图。横屏的 16:9↔16:10、竖屏的 9:16↔3:4 只差 --cv-h,同一 HTML 改一个变量就能切。竖屏务必遵守安全区(抖音/视频号首尾会叠 UI 或被裁),核心文字居中。
核心原则
0. 想法先行(方法论主线 · 最高优先)
这个 skill 只采用一套构思方法论:Paul Rand 的「想法先行」。封面不是先选模板填标题,而是先有一个想法(idea) - 这期到底要让人 get 到什么 - 再让画面为它服务。四步:研究(不预设)→ 抽取核心想法 → 删减(simplicity 是好想法的副产品)→ 缩到 160px 验证那个想法还在。
核心想法不一定是产品名,形态开放:一个名字 / 一个 logo / 一个隐喻装置 / 一种氛围。下面的所有原则(单焦点、传达、精简)都是这套方法论的执行层 - 先有想法,再谈表达。完整方法论见 references/design-conception.md,这是全 skill 的构思总纲,不要混入别的设计流派。
1. Thumbnail ≠ Slide
封面缩略图在订阅列表 / 推荐位 / 搜索结果里最小可能只有 160px 宽。bento 网格、4 个 metric tiles、信息密集的布局都不工作。要的是:
- 单焦点 - 一个主元素占据 40-60% 画面
- 大字优先 - 主标题字号 ≥ 200px (1920×1080 画布)
- 3 秒原则 - 缩到 160px 仍能读出主标题
- 少即是多 - 一个视觉钩子 + 标题 + 副标 + 作者署名,完。
元素预算(默认 ≤ 5,模板一律按此交付精简版,多出来的先删再说):① 一个 kicker(品类 / 工具,可选)② 一个核心主元素(标题 或 logo / 隐喻装置 - 单焦点)③ 一句副标(可选)④ 一处署名(只一次)⑤ 至多一个领域装置(before/after、命令条、N→1、大数字 - 仅当承载信息时)。默认不放:日期、eyebrow 小字、Ep· 胶囊、扇出节点 / 纯装饰色块 / 几何形、上下分隔横线、第二处署名、重复副标。要加回任何一项,先自问「它传达了信息吗」 - 不传达就删,这是原则 0「删减」的执行。
关键区分(精简时别误伤):风格本身的视觉语言 - swiss 的网格细线、bauhaus 的几何色块、terminal 的扫描线 + 光标、aurora / glass 的光晕渐变、blueprint 的网格线稿 - 是这个风格的身份,不是 chrome,不在精简之列。要删的是被过度填充的内容槽(eyebrow / 日期 / episode 胶囊 / 第二署名)和不承载信息的装饰(扇出节点、纯平衡用的抽象色块)。
详见 references/thumbnail-vs-slide.md 与 references/cover-composition.md(默认元素集)。
2. 模板驱动 + 参数化
每种风格落地为一个 HTML 模板,所有可变内容(主标题、副标、accent 色、作者署名等)通过 CSS 变量或文本占位符暴露。新增风格 = 新增一个模板,不动 skill 主流程。
可用模板见 templates/ 目录,每个风格一对(横屏 + 竖屏)。当前有 15 个风格(见 Step 2 列表),风格目录与扩展规格见 references/design-styles.md。
3. 固定像素画布 + 浏览器陷阱
.cover 元素必须用固定像素(通过 --cv-w / --cv-h 变量 - 横屏 1920 宽、竖屏 1080 宽,高度按比例取值),不要用 `vw` / `vh` / `%`。原因见 references/render-pipeline.md 里的浏览器视口陷阱章节 - 简单说,resize_page 设的高度不等于实际视口高度,Chrome 工具栏 / 标签栏会吃掉 200-400px。固定像素 + fullPage 截图才稳。
4. 传达 > 好看(让核心想法落到画面)
封面读得清还不够,要在三秒内说清核心想法是什么(及它的形态)/ 什么品类 / 什么领域:核心想法做最显眼的主元素(它可能是名字、logo、隐喻装置或氛围 - 见原则 0),品类给信号(agent skill→斜杠命令条、模型→logo),领域给锚(设计→before/after、安全→告警)。品牌署名只出现一次,精简次要 chrome(simplicity 是好想法的副产品)。这是封面质量能超过纯模板套用的关键一环 - 详见 references/cover-composition.md。
工作流
Step 1 · 收集信息
问用户(已给的跳过):
1. 目标平台 / 比例 - 要哪些?默认全 4 套(16:9 YouTube / 16:10 Bilibili / 9:16 抖音·视频号 / 3:4 视频号·抖音九宫格)。这决定做横屏、竖屏、还是都做 (见上方「输出比例」表) 2. 主标题 - 大字部分,通常 1-3 个英文词或 4-8 个中文字 (必填) 3. 副标题 - 一句话点题,中文 / 英文都行 (必填) 4. 品牌信息 - 频道名 / 作者名 / 日期 (可读 auto memory 的 video-promo.md) 5. 强调色 - 默认 lime #7bff9f,可换 (可选) 6. 背景 - 默认纯黑 #000,可换 (可选) 7. 品牌 logo - 要不要带某个模型/产品/厂商的官方 logo (Claude / OpenAI / Gemini / DeepSeek / Ollama……)?来源是 @lobehub/icons,取图标和放置见 references/brand-logos.md (可选) 8. 额外元素 - 比如版本号、tagline、装饰图形 (可选) 9. 输出路径 - 默认输出到视频目录,文件名带比例后缀 (cover-16x9.html/.png 等) 10. 是不是讲某个具体产品 / 工具? - 是的话拿到官网 URL,进入品牌匹配模式(见下),用产品自己的色 + 字 + logo,比频道默认 lime 更贴合产品
如果用户给了文章 / 脚本路径,先 Read 抽取主标题和副标题,再确认。
品牌匹配模式(选题是某个具体产品 / 工具 / 开源项目时)
封面采用产品官网的品牌色和字体,封面气质和被介绍的产品一致。运行:
scripts/extract-brand-theme.sh https://产品官网它输出可直接写入模板 :root 的 Google Fonts + 颜色 token(含 oklch 原值,Chrome 原生支持,原样写入即可)。用提取值覆盖模板默认的 --accent/--bg/--ink/--font-display/--font-mono,风格按官网调性从 design-styles.md 选最接近的。方法、字体中文搭配、取不到时的处理见 `references/brand-theme.md`。这和 brand-logos.md(取 logo)互补,配齐「色 + 字 + logo」一整套。
Step 1.5 · 构思:抽取核心想法(动手前必做 · 见原则 0)
收集完信息,先别选模板。按 references/design-conception.md 走一遍「想法先行」,抽出这期的核心想法写下来,再定层级(核心想法做最大主元素 > 角度 / 卖点做 accent 副线 > 能力 / 细节做灰字),带着它进 Step 2。
两条提醒:核心想法不一定是产品名(也可能是 logo / 隐喻装置 / 氛围);别把「角度 / 卖点」误当核心想法。这一步不可跳过 - 是封面超过「模板套标题」的关键。
Step 2 · 选模板(按朝向选模板族)
根据 Step 1 要的比例选模板族:
先按朝向选模板族,再按风格选具体模板(每个风格都有横屏 + 竖屏两版):
- 要横屏 (16:9 / 16:10) →
{style}.html - 要竖屏 (9:16 / 3:4) →
{style}-vertical.html - 两个都要 → 两版各做一份,共享同一套标题 / 副标 / 品牌信息
当前可用风格(每个都有横屏 {style}.html + 竖屏 {style}-vertical.html):
- `hero-typography` - 黑底霓虹大字 + fan-out 节点。技术解读 / 新功能发布,强视觉钩子。
- `swiss` - 瑞士国际主义:暖纸底 + 网格 + 极轻大字 + 单一强调色 + 1px 细线。冷静专业,技术拆解 / 评测 / 数据。
- `neo-brutalism` - 新粗野:高饱和撞色 + 粗黑描边 + 硬阴影 + 圆角块。玩味醒目,产品发布 / 工具类。
- `bauhaus` - 包豪斯:米白底 + 红黄蓝几何色块 + 粗网格。经典感,设计 / 理论 / 艺术类。
- `editorial` - 杂志风:暖纸底 + 衬线大标题 + 氛围背景 + 细线引文。有质感,观点 / 深度长文 / 人物。
- `brutalism` - 粗野:白底 + 系统/等宽字体 + 硬黑边框 + 刻意朴素。黑客 / 独立开发 / 逆向工程气质。
- `aurora` - 极光渐变:深底 + 多彩光晕 + 通透细字。AI / SaaS / 现代科技,当下感最强。
- `glass-dark` - 深色玻璃拟态:深底 + 背后柔光 + 磨砂玻璃面板。AI / 产品 / 现代科技,通透高级。
- `terminal` - 终端黑:纯黑 + 等宽荧光 + 扫描线 + 光标。CLI / 开发 / 黑客 / 逆向。
- `noir-editorial` - 暗调杂志:近黑 + 衬线大标题 + 暖金强调 + 颗粒。观点 / 深度 / 人物。
- `spotlight` - 聚光戏剧光:全黑 + 单束聚光 + 强暗角 + 高对比。发布 / 悬念 / 重磅。
- `blueprint` - 深蓝图:深藏青 + 白色网格线稿 + 等宽标注。架构 / 原理 / 技术拆解。
- `holographic` - 暗调全息:深底 + 油膜虹彩大字 + 全息箔 + 噪点。前沿 / 概念 / 潮流科技。
- `quiet-dark` - 极简黑:近黑底 + 左锚定大留白 + 纯白重字大标题 + 单一强调色只做标点(kicker + 短横杠)+ 极淡网格 + 1px 内框,零发光。冷静克制高级,发布解读 / 观点 / 系列封面。
- `editorial-index` - 编辑目录:暖纸底 + 大 wordmark 报头 + 编号目录(ruled contents)+ 单一强调色 + hairline 分隔。组合式版式而非单大标题,把整期 lineup 一眼摆出。AI 早读 / 日报周报 / 榜单 Top N / 合集目录(multi-item,缩略图会糊,宜大图 / 文章 hero)。
每个风格的视觉锚点、identity test、适用场景见 references/design-styles.md。
选风格的方法(智能选,不套默认皮)
风格 / 配色和「核心想法」一样要主动推导,不是拿模板的默认皮直接用。上面那张「风格 → 适用」清单是起点不是答案,照着题材类别机械对号最容易落进套路默认。方法:
1. 从核心想法 + 题材的「调性 / 情绪」出发,而不是题材类别机械映射。 先问:这期是什么调性 - 冷静严谨?发布的兴奋?悬念压迫?粗粝的黑客气?温和人文?调性决定风格,题材类别只是线索之一。 2. 列 2-3 个候选,逐个问「它的视觉语言服务这个想法和调性吗」,选最贴的(design-styles.md 有每个风格的 identity test)。拿不准就走「多方向预览」(Step 5),一次出 3-4 版让用户选。 3. 配色要主动定,别继承模板默认皮。 每个模板 :root 自带一套默认配色(terminal 黑绿、aurora 紫渐变、bauhaus 红黄蓝……)—— 那是起点不是终点。讲具体产品优先走品牌匹配(Step 1 的 extract-brand-theme.sh),否则按调性主动调 --bg / --accent。 4. 品类信号独立于皮肤。 「agent skill → 命令条、模型 → logo」给的是元素,不绑定某个风格 - 命令条可以放进白底编辑风,logo 可以放进任何风格。别因为「要给命令条」就反射性套 terminal 黑绿皮。
套路默认皮:合适就用,别反射性套。 CLI / skill 题材用 terminal 黑绿、AI 题材用 aurora 紫渐变 - 这些常规组合本身没问题,前提是你真判断过它贴这期的想法和调性。要避免的是「没思考、顺手抓题材最套路那张皮」,不是「避免常规选择」。
判据:你能说出「为什么这个风格服务这个核心想法」,就用;只能说出「因为这题材一般都长这样」,就再想想。
Step 3 · 定制内容
读取选定模板的 HTML,把占位符替换成用户的实际内容:
| 占位符位置 | 替换内容 |
|---|---|
<title> | 文档标题 + 频道名 |
.tag / .brand-mark | 顶部品牌行 |
.hero-eyebrow | 主标题上方小字 (类似 kicker) |
.hero-title | 主标题大字 (英文小写效果最好) |
.hero-sub | 中文副标题 |
.foot .author | 作者署名 |
.foot .episode | 底部右侧 chip |
--accent (CSS var) | 强调色 |
--bg (CSS var) | 背景色 |
| 品牌 logo (可选) | 顶部品牌行 / 标题前 lockup / 角标,来源 @lobehub/icons,做法见下「品牌 Logo」节 |
两个模板族共享上面这套占位符(竖屏族没有侧边 .nodes 和 .episode,其余一致)。竖屏族:所有文字已在 .safe 列里垂直居中,落在安全带内,不要把文字往画布上下边缘挪。
用 Write 工具把定制后的 HTML 写到输出路径(与最终 PNG 同目录)。横屏一版、竖屏一版分别写。
品牌 Logo(@lobehub/icons)
讲模型/产品/厂商时(Claude、OpenAI、Gemini、DeepSeek、Ollama……),从 [@lobehub/icons](https://lobehub.com/icons) 取官方 logo 融进模板。取图标、选变体、放置(品牌行/lockup/角标)、对比类双 logo、深底避坑、identity test 全见 `references/brand-logos.md`。
一条判断要点留在这:`@lobehub/icons` 只覆盖 AI / LLM 厂商。 讲非 AI 产品(开发工具、运行时、框架、SaaS,如 Bun、Vite、Zed)时它多半没有对应图标,fetch-brand-icon.sh 会报 FAIL。这时别硬拼 logo、别用网络图片(Rule 8) - 改走无 logo 的排版 / 品牌色识别:产品名做大字主元素 + 品牌色 + 命令条 / 领域装置(Bun 封面就是这么做的,见 references/cover-composition.md)。
Step 4 · 渲染成 PNG(每个比例一张)
默认用脚本 `scripts/render-cover.sh` - 它把多比例、2x retina、降采样预览、以及 headless Chrome 的所有坑(输出别用 /tmp、截图落盘时序、--headless=new 不自退要主动收进程、独立 profile)都封好了。一条命令搞定一期的横屏或竖屏全比例:
scripts/render-cover.sh cover.html ~/covers/<slug> # 横屏 HTML → 自动出 16x9 + 16x10
scripts/render-cover.sh cover-v.html ~/covers/<slug> 9x16 3x4 # 竖屏显式指定比例产物:cover-<比例>.png(2x retina,如 16:9 → 3840×2160)+ cover-<比例>.preview.png(≤1400px,给 Read / 验收看)。运行 scripts/render-cover.sh 无参看完整用法;坑的来由见 references/render-pipeline.md。
交互式微调时可改用 chrome-devtools MCP 逐张截(适合一边看一边调 CSS):new_page 打开 file:// → resize_page 到 画布宽 ×(画布高 + 20) → navigate_page reload → take_screenshot fullPage: true。但批量出图、最终交付一律走脚本(见 Critical Rule 10)。
Step 5 · 验收 + 迭代
open 在 macOS 默认查看器里打开所有 PNG 给用户看。
先过想法验证(见 `references/design-conception.md`):
- [ ] 核心想法 test at scale - 缩到 160px,问的不是「标题读得出吗」,而是那个核心想法还在吗(名字认得出 / 氛围透得出 / 隐喻读得懂)?读不出 → 回 Step 1.5 换更强的表达,或做得更大、删更多。
- [ ] 主元素是不是核心想法本身,而不是被「角度 / 卖点」抢了位?
再过内容传达自检(见 `references/cover-composition.md`):
- [ ] 三信号都到位?逐条指认画面里哪个元素负责:是什么(核心想法当主角,是不是最显眼)/ 什么品类(agent skill→命令条、模型→logo)/ 什么领域(设计→before/after、安全→告警…)
- [ ] 品牌署名只出现一次?(注意区分:产品名=选题主角要显眼,频道署名=低调一次。两者不算重复)
- [ ] 有没有可删的次要 chrome(日期 /
Ep·胶囊 / 分隔横线 / 装饰标签)?空白处是有信息量的视觉还是纯空? - [ ] 空白处放了领域装置没有?(转变类→before/after,整合类→N 个 ✕ → 一个 ✓,对比类→vs,数据类→大数字;partial 见
templates/partials/before-after.html)
再问视觉反馈:
- 文字位置 / 字号合不合适?
- 缩略图测试 - 缩到 ~320px / 160px 宽,主标题还读得出吗?(3 秒原则)
- 竖屏安全区 + 居中 - 9:16 / 3:4 核心文字是否在中央安全带、没贴边?短主视觉(标题/产品名)是否水平居中?(规则与例外见
references/platform-specs.md竖屏安全区) - accent / 背景色调要不要换?是否换模板?
多方向预览:定稿前不确定方向时,一次给出 3-4 个不同方向供用户选择(不同风格 / 配色),比单版反复改收敛更快。存档命名 cover-styles/{字母}-{风格}.html/.png。迭代时优先改 CSS 变量和占位符文本,不要重新写整个 HTML(节省 token,改动可见)。竖屏调试若用了 body.show-guide 看安全带,最终渲染前去掉这个 class。
Step 6 · 完成
简短清单:
封面已生成(按你选的平台 / 比例):
├── cover-16x9.png - 16:9 YouTube / 横屏短视频
├── cover-16x10.png - 16:10 Bilibili
├── cover-9x16.png - 9:16 抖音 / 视频号
└── cover-3x4.png - 3:4 视频号官方封面 / 抖音九宫格
(对应 .html 源文件同目录)
已用系统默认查看器打开。如需调整告诉我哪里改。只选了部分平台就只出对应比例。
写新模板的约定
未来加新模板时遵守以下约定(以便 skill 主流程不动也能识别):
1. 文件名 kebab-case,横屏族用基名(如 split-hero.html),竖屏族加 -vertical 后缀 2. .cover 元素必须:
- 用
--cv-w/--cv-h变量定宽高(固定像素,不要vw/vh/%);横屏族 1920 宽、竖屏族 1080 宽 position: relative; overflow: hidden;- 直接作为
<body>第一个 child(或 flex 容器居中其内容)
3. CSS 变量在 :root 暴露,至少包含:--cv-w、--cv-h、--bg、--ink、--accent、--font-display、--font-mono 4. 字体用 Google Fonts 加载,优先 Manrope (display) + Noto Sans SC (中文) + JetBrains Mono (mono) 5. 文件顶部 HTML 注释里写明:支持的比例和怎么切(改 --cv-h)、适配平台、以及"何时用这个模板"(让 Step 2 能正确推荐) 6. 竖屏模板把文字收在垂直居中的安全带里,别贴上下边缘(见 references/platform-specs.md 安全区)
新模板写好后不需要改 SKILL.md 主流程,只要 Step 2 时把它列入候选即可。
Critical Rules
1. 固定像素画布 - .cover 用 --cv-w/--cv-h 固定像素(横屏 1920 宽 / 竖屏 1080 宽),不要 viewport 单位 2. fullPage 截图 - 截图时 fullPage: true,避免视口陷阱 3. 3 秒原则 - 主标题字号 ≥ 200px,缩到 160px 也能读 4. 按目标平台出全比例 - 默认 4 套(16:9/16:10/9:16/3:4);竖屏核心文字放安全带,首尾会叠 UI/被裁(见 references/platform-specs.md) 5. 不替 cover-image 做事 - 用户要插画 / AI 生图,转 cover-image skill,本 skill 只做代码驱动的排版型封面 6. 不自动发布 - 只产 PNG,不动用户的发布渠道 7. 品牌信息从 auto memory - 频道名 / 作者名优先读 video-promo.md,首次没有再问用户 8. 品牌 logo 用官方来源 - 需要模型/产品/厂商 logo 时从 @lobehub/icons 取(见 references/brand-logos.md + scripts/fetch-brand-icon.sh),不要自行制作或随意使用网络图片;深色底别用 mono <img>(会变黑),用 color 变体或内联着色;对比类选题(X vs Y)双 logo 放大成标题上方 lockup,别缩成 kicker 小图标 9. 中文标题行高 - 中文大标题 line-height ≈1.05–1.10,别用拉丁 display 的 0.9(会撞行);见 references/design-styles.md 10. 渲染输出别用 `/tmp` - headless 渲染输出到持久目录(/tmp 会被清);优先用 scripts/render-cover.sh,坑见 references/render-pipeline.md 11. 想法先行(方法论主线,最重要) - 选模板前先按 references/design-conception.md 抽出核心想法(可能是名字/logo/隐喻/氛围,别把「角度/卖点」误当想法);全 skill 只用这一套方法论,别混入别的流派。落到画面的传达自检(三信号/署名只一次/精简 chrome)见 references/cover-composition.md 12. 讲具体产品时做品牌匹配 - 用产品官网真实色 + 字(scripts/extract-brand-theme.sh + references/brand-theme.md),不要一律采用默认 lime;封面气质和被介绍的产品一致更专业 13. 风格/配色要主动选,别套题材默认皮 - 按核心想法 + 题材调性推导风格(列 2-3 候选),模板自带配色是起点不是终点,品类信号(命令条/logo)不绑定某皮肤;常规选择合适就用,但要能说出「为什么这风格服务这个核心想法」(见 Step 2「选风格的方法」) 14. 配色服务可读性(color-for-legibility) - 字清不清取决于亮度对比,不只是配色好不好看。① 主标题的「体量」留给最高对比的中性色(白压暗 ≈21:1 / 墨压浅),目标对比 ≥7:1;② 强调色当「标点」用 - kicker / 短横杠 / 单个高亮词 / 图标 / 数字,小而实,绝不给整段大标题上饱和色(中等亮度 + 任何 glow 会软化字缘,160px 必糊);③ 主标题别加 `text-shadow` / glow;④ 不在同明度半透明色块上压同色文字(彩字发虚的常见来源)。霓虹风(hero-typography)的发光是有意识的风格选择,不是默认;范本见 quiet-dark。详见 references/thumbnail-vs-slide.md §4 + references/cover-composition.md
Brand Logos · 在封面里融入品牌 logo(@lobehub/icons)
有些封面需要带上品牌 logo - 讲某个模型 / 产品 / 厂商时(Claude、OpenAI、Gemini、DeepSeek、Ollama……),一个官方 logo 比一行文字更快建立识别。本文档说明怎么从 [@lobehub/icons](https://lobehub.com/icons)(200+ AI/LLM 品牌 logo 合集)取图标并融进 cover-design 的 HTML 模板。
来源
- 浏览全部图标:lobehub.com/icons
- 包:
@lobehub/icons-static-svg(SVG)、@lobehub/icons-static-png(PNG,分 light/dark) - 封面是代码渲染(HTML/CSS → 截图),不需要装 npm,直接用 CDN 取静态文件即可。
取图标的两种方式
A. 用 helper 脚本下载到本地(推荐,离线可渲染)
scripts/fetch-brand-icon.sh <id> [variant] [format] [outdir]<id>:小写图标 id,如claudeopenaianthropicdeepseekgeminiollamaqwenmistralmetagooglevariant:mono(默认) |color|text|brand|brand-colorformat:svg(默认) |png-light|png-darkoutdir:默认当前目录;建议放到封面 PNG 同目录的assets/
# 官方配色 SVG(多色,自带品牌色)
scripts/fetch-brand-icon.sh claude color svg ./assets # → assets/claude-color.svg
# 单色 SVG(fill=currentColor,可被 CSS 重新着色,配深底/浅底)
scripts/fetch-brand-icon.sh openai mono svg ./assets # → assets/openai.svg脚本带 CDN 回退链(unpkg → jsdelivr → npmmirror 阿里云),并在某变体不存在时自动回退到 mono。锁版本可 export LOBE_ICONS_VERSION=1.91.0。
B. 直接引 CDN URL(联网渲染时最省事)
SVG(无 light/dark 之分):
https://unpkg.com/@lobehub/icons-static-svg@latest/icons/{id}.svg
https://unpkg.com/@lobehub/icons-static-svg@latest/icons/{id}-color.svg
https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/{id}.svg # jsdelivr 镜像
https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/{id}.svg # 阿里云镜像PNG(分 light/dark):
https://unpkg.com/@lobehub/icons-static-png@latest/light/{id}.png
https://unpkg.com/@lobehub/icons-static-png@latest/dark/{id}.png
https://unpkg.com/@lobehub/icons-static-png@latest/light/{id}-color.png渲染稳定性:headless 截图时外链图标偶有加载竞态。正式封面优先用方式 A 下载到本地再内联;快速预览可用方式 B。
变体怎么选
| variant | 文件名 | 用途 |
|---|---|---|
mono | {id}.svg | 单色,fill="currentColor",内联后用 CSS `color` 重新着色,做和封面同色系的 logo |
color | {id}-color.svg | 官方品牌色,自带配色,直接用 |
text | {id}-text.svg | 纯文字 logotype(厂商名) |
brand / brand-color | {id}-brand[-color].svg | logo + 文字横排锁版 |
不是每个 id 都有全部变体:如 openai 没有 -color(只有 mono + text),anthropic 只有 mono + text。拿不准就先试 color,脚本会自动回退 mono。
融进模板的两种放法
放法 1:内联 SVG(首选,可着色、最清晰)
把下载的 SVG 文件内容直接贴进 HTML,外面包一个 .brand-logo。mono 变体靠父级 color 着色:
<!-- 顶部品牌行:logo + 文字锁版 -->
<span class="brand-logo">
<svg ...>...粘贴 {id}.svg 的内容...</svg>
</span>.brand-logo{
display:inline-flex;align-items:center;
width:1.1em;height:1.1em; /* 跟随旁边文字字号 */
color:var(--accent); /* mono SVG 会染成强调色 */
}
.brand-logo svg{width:100%;height:100%;display:block;}- 深色底(aurora / glass-dark / terminal / spotlight 等):mono logo 用
color:var(--ink)或var(--accent)。 - 浅色底(swiss / bauhaus / editorial / brutalism):mono logo 用近黑
color:var(--ink)。 - 要官方配色:换
-color.svg,不要再设color(它自带颜色)。
放法 2:<img> 引用本地/CDN 文件(简单,但 mono 不可重新着色)
<img class="brand-logo-img" src="./assets/claude-color.svg" alt="Claude" />.brand-logo-img{height:1.1em;width:auto;display:block;}<img> 引 mono SVG 时 currentColor 会落到默认黑,深底上会看不见 - 深底配 <img> 务必用 -color 变体或 png-dark。要跟随封面色系,请用放法 1 内联。
放在封面哪里
封面是「单焦点 + 大标题」,logo 是辅助识别,别喧宾夺主:
1. 顶部品牌行(单 logo 点缀):放在 kicker / eyebrow 旁,小尺寸(≈ kicker 字号的 1.1 倍)。各模板的品牌行 class 不同:
hero-typography/swiss:.topbar里的.tagaurora/glass-dark/terminal/noir-editorial/spotlight/blueprint/holographic:.top里的.kicker- 把
.brand-logo作为该 span 的第一个 child,gap:.4em与文字分开。
2. 标题前的 lockup:标题上方放一个中等尺寸 logo(≈ 80-120px),适合「单产品解读」。 3. 角标水印:右上或右下角小 logo(≈ 56-72px,低透明度),适合系列内容统一标识。
尺寸建议(1920×1080 横屏画布):品牌行 36-48px、标题 lockup 80-120px、角标 56-72px。竖屏(1080 宽)等比缩到约 0.7×,且仍要落在中央安全带内。
对比类选题(X vs Y):双 logo 当主视觉,别塞进 kicker 小字行
讲「Codex vs Claude Code」「GPT vs Gemini」这类对决/对比时,两个 logo 是内容的核心,应该放大成标题上方的 lockup(≈ 110-130px 一个,中间夹 VS),而不是缩成 kicker 行里的小图标 —— 那样在 16:9 缩略图里几乎看不清,竖屏更糊(实测踩过)。
<!-- 标题上方,作为 .hero 的第一个 child(在 .eyebrow 之前) -->
<div class="vs-lockup">
<span class="blogo mono"><svg>...openai mono...</svg></span>
<span class="vs">VS</span>
<span class="blogo"><svg>...claude color...</svg></span>
</div>.vs-lockup{display:inline-flex;align-items:center;gap:34px;margin-bottom:40px;}
.vs-lockup .blogo{display:inline-flex;align-items:center;justify-content:center;
width:128px;height:128px;background:rgba(255,255,255,.06);
border:1px solid rgba(255,255,255,.16);border-radius:28px;
box-shadow:inset 0 1px 0 rgba(255,255,255,.22);} /* 玻璃徽章托住 logo,深底/花底都清晰 */
.vs-lockup .blogo svg{width:78px;height:78px;display:block;}
.vs-lockup .blogo.mono{color:#fff;} /* openai 等 mono → 白 */
.vs-lockup .vs{font-family:var(--font-mono);font-weight:700;font-size:40px;color:var(--ink);}竖屏把尺寸缩到约 0.85×(blogo ≈112px、VS ≈34px),仍落在安全带内。
不要做
- 不要把多色 logo 摆在花哨渐变/噪点正上方(holographic、aurora 的光晕区)- 垫一个底或挪到纯色区。
- 不要拉伸:
width:auto跟着height走,保持比例。 - 不要堆一排 logo 占满画面 - 最多 1-2 个,封面仍是大标题主导。
- 深色底别用 mono
<img>(会变黑看不见)- 用-color或内联 +color。
identity test(logo 用对了吗)
1. logo 是官方来源(@lobehub/icons),不是手搓/网图。 2. 深底/浅底对应的着色正确,logo 清晰可辨、不糊不隐。 3. logo 是辅助,标题仍是第一视觉焦点(缩到 160px 先看到标题,再看到 logo)。 4. 比例正确没有拉伸。
Brand Theme · 从产品官网提取真实品牌色 + 字体
当选题是某个具体产品 / 工具 / 开源项目(impeccable、某 SaaS、某框架)时,封面采用产品自己的品牌色和字体,比采用频道默认 lime + Manrope 更贴合产品 - 封面气质和被介绍的东西一致。
这份和 brand-logos.md(取 logo)互补:logo + 色 + 字 配齐一整套品牌匹配。
来源:impeccable 选题实战。从 impeccable.style 提取出它的「漆器 + 金箔(kinpaku)」oklch 色板和 Cormorant / Alumni / Albert 字体族,应用后封面质感明显优于默认模板。
一键提取
scripts/extract-brand-theme.sh https://impeccable.style输出一份可直接写入模板 `:root` 的 token 清单:Google Fonts 链接、字体 family 列表、颜色变量(含 oklch / hex)。
手动步骤(脚本背后的三步)
# 1) 抓官网 HTML
curl -sL https://impeccable.style/ -o /tmp/site.html
# 2) 字体:Google Fonts 链接 + family 列表
rg -oiP 'fonts\.googleapis\.com/css2\?family=[^"'\'' )]+' /tmp/site.html
# 3) 颜色:先找链接的 CSS,再从中提取颜色变量(hex / oklch / 语义品牌变量名)
for css in $(rg -oP 'href="\K/[^"]+\.css' /tmp/site.html); do
curl -sL "https://impeccable.style$css"
done | rg -oiP '\-\-[a-z0-9-]+:\s*(oklch\([^)]+\)|#[0-9a-f]{3,8})' | sort -u要点
颜色
- 直接使用 `oklch()` 原值。headless Chrome(111+)原生支持
oklch(),把官网色值原样写入模板:root,比手动转 hex 更忠实、更省事。 - 例(impeccable):金箔
--kinpaku: oklch(84% .19 80.46)、漆黑底--lacquer: oklch(7% .006 95)、香槟字oklch(91% 0 0)、铜绿 patinaoklch(70% .12 188)。 - 现代站点常把颜色定义两套(light / dark 主题)。选与封面底色一致的那套(深色封面取 dark 变体)。
- 语义变量名能反推品牌调性 -
kinpaku(金箔)、lacquer(漆器)、champagne(香槟)指向日式漆器美学,对应noir-editorial/editorial这类暖金暗调风格最匹配。变量名是判断品牌想表达什么的线索。 - 取主色(accent)+ 底色(bg)+ 文字色(ink)+ 一两个辅助色即可,不必把整套色板都写进去。
字体
- 把官网字体按封面三个角色映射:display(标题)/ body(副标)/ mono(标签)。
- 例(impeccable):display 衬线 Cormorant Garamond、wordmark Alumni Sans、body Albert Sans、mono Roboto / JetBrains Mono。
- 中文标题:拉丁品牌字体渲染不了中文。选一款气质接近的中文字体搭配:
- 官网用衬线(Cormorant / 宋体感)→ 中文用
Noto Serif SC - 官网用无衬线 grotesk → 中文用
Noto Sans SC - 拉丁数字 / 字母(如「33.3k」「AI」)可单独用品牌的拉丁 display 字体,和中文混排出层次(impeccable 封面「祛 AI 味」的 AI 用 Cormorant 斜体金箔)。
- Google Fonts 链接直接沿用官网那条(已含所有 weight),再补
Noto Serif SC/Noto Sans SC。
输出可能有噪声,频率 ≠ 品牌(重要)
脚本是机器抓取,出现次数最多的颜色往往不是品牌色。常见噪声来源:
- 代码高亮主题(Dracula / Shiki / Prism):文档站的代码块色值,数量极多。
- 站内搜索框(DocSearch / Algolia)、聊天件(Inkeep)的 UI 变量。
- 灰阶色阶(gray / slate / zinc / neutral 的 50–900)。
脚本已过滤掉上面这些常见前缀,但过滤不可能完美。最终以官网 hero / 页头肉眼确认为准 - 打开官网看背景、主按钮、强调色到底是什么,别只取脚本里频率最高的几个。
取不到时的处理
- 官网没有 CSS 变量、提取不到、或非 Google Fonts(自托管 woff2 / system 字体)时:取颜色 hex + 用最接近的 Google Fonts 替代;都不行时回退频道默认(lime
#7bff9f+ Manrope + Noto Sans SC)。 - 提取只是起点,不是照搬整站。封面仍按
cover-composition.md的三信号和单焦点来组织,品牌色 / 字只是表层样式。
第二个例子:Bun(非 oklch、浅色品牌、自托管字体)
为避免方法只贴着 impeccable 一个例子,这里再放一个完全不同的:给 Bun(bun.sh,JS 运行时)做封面。
- 颜色:
extract-brand-theme.sh https://bun.sh的原始输出里,Dracula 代码高亮和 DocSearch 变量数量压过品牌色(过滤后 Bun 的--pink-*色族才浮上来)。打开官网肉眼确认,真品牌是 奶油底 `#fbf0df` + 粉 `#f472b6` / `#ec4899` + 近黑 `#0b0a08`,hex 直接用(不是 oklch)。 - 字体:Bun 自托管 system 字体栈,零 Google Fonts。按「取不到时的处理」回退模板默认字体(Archivo + Noto Sans SC + JetBrains Mono)即可。
- 风格:浅色 + 粉 + 玩味气质 → 从
design-styles.md选neo-brutalism(产品 / 工具类、玩味醒目),用 Bun 的奶油+粉覆盖模板默认的黄+红。
要点:方法一样(提取 → 过滤噪声 → 肉眼核对 → 覆盖模板 token → 按调性选风格),但具体值、颜色格式、字体来源都和 impeccable 不同。提取脚本的输出永远是起点,不是答案。
和模板的衔接
提取出的 token 覆盖模板 :root 的默认值:--bg、--ink、--accent、--font-display、--font-mono(模板约定的最小变量集,见 SKILL.md「写新模板的约定」)。风格按官网调性从 design-styles.md 选最接近的一个,再用品牌色覆盖其 accent / bg。
Cover Composition · 封面该放哪些元素来传达选题
thumbnail-vs-slide.md 管的是易读性(大字、单焦点、160px 可读)。这份管的是内容传达 - 同样一张读得清的封面,放对元素能在三秒内说清选题,放错就只是一句好看的标语。
这份是执行层。 上游是 design-conception.md(想法先行 · 方法论主线):先按那套抽出这期的核心想法,再用下面的规则把它表达成画面元素。本文回答的是「想法怎么落到画面」,不替你想出想法。>
来源:impeccable 选题实战中的多轮用户反馈。封面初版「读得清但传达不到位」,由此逐条总结出下面这些规则。
1. 三信号原则(最高优先)
封面要让人在三秒内读出三件事。做完封面先自检这三条,缺哪个补哪个:
| 信号 | 问题 | 手段 |
|---|---|---|
| 是什么 | 这期的核心想法是谁?(来自 design-conception.md 第 2 步) | 核心想法必须是显眼主元素,不是角标。形态开放:名字 / logo / 隐喻装置 / 氛围 |
| 什么品类 | 是 agent skill?CLI?模型?框架? | 品类标签(Claude Code · Agent Skill)+ 类别专属视觉信号 |
| 什么领域 | 设计?安全?数据?运维? | 领域视觉锚(见下「领域锚」) |
「是什么」指向的是核心想法,不默认等于产品名 - 想法可能是一种氛围、一个隐喻装置。下面两个反例都是「核心想法恰好是具名产品」的情形:
反例一(impeccable):初版把中文标语「祛 AI 味」做成最大字,产品名 impeccable 缩成顶角小字,用户反馈「品牌名不显眼」。这期的核心想法就是产品本身,改成产品名为最大主元素后才对。
反例二(MiniMax M3):把角度「开放权重 · 三合一」当主角、产品名缩小 → 应让产品名当主角、角度退 accent 副线(完整复盘见 design-conception.md 文末 Worked example)。具名产品的固定层级:产品名(最大)> 角度 / 卖点(accent 副线)> 能力 / 细节(灰字)。
两条共同的教训:别把「角度 / 卖点」误当成核心想法。 但也别反过来教条 —— 若这期核心想法本就是一种氛围或一个隐喻(不是具名产品),那主元素就该是那个氛围 / 装置,而不是硬塞一个名字。先回 design-conception.md 抽对想法,再决定主元素。品类信号怎么给
- agent skill / 命令行工具 → 放斜杠命令条(
/polish /bolder /colorize)或终端提示符>,让人立刻看出是「可调用的技能 / 命令」。比文字说明更直接。 - 模型 → 厂商 logo + 参数 / 版本(见
brand-logos.md)。 - 框架 / 库 → 代码片段或 import 行 + star 数。
- 产品 / SaaS → 产品 logo + 一句价值主张。
领域锚(让人看出是哪个领域)
- 设计 → before/after UI 小样、色板 swatch、字体 specimen(
Aa)。 - 安全 → 终端告警、🔒、CVE / 红色高亮、扫描结果。
- 数据 / 分析 → 图表骨架、大数字、网格。
- 基础设施 / 运维 → 架构节点、管线箭头、blueprint 线稿。
- 领域锚常常和品类信号是同一个元素(命令条既证明是 skill,命令名又点出领域)。
2. 领域装置(一个图示同时解决领域呈现 + 价值传达 + 填空白)
封面右半 / 一侧放一个和选题价值主张对应的小图示,是性价比最高的一块 - 它一眼点明领域、演示产品做什么、又把空白填了。按选题类型选装置:
- 转变型(把 X 变成 Y / 优化前后 / 重构 / 祛 XX 味)→ before → after 对照小样。
- 两个「设备框」mini(各约 330px 宽、360px 高,圆角,像缩小的浏览器 / 卡片)+ 中间 accent 箭头
→,每框顶部标签✕ 改之前/✓ 改之后。 - before 框放该领域的典型问题(设计类 = 紫蓝渐变 + 卡片套卡片 + Inter + 灰字),after 框放改进版(用本期品牌色 / 字体做干净版)。
- 整合 / 替代型(一个工具替掉一堆)→ N 个 ✕ → 一个 ✓ 列表。
- 例:Bun 封面 -
node/npm/bundler/test runner四个框各打 ✕ → 箭头 →$ bun 一个搞定。 - 对比型(X vs Y)→ 左右两栏对照 + 中缝
vs。 - 数据 / 性能型(快多少、省多少)→ 大数字 + 单位 + 一句对比基准。
可复用的「设备框对照」partial 见 ../templates/partials/before-after.html(自带 CSS,改文案和 --accent 即用,转变型 / 整合型都能改)。两个真实实现:
- 转变型:contenthub
videos/20260602-impeccable-design-skill/cover.html的.demo块(impeccable,before/after)。 - 整合型:
~/cover-verify/skill-bun/cover.html(Bun,N→1)。
3. 默认元素集 + 精简 chrome / 品牌只出现一次
先从精简版起手,再按需加回 - 不要从塞满的模板往下删。 这是默认交付形态,不是收尾才做的验收。
默认元素集(≤ 5,模板出厂即此形态)
| # | 元素 | 是否默认 |
|---|---|---|
| 1 | kicker(品类 / 工具,一行) | 可选,最多一个 |
| 2 | 核心主元素(标题 或 logo / 隐喻装置 - 单焦点) | 必有,只一个 |
| 3 | 副标(一句点题) | 可选 |
| 4 | 署名(频道 / 作者) | 必有,只一次 |
| 5 | 领域装置(before/after、命令条、N→1、大数字) | 可选,仅当承载信息 |
默认不放(除非确有信息价值):日期、eyebrow 小字、Ep· 胶囊、扇出节点 / 纯装饰色块 / 几何形(不承载领域信息的)、上下分隔横线、// 标签 装饰、第二处署名、重复副标。
精简时别误伤风格身份:风格本身的视觉语言(swiss 网格细线、bauhaus 几何色块、terminal 扫描线 + 光标、aurora / glass 光晕、blueprint 网格线稿)是这个风格之所以成立的身份,保留。要删的是被过度填充的内容槽和不承载信息的装饰,不是风格的招牌处理。
简洁不是留白,而是去掉次要 chrome,把空间让给有信息量的视觉。
- 品牌署名全图只出现一次。 反例(本例遇到的):VerySmallWoods 同时出现在右上角和页脚,用户反馈冗余。改到只剩左下一处。
- 注意区分产品名(选题主角,要显眼)和频道署名(作者标识,低调一次即可)—— 这是两个不同的东西,不算重复。
- 默认不放这些次要 chrome,除非有信息价值:日期、
Ep ·胶囊、上下分隔横线、// 标签这类装饰性 label、重复的副标。 - 纯装饰的色块 / 几何形若不承载领域或价值信息,删掉或换成有意义的装置。 反例(本例遇到的):封面右侧放了一个抽象珊瑚色方块纯做平衡,用户反问「这么抽象吗」 - 它不传达任何信息。要么删,要么换成领域装置(before/after、命令条、N→1、大数字)。用「有意义的内容」做平衡,而不是「无意义的形状」。
- 精简后留出的空间,用 before/after、命令条、大数字这类有信息量的视觉来填充,而不是留一片黑。
4. 构图:居中之外,左锚定非对称留白也是一等公民
封面不必把主元素摆在正中。左锚定非对称构图(内容压在左 ~45%、右侧 ≥40% 留空)同样成立,而且在「冷静 / 克制 / 编辑感」调性下往往更高级 - 大片空白不是「没填完」,它就是构图本身(参考 quiet-dark 风格)。
- 所有元素共享同一条左边缘:kicker、标题、横杠、副标、署名都对齐左线,靠对齐而非居中建立秩序。
- 层级只靠字号 + 字重 + 留白:letter-spaced 微 kicker → 大字标题 → 细横杠 + 小灰副标。不靠任何装饰承载层级。
- 右侧留空别手痒去填 - 想填就回 §2 放领域装置(before/after、命令条、N→1、大数字),用有信息量的视觉,不用纯装饰色块(见 §3)。
- 竖屏窄画幅里「右侧大留白」不成立,改用「纵向大留白 + 文字左对齐」保持同一种克制感(见
quiet-dark-vertical)。
5. 强调色:当「标点」用,不当「填充」用
这是「读得清」最容易翻车的一处,单列出来(完整原理见 thumbnail-vs-slide.md §4 + SKILL.md Critical Rule 14):
- 标点式(对):强调色只出现在 kicker、一段短横杠、单个高亮词、图标、数字 - 小、实、短促。主标题保持最高对比的中性色(白压暗 / 墨压浅)。
- 填充式(错):给整段大标题上饱和色、或在同明度半透明色块上压同色文字。中等亮度 + 任何 glow → 字缘发虚,160px 缩略图必糊。
- 一句话:颜色用来「指」,不用来「铺」。 想强调,是让强调色那一小块更显眼,不是把主元素整个染色。
验收自检清单(并入 SKILL.md Step 5)
- [ ] 三信号都到位?(是什么 / 什么品类 / 什么领域 - 逐条指认画面里哪个元素负责)
- [ ] 主角(产品 / 概念)是不是最显眼的主元素?
- [ ] 品牌署名是否只出现一次?产品名与频道署名有没有混淆或重复?
- [ ] 有没有可删的次要 chrome(日期 / 胶囊 / 横线 / 装饰标签)?
- [ ] 空白处是有信息量的视觉还是纯空?转变类选题有没有放 before/after?
- [ ] 三秒原则 / 160px 可读(见
thumbnail-vs-slide.md)。 - [ ] 配色服务可读性?主标题是最高对比中性色(没被饱和色填充、没加 glow),强调色只当「标点」(kicker / 横杠 / 高亮词),没有同明度色块压同色字(见 §5)。
多方向预览工作法(推荐)
定稿前,一次给出 3-4 个不同方向供用户选择(不同风格 / 配色),而不是单版反复改 - 收敛更快,也更容易找到合适的方向。本例给了 A 杂志浅色 / B 粗野 / C 暖色块 / D 瑞士蓝 四版预览,用户选定方向后再精修。各方向存档在 cover-styles/,命名 {字母}-{风格}.html/.png。
Design Conception · 构思:想法先行(本 skill 唯一的方法论主线)
这是 cover-design 唯一的构思方法论,贯穿全流程。其余 reference(三信号、领域装置、缩略图、风格)都是它的执行层 - 先有想法,再谈怎么表达、怎么排版、怎么验证。不要混入别的设计流派,单线走到底。
>
出处:Paul Rand。
- "Don't try to be original, just try to be good."
- "The designer does not begin with a preconceived idea. The idea is the result of careful study and observation."
- "Simplicity is not the goal. It is the by-product of a good idea and modest expectations."
为什么要先有这一步
模板能套出一张「读得清」的封面,但读得清 ≠ 传达到位。一张好封面背后,先有一个想法(idea) - 这期到底要让人 get 到什么。跳过想法、直接拿标题填模板,是这个 skill 最常见的失败:封面好看,却没有重量。
抽取核心想法,是构思的第一步,也是封面质量能超过「模板套用」的地方。 这一步在 SKILL.md 工作流里是 Step 1.5,排在「收集信息」之后、「选模板」之前。
四步
1. 研究(不预设)
读完上下文(文章 / 脚本 / 选题)后,别急着想「放什么大字」。先弄清这期真正讲的是什么、对观众意味着什么。想法是研究出来的,不是拍脑袋拍的。如果用户只给了标题,主动追问或读源文,把上下文吃透再往下走。
2. 抽取核心想法 —— 形态是开放的
问自己一句:这期封面如果只能让人记住一件事,是什么? 那就是核心想法。它不一定是产品名,形态可以是:
| 形态 | 何时是它 | 例 |
|---|---|---|
| 一个名字 | 选题就是某个具名产品 / 模型 / 概念 | MiniMax M3、Claude Code |
| 一个 logo / 标志 | 品牌识别本身是重点(对决、站队、生态归属) | 两家 logo 正面对撞 |
| 一个隐喻 / 装置 | 核心是「一个转变」或「一种关系」 | before→after、N→1、X vs Y |
| 一种氛围 / 环境 | 选题传达的是一种感受 / 状态,而非某个物件 | 「深夜调试」的孤灯感、「失控」的混乱感 |
判断方法:把几个候选各自缩到 160px,哪个还能让人 get 到这期? 那个就是核心想法。
别把「角度 / 卖点」误当成核心想法。 角度再好,也只是想法的一种说法,不能盖过想法本身(MiniMax M3 完整实战见文末 Worked example)。
3. 删减(simplicity 是副产品,不是目标)
想法定了,画面上任何不为这个想法服务的元素都删掉 - 多余的日期、Ep · 胶囊、分隔横线、纯装饰色块 / 几何形、第二个署名。不是为了「看起来简洁」而删,而是因为它们不为想法服务。简洁是好想法的结果,不是你追求的目标。
纯装饰的色块 / 几何形若不承载信息(反例:某封面右侧放一个抽象珊瑚方块纯做平衡,用户反问「这么抽象吗」),删掉,或换成承载想法的装置(before/after、命令条、大数字)。用「有意义的内容」做平衡,不用「无意义的形状」。
4. 验证(test at scale)
缩到 160px,问的不是「标题读得出吗」,而是:那个核心想法还在吗? - 名字还认得出 / 氛围还透得出 / 隐喻还读得懂,才算成立。读不出,回到第 2 步:要么换一个更强的表达形式,要么把它做得更大、更狠地删掉别的。
Worked example · MiniMax M3 发布解读(思路复盘)
一次真实的构思,重点看「抽取」那一步怎么从错到对。
选题:MiniMax 在 2026-06-01 发布开源模型 M3,做一期发布解读视频。这期的角度(hook)定为「开放权重 · 三合一」 - 前沿 coding + 1M 上下文 + 原生多模态。
1. 研究:读官方博客 + 文档,确认这期讲的是 M3 这款新模型,卖点是「权重开放 + 三件能力一次到位」。
2. 抽取核心想法 —— 这里抽错过一次。
- ❌ 初版直觉:既然这期的 hook 是「开放权重 · 三合一」,就把这句话做成最大主元素,产品名 MiniMax M3 缩小。
- 用户反馈:「话题的核心应该是 MiniMax M3 这款新模型,开放权重三合一相对没那么重要。」
- ✅ 修正:这期的核心想法是 MiniMax M3 这款模型本身(形态 = 一个名字)。「开放权重 · 三合一」是它的卖点注脚,不是想法本身。教训:hook / 角度很容易被误当成核心想法 —— 它再吸引人,也只是想法的一种说法。
3. 删减:定了「M3 当主角」后,把不为它服务的删掉 —— 右侧一个纯装饰的抽象珊瑚方块(用户反问「这么抽象吗」,它不传达任何信息)删掉;重复的署名删到只剩一处;多余 chrome 精简。保留的:厂商 logo(品类信号 = 这是个模型)、MiniMax 珊瑚色(品牌匹配)。
4. 验证(test at scale):缩到 160px,问的不是「字读得出吗」,而是「MiniMax M3 这个想法还在吗」 - 在。竖版另外把左对齐大字改成水平居中,避免窄高画幅失衡(见 platform-specs.md)。
最终层级:MiniMax M3(最大,主角)> 开放权重 · 三合一(珊瑚 accent 副线)> 前沿 coding + 1M 上下文 + 原生多模态(灰字细节)> logo + 日期 + 署名(低调,各一次)。
一句话带走:先分清「这期的核心想法」和「这期的卖点角度」,让想法当主角,角度退成副线。 抽对了想法,层级、删减、模板都顺势而定。
和其他 reference 的关系(都为「表达已抽取的想法」服务)
- 想法怎么表达成画面元素 →
cover-composition.md(三信号 / 领域装置 / 层级 / 精简) - 想法用什么视觉语言 →
design-styles.md(13 风格)、brand-theme.md(品牌色 + 字)、brand-logos.md(logo) - 想法读不读得出 →
thumbnail-vs-slide.md(缩略图 / 3 秒原则)
顺序永远是:先有想法,再谈这些。 这些 reference 解决的都是「把想法表达清楚」,不是「替你想出想法」。
Design Styles · 封面风格目录
cover-design 的每个风格都落成一对模板:横屏({style}.html,16:9 / 16:10)+ 竖屏({style}-vertical.html,9:16 / 3:4)。这份文档给每个风格的视觉锚点、identity test(判定是否"做对了"的硬标准)和适用场景。新增风格时照这个结构补一段。
通用约束(所有风格都遵守,见 thumbnail-vs-slide.md 和 platform-specs.md):
- 固定像素画布(
--cv-w/--cv-h),不用 viewport 单位 - 单焦点 + 大标题(缩到 160px 仍可读)
- 竖屏文字收在中央安全带
- 一个风格 = 一套可复用的 CSS 变量 + 占位符,新增风格不动主流程
---
中文大标题的行高(line-height)
这是个真坑,不是品味问题。 拉丁字母的字身框(em box)上下自带留白(ascender 上方、descender 下方),所以西文 display 大字用 0.9 行高仍有呼吸感;中日韩方块字几乎填满整个 em box,同样 0.9 行高,两行的笔画就会贴脸甚至相撞(带 辶 / 宀 / 廴 这类上下伸展部首时最明显)。所以同字号下中文标题要比拉丁标题松一档。
两个约束的交集决定取值:
- 中文比拉丁需要更大行高(Typotheque:CJK 填满 em box;Material Design:CJK 行高比西文统一 +0.1em)。
- display 越大行高越紧(headline 安全区 1.1–1.4;Pimp my Type / Material 3)。
- 中文正文是 1.5–1.7(W3C clreq:行距取字身 50%–100%)——标题要比这紧得多。
落点:中文 display 标题 ≈ 1.05–1.10。字重越重 / 笔画越粗 / 衬线,越要往高取:
| 档 | 风格(按字重·字体) | 中文标题 line-height |
|---|---|---|
| 轻(weight ≤500) | swiss(300)、aurora(500) | 1.06 |
| 中(weight 600) | glass-dark(600) | 1.08 |
| 重(weight ≥700)/ 衬线 | hero-typography、neo-brutalism、bauhaus、brutalism、spotlight、blueprint、terminal、holographic、editorial、noir-editorial、quiet-dark、editorial-index | 1.10 |
注意:
- line-height 只影响多行标题。这些模板标题都是两行
.stack,所以都生效;单行标题取多少都无所谓。 - 取值按中文最坏情况定;纯英文标题在同一行高下只是略松,不影响。
- 副标 / 正文(
.sub)保持 1.1–1.25,不动。 - 改完务必渲染验证:横屏挑一个重字重风格(如 neo-brutalism)确认不撞行,竖屏确认三行标题仍在安全带内。
---
已实现
hero-typography(黑底霓虹)
- 锚点:纯黑底 + 霓虹大字(lime)+ 两侧 fan-out 节点 + radial glow
- identity test:①纯黑底 ②单一霓虹强调色 ③大标题字重 ≥800(越大越粗)④装饰是发光节点/连线
- 适合:技术解读、新功能发布、概念视频,强视觉钩子
swiss(瑞士国际主义)
- 锚点:暖纸底 + 严格网格 + 极轻大字 + 单一高饱和强调色 + 1-2px 细线,左对齐非对称留白
- identity test:①大标题(≥72px)字重 ≤300(越大越细)②不加载衬线 ③分隔只用 hairline/网格线,无卡片/阴影/圆角 ④恰好一个强调色
- 强调色预设:IKB 蓝
#0a1cff、安全橙#ff5a1f、柠檬绿#b6ff3b、柠檬黄#e8ff00(只选一个) - 适合:技术拆解、评测、数据类,冷静专业
neo-brutalism(新粗野)
- 锚点:高饱和撞色底(亮黄/亮粉/电蓝)+ 粗黑描边(4-6px)+ 硬阴影(offset 实色,无模糊)+ 圆角块 + 粗黑字
- identity test:①每个主元素有 ≥4px 纯黑 border ②阴影是
Npx Npx 0 #000(hard,无 blur)③≥2 个高饱和色块 ④无渐变、无柔和阴影 - 字体:粗 grotesque(Archivo / Space Grotesque)+ Noto Sans SC
- 适合:产品发布、工具类、玩味醒目的选题
bauhaus(包豪斯)
- 锚点:米白/黑底 + 三原色(红黄蓝)色块 + 圆/三角/方几何构成 + 粗网格 + 大写无衬线
- identity test:①只用红黄蓝 + 黑(不加其他色相)②画面有圆/三角/矩形几何元素参与构图 ③字体几何无衬线(Futura/Poppins 类)④构图基于网格
- 适合:设计/理论/艺术类、经典感选题
editorial(杂志风)
- 锚点:暖纸底 + 衬线大标题(Songti/Playfair 类)+ 安静无衬线正文 + 氛围背景(纸纹/墨晕)+ 细线/分栏/pull quote
- identity test:①大标题用衬线字族 ②背景有氛围层(grain/纸纹/墨色),不是纯平色 ③"越大越细"(display weight ~500 + 宽字距)④有杂志感元素(栏线、引文、页眉 ledger)
- 适合:观点、深度长文、人物、叙事类
brutalism(粗野)
- 锚点:白底 + 系统默认字体(Times/Courier/Arial)+ 硬黑边框 + 刻意"未加工"的排版 + 裸链接/下划线感
- identity test:①字体是系统默认/等宽(不加载花哨 web font)②硬边框、无圆角无阴影 ③高对比黑白为主,强调色至多一个且生硬 ④排版刻意朴素甚至"丑"
- 适合:黑客、独立开发、逆向工程、反精致气质的技术内容
aurora(极光渐变)
- 锚点:近黑深色底 + 2-3 团彩色径向光晕(蓝/紫/青)柔和叠加 + 细网格 + 渐变描字
- identity test:①深色底 ②多团彩色光晕通透叠加 ③大字偏细(weight ≤500)④无硬边框/无硬阴影,整体通透(Vercel / Linear / OpenAI 那种现代 AI/SaaS 审美)
- 适合:AI / SaaS / 现代科技产品解读,当下感最强
- 强调色:默认蓝紫青三色光晕,可整体换色相
glass-dark(深色玻璃拟态)
- 锚点:深底 + 背后柔光透出 + 磨砂玻璃面板(backdrop-blur + 半透明 + 1px 高光描边 + 大圆角)
- identity test:①深色底 ②背后 2-3 团柔光 ③主内容在磨砂玻璃面板里(模糊 + 透明 + 高光边)④大字偏白通透,无硬阴影
- 适合:AI / 产品 / 现代科技解读,通透高级(visionOS / Apple 质感)
- 强调色:默认蓝紫青光晕,可整体换色相
terminal(终端黑)
- 锚点:纯黑 + 全等宽字 + 绿/琥珀荧光 + 扫描线 + 命令提示符 + 光标块
- identity test:①纯黑底 ②全等宽字体 ③单一荧光色带 glow ④有扫描线 + prompt + 闪烁光标
- 适合:CLI / 开发 / 黑客 / 逆向 / 命令行工具
- 换色:默认荧光绿,琥珀把
--term改#ffb000
noir-editorial(暗调杂志)
- 锚点:近黑底 + 衬线大标题 + 单一暖金强调 + 胶片颗粒 + 细线/№ 页眉
- identity test:①近黑底(非纯黑)②大标题衬线字族 ③恰好一个暖金强调色 ④背景有颗粒氛围,"越大越细"
- 适合:观点 / 深度长文 / 人物 / 叙事(editorial 的暗黑高级版)
spotlight(聚光戏剧光)
- 锚点:全黑 + 单束聚光(径向亮斑)落在标题 + 四周强暗角 + 高对比白字
- identity test:①近黑/全黑底 ②单束聚光照亮标题区 ③明显暗角 vignette ④明暗反差极大(chiaroscuro)
- 适合:发布 / 悬念 / 重磅选题,电影海报感
blueprint(深蓝图)
- 锚点:深藏青底 + 白色双层网格 + 线稿装饰(角标尺/标注线/⌖)+ 等宽标注,标题描边
- identity test:①深藏青/蓝图蓝底 ②细网格 + 主网格两层 ③有工程线稿元素(标注线/十字/尺寸线)④等宽标注字
- 适合:架构 / 原理 / 技术拆解,工程蓝图气质
holographic(暗调全息)
- 锚点:深底 + 油膜虹彩渐变大字(青/品红/黄/绿)+ 全息箔色带 + 微噪点
- identity test:①深色底 ②大字填充多色虹彩渐变 ③有一条全息箔色带 ④微噪点/微光,premium 不廉价
- 适合:前沿 / 概念 / 潮流科技
- 字体:Space Grotesk + Noto Sans SC
quiet-dark(极简黑)
- 锚点:近黑底 + 左锚定非对称留白 + 纯白重字大标题 + 单一强调色只做「标点」(kicker + 短横杠)+ 极淡网格 + 1px 内框,零发光/阴影/渐变
- identity test:①近黑底,结构只靠极淡网格 + 1px 内描边暗示(felt not seen)②内容左锚定,右侧 ≥40% 留空 ③主标题纯白、是全图对比最高的元素,强调色绝不用作大标题填充 ④强调色只出现在小而实的标记(kicker / 横杠 / 单个高亮词),无任何 glow
- 跟邻近风格的边界:vs
hero-typography(居中 + 霓虹大字 + 发光)→ 这个左锚定、纯白、零发光;vsswiss(暖纸底 + 极轻字重 ≤300)→ 这个黑底、重字重 ≥700;vsspotlight(聚光 + 强暗角)→ 这个全程平光、无戏剧光 - 强调色:默认电光蓝
#2f6bff,可换任一高饱和色(只用作标点,不影响标题对比) - 适合:冷静 / 克制 / 高级调性 - 发布解读、观点、系列封面、需要呼吸感的选题
- 字号注意:编辑式多词标题默认 ~132px(让短语单行不折),1-3 个短词时可上推 200-260px(见
thumbnail-vs-slide.md§1 例外)
editorial-index(编辑目录 / 报头)
- 锚点:暖纸底 + 大 wordmark 报头(下压粗规则线)+ 编号目录(ruled contents,行间 1px hairline)+ 单一强调色(只落在 wordmark 重点字 + 行号)+ 严格左对齐网格,无卡片/阴影/圆角
- identity test:①报头:大 wordmark + 右侧期号/来源 + 一条 ≥3px 规则线 ②正文是「编号 + 条目」的目录,行间 hairline 分隔 ③暖纸底 + 恰好一个强调色 ④等宽字做编号/meta,grotesque + Noto Sans SC 做条目
- 关键区别:这是组合式版式(报头 + 目录)而非单大标题 - 它刻意打破单焦点,因为「一期速读 / 榜单 / 目录」本身就是多条目。视觉气质 Swiss-邻近(暖纸 + hairline + 单强调色),但版式不同
- 适合:AI 早读 / 日报周报 / 合集 / 榜单 Top N / 专题索引 - 把整期 lineup 一眼摆出
- 取舍:multi-item,全幅 / 大图(文章 hero、Substack 题图、X Card)下最好看;缩到 160px 列表会糊,需小缩略图可读时改用单焦点风格(见
thumbnail-vs-slide.md「何时违反这些规则」)。行数 3-6(竖屏 4-5),多了减字号
---
写新风格的检查清单
1. 横屏 {style}.html + 竖屏 {style}-vertical.html 两个文件 2. :root 暴露 --cv-w --cv-h --bg --ink --accent --font-display --font-mono(按风格可加) 3. 文件顶部 HTML 注释写:支持比例 + 怎么切 --cv-h + 适配平台 + identity test 摘要 4. 竖屏文字收在中央安全带,带 body.show-guide 辅助线 5. 在本文件加一段(锚点 + identity test + 适用场景),并在 SKILL.md Step 2 列入候选 6. 渲染验证:横屏 16:9 + 竖屏 9:16 各截一张,肉眼确认 identity test 全过
Platform Specs · 各平台封面规格与长宽比
封面要覆盖横屏长视频(YouTube / Bilibili)和竖屏短视频(抖音 / 视频号)。去重后有 4 种核心长宽比,分两个模板族生成。规格来自各平台 2026 年公开建议(见 SKILL.md 末尾来源)。
4 种核心长宽比
| 比例 | 朝向 | 设计画布(--cv-w × --cv-h) | 模板族 | 主要平台 |
|---|---|---|---|---|
| 16:9 | 横 | 1920 × 1080 | hero-typography | YouTube、横屏短视频 |
| 16:10 | 横 | 1920 × 1200 | hero-typography | Bilibili |
| 9:16 | 竖 | 1080 × 1920 | hero-typography-vertical | 抖音、视频号(竖屏视频) |
| 3:4 | 竖 | 1080 × 1440 | hero-typography-vertical | 视频号(官方封面外围)、抖音九宫格裁切 |
同一个横屏设计可同时导出 16:9 和 16:10(只改 --cv-h);同一个竖屏设计可同时导出 9:16 和 3:4。所以一期视频通常做 1 个横屏 + 1 个竖屏设计,导出 4 张图。各平台落点像素与导出
| 平台 | 用哪个比例 | 平台展示像素 | 导出建议 |
|---|---|---|---|
| YouTube 缩略图 | 16:9 | 1280×720(最小宽 640,≤2MB) | 设计 1920×1080 → 下采样 1280×720 |
| Bilibili 封面 | 16:10 | 推荐 1146×717,高清 1920×1200 | 封面位是 16:10,不是 16:9;16:9 图会被裁/留边 |
| 抖音 竖屏 | 9:16 | 视频 1080×1920,封面实际显示带 ~1080×1464,九宫格裁 3:4 | 设计 1080×1920,核心放中央安全带 |
| 抖音 横屏 | 16:9 | 1080×608 | 用横屏设计下采样 |
| 视频号 竖屏 | 3:4(外围)+ 9:16 安全区 | 视频满屏 6:7(1080×1260),官方封面建议外围 3:4、中间留 9:16 安全区 | 用竖屏设计,核心居中 |
| 视频号 横屏 | 16:9 | 1080×608 | 用横屏设计下采样 |
竖屏安全区(必须遵守)
抖音首尾会叠 UI(顶部进度/账号、底部字幕和按钮),视频号首尾可能被裁、九宫格还会裁成 3:4。所以:
- 所有文字放在垂直居中的安全带里,上下各留足余量(≥15-18% 画布高度)。
- 主视觉(大标题 / 产品名)默认水平居中。 窄高画幅里,左对齐的大字会把重量压向一侧、显得不平衡 - 横版能成立的左对齐编辑感,到竖版要改成居中。编辑 / 瑞士这类横版走左对齐的风格,做竖版时务必把核心大字块居中(实战反馈:竖版左对齐「核心大字体都在左对齐,显得很不平衡」)。顶/底的细线 chrome(kicker 行、页脚)可保留 full-width,不必跟着居中。
- 竖屏模板
hero-typography-vertical.html用 flex 居中,天然把内容收在中央;不要把品牌行/署名贴到上下边缘。 - 设计时给
<body>加class="show-guide"可显示 ~1080×1464 的抖音显示带辅助线,最终渲染前去掉。
命名约定
一期视频导出多张图时,文件名带比例后缀,便于区分和上传:
cover-16x9.png # YouTube / 横屏短视频
cover-16x10.png # Bilibili
cover-9x16.png # 抖音 / 视频号 竖屏
cover-3x4.png # 视频号官方封面 / 抖音九宫格对应的 HTML 源文件同名:cover-16x9.html 等(或一个横屏 HTML + 一个竖屏 HTML,渲染时改 --cv-h)。
规格来源
各平台尺寸为 2026 年公开建议,会变动,发布前以平台当时的创作者后台为准。
- YouTube 缩略图 1280×720 / 16:9:YouTube 帮助中心及多家 2026 尺寸指南
- Bilibili 封面 16:10(1146×717 / 1920×1200):B 站创作中心封面建议
- 抖音竖屏 1080×1920、显示带 ~1080×1464、九宫格 3:4:抖音创作者尺寸说明
- 视频号竖屏满屏 6:7(1080×1260)、封面外围 3:4 + 中间 9:16 安全区、横屏 16:9(1080×608):微信视频号官方建议
渲染管线 · Chrome DevTools MCP 截图
把 HTML 封面渲染成 PNG 的标准三步加陷阱说明。
最快路径:用 scripts/render-cover.sh
多比例渲染 + 生成可 Read 的预览图,已打包成一个脚本,优先用它,别每次手搓 headless 命令:
scripts/render-cover.sh <定制好的.html> <持久输出目录> [比例...]
# 横屏自动出 16x9 + 16x10;竖屏自动出 9x16 + 3x4;也可显式指定
scripts/render-cover.sh cover-h.html ~/covers/demo
scripts/render-cover.sh cover-v.html ~/covers/demo 9x16产物:cover-<ratio>.png(2x 成品)+ cover-<ratio>.preview.png(≤1400px,给 Read 工具看验收)。脚本把下面这些坑都内置了。要手动渲染时,照这些坑做。
踩过的坑(务必避开)
1. 输出别用 `/tmp` —— 会被清空,渲染到一半文件就没了。永远输出到持久目录(如 ~/covers/<slug>)。 2. zsh 不对 `$VAR` 分词 —— for s in $STYLES(STYLES 是空格分隔的变量)在 zsh 里只迭代一次,会把整串当一个文件名。脚本里用字面量列表或数组,别靠变量分词。 3. 渲染和 `sips` 缩图分两段 —— headless Chrome 写完 PNG 常不退出,把渲染 + 缩图串在一条命令里,整条会被后台化/截断,缩图永远不执行。先渲染、wait + 有界轮询确认 PNG 落地,再单独缩图。 4. 2x 成品 >2000px,Read 工具读不了 —— 每张成品配一张 sips -Z 缩到 ≤1400px 的预览再 Read。 5. `wait` 早返回 —— headless Chrome 常在截图落盘前就让 wait 返回。落盘后再处理要轮询文件存在(until [ -s "$png" ],加超时),不要 wait 完立刻读。 6. `set -e` + `read <<<(...)` 会误中断 —— 进程替换里的 read 读到无换行结尾返回非零,在 set -e 下会炸。批处理脚本别开 -e,零星非零退出码不该中断整批。
标准命令序列
mcp__chrome-devtools__new_page → 打开 file:// 路径
mcp__chrome-devtools__resize_page → 设到 画布宽 × (画布高 + 20)
mcp__chrome-devtools__navigate_page → reload (新尺寸下重新布局)
mcp__chrome-devtools__take_screenshot → fullPage: true,保存到 cover-{比例}.png多比例渲染
每个目标比例渲染一次。改 HTML 里 :root 的 --cv-h(横屏族 16:9↔16:10,竖屏族 9:16↔3:4),resize 到对应尺寸,截图存成带比例后缀的文件。resize 高度比画布多 20px 留余量(见视口陷阱)。
| 比例 | --cv-w × --cv-h | resize(width × height) | 输出文件 | 输出像素(2x) |
|---|---|---|---|---|
| 16:9 | 1920 × 1080 | 1920 × 1100 | cover-16x9.png | ~3840×2160 |
| 16:10 | 1920 × 1200 | 1920 × 1220 | cover-16x10.png | ~3840×2400 |
| 9:16 | 1080 × 1920 | 1080 × 1940 | cover-9x16.png | ~2160×3840 |
| 3:4 | 1080 × 1440 | 1080 × 1460 | cover-3x4.png | ~2160×2880 |
同一个浏览器页面可复用:改 --cv-h 后 reload 再截图,省去重开页面。比例集合与平台映射见 platform-specs.md。
Step 1 - 打开页面
new_page(url: "file:///Users/.../cover.html")返回页面列表,新页面通常会自动 [selected]。如果之前有页面占着,后续命令会作用在新打开的这个上。
Step 2 - 调视口大小
resize_page(width: 1920, height: 1100)高度故意设到 1100 而不是 1080。原因见下方"视口陷阱"章节 - 多 20px 让浏览器 UI 余出空间,避免内容被压缩。
Step 3 - Reload
navigate_page(type: "reload")resize 之后必须 reload。原因:CSS 里的 clamp(_, _vw, _) 在 viewport 改变时不会自动重算,需要文档重新解析才能拿到正确的尺寸。
Step 4 - 截图
take_screenshot(
filePath: "/Users/.../cover.png",
format: "png",
fullPage: true,
)`fullPage: true` 是关键。fullPage: false 只截视口可见区域,因为视口陷阱问题底部会被裁掉。fullPage: true 截完整 document,刚好对应 1920×1080 的 .cover 元素。
视口陷阱
resize_page(width: 1920, height: 1080) 不等于实际可用视口是 1920×1080。Chrome 的浏览器 UI(标签栏、URL 栏、bookmark 栏、Developer Tools 等)会占掉一部分高度。实测在 macOS Chrome 上,设 1920×1080 实际拿到的 viewport 是 1920 × 714 左右,差了将近 366px。
如果 HTML 用 100vh 决定 .cover 高度,内容会被压缩到 714px 高,底部行被裁。
两个修正
1. CSS 用固定像素 - .cover { width: 1920px; height: 1080px; } 不依赖视口。 2. 截图用 fullPage - 不依赖视口大小,截整个 document。
两条都要做。只做其一仍会出问题:
- 只用固定像素 + 非 fullPage 截图 → 截到的图被视口裁剪
- 只用 fullPage 截图 + 100vh →
.cover高度变成 714px,内容被压缩
输出尺寸
截图默认按当前设备的 device pixel ratio 输出。在 macOS Retina 屏上 DPR = 2,所以 1920×1080 的 CSS 像素 → 3840 × 2160 的实际像素 PNG。
这是好事 - 高分辨率 PNG 缩到平台落点尺寸(YouTube 1280×720、Bilibili 1146×717、抖音 1080×1920、视频号 1080×1440、X Card 1200×630)都还清晰。各平台精确像素见 platform-specs.md。
如果需要严格 1920×1080 输出,可在 Step 1 之前加:
new_page(url: "...")
# 用 evaluate_script 强制 DPR = 1
# (一般不需要,3840×2160 直接缩到 1920×1080 质量更好)常见错误排查
| 现象 | 可能原因 | 修法 |
|---|---|---|
| 截图底部被裁 | 用了 fullPage: false 或 CSS 用了 100vh | 改成 fullPage: true + 固定像素 |
| 字号比预期小 | clamp() 里 _vw 没达到最大值,因为视口宽度不够 | resize 到至少 1920 宽 |
| 字体加载失败 | Google Fonts 没加载完就截图了 | 截图前用 wait_for 等待 / mcp__chrome-devtools__wait_for |
| 颜色偏暗 / 偏亮 | Chrome 用了自动暗色模式 | 加 <meta name="color-scheme" content="dark"> |
| PNG 是 1920×714 | 视口陷阱,见上方 | 改用 fullPage: true |
完整调用示例
# Step 1
mcp__chrome-devtools__new_page(
url: "file:///Users/wyang14/github/contenthub/videos/20260529-claude-code-dynamic-workflows/cover.html"
)
# Step 2
mcp__chrome-devtools__resize_page(width: 1920, height: 1100)
# Step 3
mcp__chrome-devtools__navigate_page(type: "reload")
# Step 4
mcp__chrome-devtools__take_screenshot(
filePath: "/Users/wyang14/github/contenthub/videos/20260529-claude-code-dynamic-workflows/cover.png",
format: "png",
fullPage: true
)
# Step 5 (optional - 给用户预览)
Bash: open /Users/wyang14/github/contenthub/videos/20260529-claude-code-dynamic-workflows/cover.png回退:headless Chrome(MCP 被占用时)
如果 Chrome DevTools MCP 报 browser is already running ... profile —— 说明已有 Chrome 占着 MCP 的 profile 锁,MCP 连不上。不要去劫持用户当前的标签页。改用 headless Chrome 直接截图(用独立 profile,不抢锁):
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless=new --disable-gpu --hide-scrollbars \
--user-data-dir=/tmp/chrome-render-profile \
--force-device-scale-factor=2 \
--window-size=1080,1920 \
--virtual-time-budget=5000 \
--screenshot=/path/to/cover-9x16.png \
"file:///path/to/cover.html"--window-size设成该比例的画布尺寸(横屏 1920×1080 等),截图即视口,正好对应固定像素.cover--force-device-scale-factor=2出 2x retina(如 1080×1920 → 2160×3840)--virtual-time-budget=5000给 Google Fonts 留加载时间,否则字体可能没就绪就截了- 每个比例改
--window-size和 HTML 里的--cv-h,跑一次
坑:`--headless=new` 写完 PNG 后常常不退出(进程挂住)。所以不要把多个渲染串行写在一条命令里 - 第一个会卡住,后面的永远不跑。两个稳妥做法: 1. 每个渲染用独立 --user-data-dir,detached 并行起(( "$CHROME" ... & )),PNG 落地后再 pkill -f "Google Chrome.*--headless" 统一收尸; 2. 或用 --headless(老模式),截完更可能自己退。 PNG 本身是好的,挂住的只是进程退出。
性能与并发
每次 take_screenshot 大约 1-3 秒(取决于字体加载、渲染复杂度)。一次性渲染多版本可以:
1. 在同一个浏览器实例里复用页面,每次只换 URL 2. 或者在不同模板间用 evaluate_script 切换 CSS 变量,只截图不 reload
但绝大多数封面只渲染 1-2 次,简单串行就够,不用过早优化。
Thumbnail vs Slide · 为什么封面不能像幻灯片
封面和幻灯片在视觉上看着都是 16:9,但服务的场景完全不同。把幻灯片那套搬过来做封面会失败 - 这份 reference 把两者的差别说清楚,后续写新模板时不要踩坑。
使用场景对比
| 封面 (Thumbnail) | 幻灯片 (Slide) | |
|---|---|---|
| 出现位置 | 订阅列表 / 推荐位 / 搜索结果 | 视频播放区域 / 全屏演示 |
| 观看距离 | 缩略图 160-320px 宽 | 1080p 全屏 |
| 观看时长 | 0.5-2 秒决定要不要点 | 30-60 秒一页 |
| 信息密度 | 极低 (1 个主标 + 1-2 个支撑元素) | 中等 (多 bullet / 数据 / 图表) |
| 目标 | 让人想点进来 | 让看进来的人理解某一点 |
设计规则
1. 3 秒原则
封面在缩到 160px 宽时,主标题必须 3 秒内能读出来。这意味着:
- 主标题字号 ≥ 200px (在 1920×1080 画布上)
- 主标题字数 ≤ 3 个英文词 或 ≤ 8 个中文字
- 字体粗细 ≥ 800 (Manrope Bold 或同等级)
幻灯片完全相反 - 字号 60-80px 是正常的,bullet 多行也行,因为观众在全屏看。
例外:编辑式多词标题(如 quiet-dark 那种「The Harness / Is The Product」整句拆两行)字号天然比单词 hero 小一档(~130-170px),靠最高亮度对比 + 大留白而非纯字号取得可读性 - 这类风格里 200px 会折行撞版。短到 1-3 个词时再把字号推回 200px+。2. 单焦点
封面应该有一个让眼睛停下来的元素。不是两个,不是四个 bento tile,是一个。
幻灯片可以有多焦点(标题 + 几条 bullet + 图表),因为观众会顺序读完。封面观众根本不读全,只扫一眼,所以多焦点 = 没有焦点 = 没人点。
错误示范:
[Logo] [Date]
[标题 1] [标题 2]
[副标 1] [副标 2]
[数据 1] [数据 2] [数据 3] [数据 4]这是 dashboard 或 slide,不是 thumbnail。
正确示范:
[BIG BOLD TITLE]
[supporting subtitle]
[small author info]3. 留白即信息
幻灯片留白可以填,封面留白要保持。原因:
- 缩略图小,任何角落塞东西都会变噪点
- 大留白让主标题更突出
- 留白本身是品牌一致性的一部分(系列内容看上去同一个频道出的)
具体比例:主标题区域占画面 40-60%,其余全部用作呼吸。
4. 颜色对比
封面在小尺寸下,弱对比的颜色会糊成一团。规则:
- 背景和主标题对比度 ≥ 7:1 (WCAG AAA)
- 强调色和背景对比度 ≥ 7:1
- 副标题颜色不要太接近背景
黑底 + lime 是高对比组合;深灰 + 暖白则是低对比,封面会显得"灰扑扑"。
对比是亮度对比,不只是配色 - 这决定了字清不清。 易读性取决于前景与背景的亮度差:白字压黑底 ≈ 21:1,是能做到的最锐利边缘。把一个饱和强调色用作大标题的填充(整段标题上 lime / 蓝),强调色处在中等亮度,字缘对比骤降;再叠任何 text-shadow / glow,字缘进一步被晕开 → 缩到 160px 就糊。所以一条铁律(见 SKILL.md Critical Rule 14「配色服务可读性」):
- 标题的「体量」留给最高对比的中性色(白压暗 / 墨压浅),不要给整段大标题上饱和色;主标题上别加发光。
- 强调色当「标点」用:kicker、一段短横杠、单个高亮词、图标、数字 - 小而实,短促出现,对比够就行。
- 绝不在同明度的半透明色块上压同色文字(例如 lime 文字压
rgba(lime,0.1)衬底)—— 这是「彩色字发虚」最常见的来源,要么文字用实色,要么衬底换成实底反白。
quiet-dark 风格就是这条规则的范本:纯白标题 + 蓝色只出现在 kicker 和横杠。
5. 不要小图标
幻灯片可以放 16-24px 的小图标说明 bullet 含义。封面不要,理由:
- 缩到 160px 后小图标变成 2-3px 像素,完全看不出是什么
- 反而吃掉本可以让主标题更大的空间
如果非要图标,至少 80px,且要么有色块衬底,要么作为装饰呼应主标题。
模板设计时的常见反模式
写新模板时这几个反模式要避开:
| 反模式 | 为什么不行 | 替代方案 |
|---|---|---|
| Bento 网格 (4 个 tile 平均分) | 多焦点,小尺寸下每个 tile 都糊 | 单 hero + 1-2 个装饰 |
| Hero card + 右侧 4 metric | 同上,而且 metric 数字小 | 主标题占大头,metric 删掉或合并成一句话 |
| 信息密集的 chip / tag 行 | 看不清,且让人觉得"信息量大要花时间读" | 最多 1-2 个,字号要够大 |
| 暗色渐变(深灰 → 深黑) | 缩略图下渐变消失,变纯灰 | 纯色背景 + 一个 radial glow 提亮中心 |
| 装饰性 fan-out 网络 (几十个节点) | 缩成一团黑点 | 5-7 个节点足够,大节点 + 粗连线 |
系列一致性
如果是系列视频(VerySmallWoods 每期一个发布解读),封面要保持:
- 背景色固定 (比如全系列都纯黑)
- 强调色固定 (比如全系列 lime
#7bff9f) - 字体固定 (Manrope + Noto Sans SC + JetBrains Mono)
- 品牌行位置固定 (顶部 brand-mark,底部 author)
变的只有:主标题、副标题、可能的强调元素(节点图 / 几何符号 / 数据)。
这样观众在订阅列表里一眼能认出是同一个频道,即使没看到频道名。
何时违反这些规则
教程类 / 配置类 / 步骤类视频的封面有时确实需要列几个要点(比如"3 个步骤"、"5 个工具")。这种情况下:
- 仍然保持一个主标题(比如"3 步搞定")作为最大字号
- 3 个要点字号小一档(主标题的 40-60%)
- 要点之间不要 bento 框,用 inline 或 stack 方式呈现
- 整体不超过 5 行文字
不要为了"信息丰富"把封面塞满 - 信息丰富在视频里讲,封面只负责让人点进来。
「目录 / 速读」类是另一种正当例外。 一期速读、日报周报、榜单 Top N、合集 - 内容本身就是一个列表,这时 editorial-index 风格(报头 + 编号目录)主动放弃单焦点是合理的:它把整期 lineup 一眼摆出,适合全幅 / 大图场景(文章 hero、Substack 题图、X Card、横屏视频封面)。代价:缩到 160px 列表会糊,所以需要小缩略图也读得出时(订阅列表 / 推荐位),改用单焦点风格(quiet-dark 大标题 / 大数字)。同一期可以两套并用 - 列表版做题图,单焦点版做缩略图。
#!/usr/bin/env bash
# extract-brand-theme.sh — 从产品官网提取品牌色 + 字体,供封面做「品牌匹配」。
#
# 当选题是某个具体产品/工具/开源项目时,用它官网自己的品牌色和字体做封面,
# 比套频道默认 lime + Manrope 更原生。配方与避坑见 references/brand-theme.md。
#
# 用法:
# extract-brand-theme.sh <url>
# <url> 产品官网首页,如 https://impeccable.style
#
# 输出(stdout,可直接写入模板 :root):
# - Google Fonts 链接 + 解析出的 family 列表(按 display/body/mono 自己分工)
# - 颜色变量与字面量(含 oklch 原值,可原样写入 Chrome)
# - 语义化品牌变量名(反推调性,如 kinpaku/lacquer → 日式漆器暖金)
#
# 依赖: curl + rg(ripgrep)。
set -uo pipefail # 不开 -e:单个 css 取不到不应中断整体
URL="${1:?need a url, e.g. https://impeccable.style}"
command -v rg >/dev/null 2>&1 || { echo "need ripgrep (rg)" >&2; exit 1; }
command -v curl >/dev/null 2>&1 || { echo "need curl" >&2; exit 1; }
# origin = scheme://host(用于补全相对 css 路径)
ORIGIN="$(printf '%s' "$URL" | sed -E 's#^(https?://[^/]+).*#\1#')"
TMP="$(mktemp)"; trap 'rm -f "$TMP"' EXIT
curl -sL --max-time 25 "$URL" -o "$TMP" || { echo "fetch failed: $URL" >&2; exit 1; }
[ -s "$TMP" ] || { echo "empty response: $URL" >&2; exit 1; }
echo "# Brand theme extracted from: $URL"
echo "# origin: $ORIGIN"
echo
echo "## Google Fonts 链接(直接写入 <head>)"
rg -oiP 'https://fonts\.googleapis\.com/css2\?family=[^"'\'' )]+' "$TMP" | sort -u
echo
echo "## 字体 family(自己按 display / body / mono 分工,中文另配 Noto Serif/Sans SC)"
rg -oiP 'family=\K[A-Za-z0-9+]+' "$TMP" | sed 's/+/ /g' | sort -u
echo
# 收集所有 CSS:内联 <style> + 链接的样式表
CSSALL="$(mktemp)"; trap 'rm -f "$TMP" "$CSSALL"' EXIT
cat "$TMP" >> "$CSSALL" # 内联 style / CSS-in-HTML 也一并纳入
# 链接的样式表(相对 / 绝对 / 协议相对)
while IFS= read -r href; do
case "$href" in
http*) url="$href" ;;
//*) url="https:$href" ;;
/*) url="$ORIGIN$href" ;;
*) url="$ORIGIN/$href" ;;
esac
curl -sL --max-time 20 "$url" >> "$CSSALL" 2>/dev/null || true
done < <(rg -oiP '<link[^>]+rel="stylesheet"[^>]+href="\K[^"]+|href="\K[^"]+\.css[^"]*' "$TMP" | sort -u)
# 噪声来源:站点搜索框(DocSearch/Algolia)、代码高亮主题(Dracula/Shiki/Prism/highlight.js)、
# 文档框架(Docusaurus --ifm-*)、灰阶色阶(gray/slate/zinc/neutral/stone)。这些常压过真正的品牌色。
NOISE='docsearch|algolia|inkeep|--ifm|shiki|prism|hljs|highlight|token|gray|grey|slate|zinc|neutral|stone'
echo "## 颜色:品牌相关的 CSS 变量(已过滤搜索框 / 代码高亮 / 灰阶噪声)"
rg -oiP '\-\-[a-z0-9-]+:\s*(oklch\([^)]+\)|#[0-9a-f]{3,8}\b)' "$CSSALL" 2>/dev/null \
| rg -ivP "$NOISE" | sort -u | head -50
echo
echo "## ⚠️ 频率最高 ≠ 品牌色。机器抓取仍可能混入噪声(代码高亮、搜索框、灰阶)。"
echo "## 真正的品牌色以官网 hero / 页头实际呈现为准 - 打开官网,肉眼确认背景、主按钮、强调色,"
echo "## 不要只取这里出现次数最多的几个。"
echo
echo "## 颜色:裸字面量(噪声更多,仅在上面变量没取到时参考)"
rg -oiP '(oklch\([^)]+\)|#[0-9a-f]{6}\b)' "$CSSALL" 2>/dev/null | rg -ivP "$NOISE" | sort -u | head -30
echo
echo "## 语义品牌变量名(反推调性 - 如 kinpaku/lacquer 暗示暖金漆器;docsearch/gray 等噪声已过滤)"
rg -oiP '\-\-[a-z]+(?:-[a-z0-9]+)*(?=:\s*(oklch|#|rgb))' "$CSSALL" 2>/dev/null \
| rg -ivP '^--(color|bg|fg|ink|text|border|card|cat|demo|font|space|size|radius|shadow|gap|width|height|x|y|c)$' \
| rg -ivP "$NOISE" | sort -u | head -40
echo
echo "# 下一步:选 accent/bg/ink + display/body/mono,覆盖模板 :root;风格按调性从 design-styles.md 选。"
echo "# 拿不准就打开官网肉眼对一遍(频率≠品牌)。无 Google Fonts(自托管 system 字体)时回退模板默认字体。"
echo "# 详见 references/brand-theme.md"
#!/usr/bin/env bash
# fetch-brand-icon.sh — 从 @lobehub/icons 下载品牌 logo 到本地,供封面内联使用。
#
# 来源: https://lobehub.com/icons (@lobehub/icons,200+ AI/LLM 品牌 logo)
# 用法:
# fetch-brand-icon.sh <id> [variant] [format] [outdir]
# <id> 图标 id(小写),如 claude / openai / anthropic / deepseek / gemini / ollama / qwen
# [variant] mono(默认) | color | text | brand | brand-color
# [format] svg(默认) | png-light | png-dark
# [outdir] 输出目录,默认当前目录
#
# 示例:
# fetch-brand-icon.sh claude color svg ./assets → ./assets/claude-color.svg
# fetch-brand-icon.sh openai mono svg . → ./openai.svg (fill=currentColor,可 CSS 重新着色)
# fetch-brand-icon.sh gemini color png-dark ./assets → ./assets/gemini-color.png
#
# 注意: 不是每个 id 都有全部变体(如 openai 没有 -color,anthropic 只有 mono + text)。
# 命中 404 会自动回退到 mono;全部失败则报错退出。
set -euo pipefail
ID="${1:?need icon id, e.g. claude}"
VARIANT="${2:-mono}"
FORMAT="${3:-svg}"
OUTDIR="${4:-.}"
VER="${LOBE_ICONS_VERSION:-latest}" # 可 export LOBE_ICONS_VERSION=1.91.0 锁版本
mkdir -p "$OUTDIR"
# variant → 文件名后缀
suffix=""
case "$VARIANT" in
mono|"") suffix="" ;;
color) suffix="-color" ;;
text) suffix="-text" ;;
brand) suffix="-brand" ;;
brand-color) suffix="-brand-color" ;;
*) echo "unknown variant: $VARIANT (use mono|color|text|brand|brand-color)" >&2; exit 2 ;;
esac
# format → 包 + 路径前缀 + 扩展名
case "$FORMAT" in
svg) pkg="icons-static-svg"; prefix="icons"; ext="svg" ;;
png-light)pkg="icons-static-png"; prefix="light"; ext="png" ;;
png-dark) pkg="icons-static-png"; prefix="dark"; ext="png" ;;
*) echo "unknown format: $FORMAT (use svg|png-light|png-dark)" >&2; exit 2 ;;
esac
fname="${ID}${suffix}.${ext}"
out="$OUTDIR/$fname"
# CDN 回退链: unpkg → jsdelivr → npmmirror(阿里云)
urls=(
"https://unpkg.com/@lobehub/${pkg}@${VER}/${prefix}/${fname}"
"https://cdn.jsdelivr.net/npm/@lobehub/${pkg}@${VER}/${prefix}/${fname}"
"https://registry.npmmirror.com/@lobehub/${pkg}/${VER}/files/${prefix}/${fname}"
)
try_download() {
local url="$1" dest="$2"
local code
code=$(curl -sL -A "Mozilla/5.0" -o "$dest" -w "%{http_code}" "$url" || echo 000)
# 404 / 错误响应常返回 200 + 一个 JSON 或纯文本错误体(如 npmmirror 的 {"error":"[NOT_FOUND]"})。
# 按大小 + 内容校验;任何不合格的下载都删掉写坏的文件,绝不把错误响应体留在磁盘当 logo。
if [ "$code" = "200" ] && [ -s "$dest" ]; then
if [ "$ext" = "svg" ] && ! grep -q "<svg" "$dest"; then rm -f "$dest"; return 1; fi
return 0
fi
rm -f "$dest"
return 1
}
for url in "${urls[@]}"; do
if try_download "$url" "$out"; then
echo "OK $out ($(wc -c < "$out" | tr -d ' ') bytes) <- $url"
exit 0
fi
done
# 全失败:若不是 mono,回退到 mono 再试一遍
if [ "$VARIANT" != "mono" ]; then
echo "warn: ${VARIANT} not available for '${ID}', falling back to mono" >&2
exec "$0" "$ID" mono "$FORMAT" "$OUTDIR"
fi
echo "FAIL could not fetch '${ID}' (${VARIANT}/${FORMAT}) from any CDN" >&2
rm -f "$out"
exit 1
#!/usr/bin/env bash
# render-cover.sh — 把定制好的封面 HTML 渲染成多比例 2x PNG,并各生成一张可 Read 的小图预览。
#
# 这是 headless Chrome 回退管线的「打包版」,把本仓库遇到过的问题都内置了:
# - 用字面 ratio 列表,不靠 shell 变量分词(zsh 不会对 $VAR 分词,老 bug)
# - 输出到调用方给的持久目录,绝不用 /tmp(会被清)
# - 渲染与降采样分两段:先确认 PNG 落地(有界轮询,因为 headless Chrome 常在
# 截图落盘前就让 wait 返回),再 sips 缩图
# - 每个渲染用独立 --user-data-dir,跑完 pkill 收尾,避免 SingletonLock / 不退出
# - 预览图缩到 <2000px,方便 Read 工具直接看(验收用)
#
# 用法:
# render-cover.sh <html> <outdir> [ratios...]
# <html> 定制好的封面 HTML(占位符已替换)
# <outdir> 输出目录(持久路径,如 ~/covers/<slug>)
# [ratios] 要出的比例,缺省按 HTML 朝向自动选:
# 横屏 (--cv-w:1920) → 16x9 16x10
# 竖屏 (--cv-w:1080) → 9x16 3x4
# 可选值: 16x9 16x10 9x16 3x4
#
# 例:
# render-cover.sh cover-h.html ~/covers/demo # 横屏自动出 16x9 + 16x10
# render-cover.sh cover-v.html ~/covers/demo 9x16 # 只出 9:16
# render-cover.sh cover-h.html ~/covers/demo 16x9 16x10 # 显式
#
# 产物(outdir 内):
# cover-<ratio>.png 2x retina 成品(如 16x9 → 3840×2160)
# cover-<ratio>.preview.png 缩到 ≤1400px 的预览(给 Read 看,验收后可删)
set -uo pipefail # 注意:不开 -e,渲染/缩图的零星非零退出码不应中断整批
HTML="${1:?need html path}"
OUTDIR="${2:?need output dir (use a persistent path, NOT /tmp)}"
shift 2
RATIOS=("$@")
CHROME="${CHROME:-/Applications/Google Chrome.app/Contents/MacOS/Google Chrome}"
[ -f "$HTML" ] || { echo "no such html: $HTML" >&2; exit 1; }
[ -x "$CHROME" ] || { echo "Chrome not found at: $CHROME (set \$CHROME)" >&2; exit 1; }
mkdir -p "$OUTDIR"
# 绝对化 OUTDIR:下面用 "file://$src"(src 在 OUTDIR 内)拼截图 URL,
# 若 OUTDIR 是相对路径会拼成非法的 file://covers/...(缺主机/根斜杠),Chrome 渲染出错误页。踩过的坑。
OUTDIR="$(cd "$OUTDIR" && pwd)"
# ratio → "cv-h 画布宽 画布高 预览长边"
dims() {
case "$1" in
16x9) echo "1080 1920 1080 1400" ;;
16x10) echo "1200 1920 1200 1400" ;;
9x16) echo "1920 1080 1920 760" ;;
3x4) echo "1440 1080 1440 760" ;;
*) echo "" ;;
esac
}
# 朝向:横屏 cv-w 1920 / 竖屏 1080;未显式给比例时按朝向默认
cvw=$(grep -oE -- '--cv-w: *[0-9]+px' "$HTML" | grep -oE '[0-9]+' | head -1)
[ -z "$cvw" ] && cvw=1920
if [ "${#RATIOS[@]}" -eq 0 ]; then
if [ "$cvw" = "1080" ]; then RATIOS=(9x16 3x4); else RATIOS=(16x9 16x10); fi
fi
# 先校验比例合法,过滤掉非法值(避免后面空迭代误计数)
VALID=()
for r in "${RATIOS[@]}"; do
if [ -n "$(dims "$r")" ]; then VALID+=("$r"); else
echo "skip unknown ratio: $r (use 16x9|16x10|9x16|3x4)" >&2
fi
done
[ "${#VALID[@]}" -eq 0 ] && { echo "no valid ratios" >&2; exit 1; }
pkill -9 -f "Google Chrome.*--headless" 2>/dev/null || true
sleep 1
# 1) 渲染阶段:每个比例并行,独立 profile
PIDS=()
for r in "${VALID[@]}"; do
read -r cvh W H _ <<< "$(dims "$r")"
src="$OUTDIR/.render-$r.html"
sed -E "s/--cv-h: *[0-9]+px/--cv-h:${cvh}px/" "$HTML" > "$src"
"$CHROME" --headless=new --disable-gpu --force-device-scale-factor=2 --hide-scrollbars \
--default-background-color=00000000 --user-data-dir="$OUTDIR/.prof-$r" \
--window-size="$W,$H" --virtual-time-budget=8000 \
--screenshot="$OUTDIR/cover-$r.png" "file://$src" >/dev/null 2>&1 &
PIDS+=($!)
done
# 2) 等所有 PNG 落地 —— 有界轮询。
# 关键:不要 `wait` 这些 Chrome 进程。--headless=new 截图后常不自退(render-pipeline.md 记录的坑),
# `wait` 会永久阻塞,后面的收尾就永远到不了。改为只等文件落地,再主动收掉进程。
deadline=$((SECONDS + 40))
while [ "$SECONDS" -lt "$deadline" ]; do
missing=0
for r in "${VALID[@]}"; do [ -s "$OUTDIR/cover-$r.png" ] || missing=1; done
[ "$missing" -eq 0 ] && break
sleep 1
done
# 主动收掉本次起的 Chrome:先按 PID,再按本次 OUTDIR 的 profile 目录精确收掉残留
#(用 profile 路径匹配,不会误杀别处正在跑的其它 headless Chrome 实例)
for pid in "${PIDS[@]}"; do kill "$pid" 2>/dev/null || true; done
sleep 1
pkill -9 -f "user-data-dir=$OUTDIR/.prof-" 2>/dev/null || true
for pid in "${PIDS[@]}"; do kill -9 "$pid" 2>/dev/null || true; done
# 3) 降采样 + 计数(此时文件已落地)
ok=0; fail=0
for r in "${VALID[@]}"; do
read -r _ _ _ plong <<< "$(dims "$r")"
png="$OUTDIR/cover-$r.png"
if [ -s "$png" ]; then
sips -Z "$plong" "$png" --out "$OUTDIR/cover-$r.preview.png" >/dev/null 2>&1
d=$(sips -g pixelWidth -g pixelHeight "$png" 2>/dev/null | awk '/pixel/{printf "%s ",$2}')
echo "OK cover-$r.png [${d}] + preview"
ok=$((ok + 1))
else
echo "FAIL cover-$r.png not produced"
fail=$((fail + 1))
fi
done
# 4) 清理中间文件
rm -f "$OUTDIR"/.render-*.html 2>/dev/null || true
rm -rf "$OUTDIR"/.prof-* 2>/dev/null || true
echo "done: $ok ok, $fail fail → $OUTDIR"
[ "$fail" -eq 0 ]
<!--
Template: aurora-vertical (竖屏族 / vertical family)
风格: 极光渐变 - 深底 + 多彩光晕
支持比例: 9:16 (1080×1920, 默认) / 3:4 (1080×1440, 把 --cv-h 改 1440px)
适配: 抖音 / 视频号 ; 文字收在中央安全带, body.show-guide 看显示带
identity test 同 aurora.html
身份装饰(必留): 深底 + 极光多彩光晕 (.cover::before) + 细网格层 (.cover::after) + 通透细字 = aurora 身份,勿删。
默认精简版 (element budget ≤ 5,见 SKILL.md 原则 1):
出厂只放 4 个元素 - kicker(.top 左) + 主标题(.title) + 副标(.sub) + 一处署名(.foot)。
页眉 index 编号 / eyebrow 小字 默认不放;
确有信息价值时,从 body 末尾「可选 opt-in」注释取用(CSS 已保留)。
Customization points (替换占位符,改 CSS 变量):
1. CSS :root - 改 --a1/--a2/--a3 调极光三色,--bg 调底色;改 --cv-h 切 9:16 / 3:4
2. .kicker {{KICKER}} - 页眉 kicker (品类 / 工具)
3. .title - 主标题,两行 <span class="stack">,第二行 .alt 走极光渐变
4. .sub - 中文副标题,重点词加 <span class="mark">
5. .foot .name / channel - 作者署名 + 频道 (全图只此一处署名)
可选 (默认不放): .index(页眉编号) / .eyebrow(标题上方小字)
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" /><meta name="color-scheme" content="dark">
<title>{{TITLE}} · Cover</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{
--bg:#05060a;--ink:#f3f5fa;--ink-soft:#aab3c5;--muted:#6b7488;
--a1:#5b8cff;--a2:#9b5bff;--a3:#33e0d0;--accent:#8aa0ff;
--font-display:'Inter','Noto Sans SC',sans-serif;--font-zh:'Noto Sans SC',sans-serif;--font-mono:'JetBrains Mono',monospace;
--cv-w:1080px; --cv-h:1920px;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{background:var(--bg);color:var(--ink);font-family:var(--font-display);-webkit-font-smoothing:antialiased;}
.cover{width:var(--cv-w);height:var(--cv-h);position:relative;background:var(--bg);overflow:hidden;}
.cover::before{content:"";position:absolute;inset:0;background:
radial-gradient(45% 30% at 20% 22%, rgba(91,140,255,0.55), transparent 60%),
radial-gradient(45% 28% at 84% 38%, rgba(155,91,255,0.45), transparent 60%),
radial-gradient(55% 32% at 55% 82%, rgba(51,224,208,0.32), transparent 60%);
filter:blur(8px);pointer-events:none;}
.cover::after{content:"";position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.04) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.04) 1px,transparent 1px);background-size:80px 80px;mask-image:radial-gradient(ellipse 80% 70% at 50% 50%,#000 40%,transparent 78%);pointer-events:none;}
.content{position:relative;z-index:5;padding:170px 76px;height:100%;display:flex;flex-direction:column;}
.top{display:flex;justify-content:space-between;align-items:center;}
.kicker{font-family:var(--font-mono);font-size:24px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-soft);}
.index{font-family:var(--font-mono);font-size:22px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);}
.hero{flex:1;display:flex;flex-direction:column;justify-content:center;}
.eyebrow{font-family:var(--font-mono);font-size:25px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent);margin-bottom:28px;}
.title{font-weight:500;font-size:212px;line-height: 1.06;letter-spacing:-.035em;text-transform:lowercase;color:var(--ink);}
.title .stack{display:block;}
.title .alt{background:linear-gradient(110deg,var(--a1),var(--a2) 45%,var(--a3));-webkit-background-clip:text;background-clip:text;color:transparent;}
.sub{font-family:var(--font-zh);font-weight:500;font-size:62px;line-height:1.18;margin-top:36px;color:var(--ink-soft);}
.sub .mark{color:var(--ink);font-weight:700;}
.foot{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:24px;letter-spacing:.08em;color:var(--muted);}
.foot .name{font-family:var(--font-zh);color:var(--ink);font-weight:500;}
.guide{display:none;position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:1464px;border:2px dashed var(--a3);z-index:60;pointer-events:none;}
body.show-guide .guide{display:block;}
</style>
</head>
<body>
<div class="cover">
<div class="guide"></div>
<div class="content">
<div class="top"><div class="kicker">{{KICKER}}</div></div>
<div class="hero">
<h1 class="title"><span class="stack">{{TITLE_LINE_1}}</span><span class="stack alt">{{TITLE_LINE_2}}</span></h1>
<div class="sub">{{SUBTITLE_HTML}}</div>
</div>
<div class="foot"><span class="name">{{AUTHOR_NAME}}</span><span>{{CHANNEL}}</span></div>
<!-- 可选 opt-in (默认精简不放;仅当承载信息时取用,CSS 已保留):
页眉编号 → .top 内 .kicker 后加: <div class="index">{{INDEX}}</div>
标题上方小字 → .hero 内 <h1> 前加: <div class="eyebrow">{{EYEBROW}}</div>
-->
</div>
</div>
</body>
</html>
<!--
Template: aurora (横屏族 / horizontal family)
风格: 极光渐变 - 深底 + 多彩光晕 (Vercel / Linear / OpenAI 那种 AI/SaaS 现代审美)
支持比例: 16:9 (1920×1080, 默认) / 16:10 (1920×1200, 把 --cv-h 改 1200px)
适配: YouTube / Bilibili / 横屏短视频 ; 竖屏用 aurora-vertical.html
identity test: ①深色底 (近黑) ②2-3 团彩色径向光晕 (蓝/紫/青) 柔和叠加 ③大字偏细 (weight ≤500) ④无硬边框/无硬阴影, 整体通透
身份装饰(必留): 深底 + 极光多彩光晕 (.cover::before) + 细网格层 (.cover::after) + 通透细字 = aurora 身份,勿删。
默认精简版 (element budget ≤ 5,见 SKILL.md 原则 1):
出厂只放 4 个元素 - kicker(.top 左) + 主标题(.title) + 副标(.sub) + 一处署名(.foot .name)。
页眉 index 编号 / eyebrow 小字 / 页脚日期 默认不放;
确有信息价值时,从下面 body 末尾的「可选 opt-in」注释里取用(CSS 已保留)。
Customization points (替换占位符,改 CSS 变量):
1. CSS :root - 改 --a1/--a2/--a3 调极光三色,--bg 调底色
2. .kicker {{KICKER}} - 页眉 kicker (品类 / 工具),可在词间加 <span class="dot">·</span>
3. .title - 主标题,两行 <span class="stack">,第二行 .alt 走极光渐变
4. .sub - 中文副标题,重点词加 <span class="mark">
5. .foot .name - 作者署名 · 频道 (全图只此一处署名)
可选 (默认不放): .index(页眉编号) / .eyebrow(标题上方小字) / 页脚日期
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" /><meta name="color-scheme" content="dark">
<title>{{TITLE}} · Cover</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{
--bg:#05060a;--ink:#f3f5fa;--ink-soft:#aab3c5;--muted:#6b7488;
--a1:#5b8cff;--a2:#9b5bff;--a3:#33e0d0; /* 极光三色 */
--accent:#8aa0ff;
--font-display:'Inter','Noto Sans SC',sans-serif;--font-zh:'Noto Sans SC',sans-serif;--font-mono:'JetBrains Mono',monospace;
--cv-w:1920px; --cv-h:1080px;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{background:var(--bg);color:var(--ink);font-family:var(--font-display);-webkit-font-smoothing:antialiased;}
.cover{width:var(--cv-w);height:var(--cv-h);position:relative;background:var(--bg);overflow:hidden;}
/* 极光光晕层 */
.cover::before{content:"";position:absolute;inset:0;background:
radial-gradient(50% 60% at 18% 22%, rgba(91,140,255,0.55), transparent 60%),
radial-gradient(45% 55% at 82% 30%, rgba(155,91,255,0.45), transparent 60%),
radial-gradient(55% 60% at 60% 95%, rgba(51,224,208,0.32), transparent 60%);
filter:blur(8px);pointer-events:none;}
/* 细微网格提精致感 */
.cover::after{content:"";position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.04) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.04) 1px,transparent 1px);background-size:80px 80px;mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,#000 40%,transparent 75%);pointer-events:none;}
.content{position:relative;z-index:5;padding:88px 104px;height:100%;display:flex;flex-direction:column;}
.top{display:flex;justify-content:space-between;align-items:center;}
.kicker{font-family:var(--font-mono);font-size:24px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-soft);}
.kicker .dot{color:var(--a3);}
.index{font-family:var(--font-mono);font-size:22px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);}
.hero{flex:1;display:flex;flex-direction:column;justify-content:center;max-width:84%;}
.eyebrow{font-family:var(--font-mono);font-size:26px;letter-spacing:.2em;text-transform:uppercase;color:var(--accent);margin-bottom:30px;}
.title{font-weight:500;font-size:264px;line-height: 1.06;letter-spacing:-.035em;text-transform:lowercase;color:var(--ink);}
.title .stack{display:block;}
.title .alt{background:linear-gradient(110deg,var(--a1),var(--a2) 45%,var(--a3));-webkit-background-clip:text;background-clip:text;color:transparent;}
.sub{font-family:var(--font-zh);font-weight:500;font-size:62px;line-height:1.15;margin-top:38px;color:var(--ink-soft);}
.sub .mark{color:var(--ink);font-weight:700;}
.foot{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:24px;letter-spacing:.1em;color:var(--muted);}
.foot .name{font-family:var(--font-zh);color:var(--ink);font-weight:500;}
</style>
</head>
<body>
<div class="cover">
<div class="content">
<div class="top"><div class="kicker">{{KICKER}}</div></div>
<div class="hero">
<h1 class="title"><span class="stack">{{TITLE_LINE_1}}</span><span class="stack alt">{{TITLE_LINE_2}}</span></h1>
<div class="sub">{{SUBTITLE_HTML}}</div>
</div>
<div class="foot"><span class="name">{{AUTHOR_NAME}} · {{CHANNEL}}</span></div>
<!-- 可选 opt-in (默认精简不放;仅当承载信息时取用,CSS 已保留):
页眉编号 → .top 内 .kicker 后加: <div class="index">{{INDEX}}</div>
标题上方小字 → .hero 内 <h1> 前加: <div class="eyebrow">{{EYEBROW}}</div>
页脚日期 → .foot 内 .name 后加: <span>{{DATE}}</span>
-->
</div>
</div>
</body>
</html>
<!--
Template: bauhaus-vertical (竖屏族 / vertical family)
风格: 包豪斯 - 米白底 + 红黄蓝色块 + 圆/三角/方几何 + 粗网格 + 几何无衬线
支持比例: 9:16 (1080×1920, 默认) / 3:4 (1080×1440, 把 --cv-h 改 1440px)
适配: 抖音 / 视频号 ; 文字收在中央安全带, body.show-guide 看显示带
identity test 同 bauhaus.html
默认精简版 (element budget ≤ 5,见 SKILL.md 原则 1):
出厂只放 4 个元素 - kicker(.kicker) + 主标题(.title) + 副标(.sub) + 一处署名(.foot 作者+频道)。
红黄蓝几何色块(.geo) + 粗网格细线 = 包豪斯身份,保留(不是可删的装饰色块)。
右上编号(.index) / eyebrow 默认不放;
确有信息价值时从 body 末尾「可选 opt-in」注释取用(CSS 已保留)。
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" /><meta name="color-scheme" content="light">
<title>{{TITLE}} · Cover</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700;800&family=Noto+Sans+SC:wght@500;700;900&display=swap" rel="stylesheet">
<style>
:root{
--bg:#f2ece1;--ink:#1a1a1a;--red:#e63329;--yellow:#f7c52b;--blue:#1f4ed8;
--font-display:'Poppins','Noto Sans SC',sans-serif;--font-zh:'Noto Sans SC',sans-serif;
--cv-w:1080px; --cv-h:1920px;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{background:var(--bg);color:var(--ink);font-family:var(--font-display);-webkit-font-smoothing:antialiased;}
.cover{width:var(--cv-w);height:var(--cv-h);position:relative;background:var(--bg);overflow:hidden;}
.geo{position:absolute;z-index:1;}
.g-circle{top:80px;right:-120px;width:420px;height:420px;border-radius:50%;background:var(--blue);}
.g-tri{top:60px;left:0;width:0;height:0;border-right:240px solid transparent;border-top:220px solid var(--yellow);}
.g-bar{bottom:260px;left:0;width:100%;height:30px;background:var(--ink);}
.g-sq{bottom:120px;right:72px;width:200px;height:200px;background:var(--red);}
.content{position:relative;z-index:5;padding:170px 72px;height:100%;display:flex;flex-direction:column;}
.top{display:flex;justify-content:space-between;align-items:center;border-bottom:4px solid var(--ink);padding-bottom:20px;}
.kicker{font-weight:700;font-size:26px;letter-spacing:.1em;text-transform:uppercase;}
.kicker .sq{display:inline-block;width:18px;height:18px;background:var(--red);margin-right:12px;vertical-align:middle;}
.index{font-weight:600;font-size:22px;letter-spacing:.08em;text-transform:uppercase;}
.hero{flex:1;display:flex;flex-direction:column;justify-content:center;}
.eyebrow{font-weight:600;font-size:26px;letter-spacing:.12em;text-transform:uppercase;color:var(--blue);margin-bottom:26px;}
.title{font-weight:800;font-size:206px;line-height: 1.1;letter-spacing:-.03em;text-transform:lowercase;}
.title .stack{display:block;}
.title .alt{color:var(--red);}
.sub{font-family:var(--font-zh);font-weight:700;font-size:62px;line-height:1.14;margin-top:34px;}
.sub .mark{color:var(--bg);background:var(--blue);padding:0 .14em;}
.foot{display:flex;justify-content:space-between;align-items:center;border-top:4px solid var(--ink);padding-top:20px;font-weight:600;font-size:24px;letter-spacing:.05em;}
.foot .name{font-family:var(--font-zh);font-weight:700;}
.guide{display:none;position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:1464px;border:3px dashed var(--ink);z-index:60;pointer-events:none;}
body.show-guide .guide{display:block;}
</style>
</head>
<body>
<div class="cover">
<div class="geo g-circle"></div>
<div class="geo g-tri"></div>
<div class="geo g-bar"></div>
<div class="geo g-sq"></div>
<div class="guide"></div>
<div class="content">
<div class="top"><div class="kicker"><span class="sq"></span>{{KICKER}}</div></div>
<div class="hero">
<h1 class="title"><span class="stack">{{TITLE_LINE_1}}</span><span class="stack alt">{{TITLE_LINE_2}}</span></h1>
<div class="sub">{{SUBTITLE_HTML}}</div>
</div>
<div class="foot"><span class="name">{{AUTHOR_NAME}}</span><span>{{CHANNEL}}</span></div>
</div>
<!-- 可选 opt-in (默认精简不放;仅当承载信息时取用,CSS 已保留):
右上编号 → .top 内 .kicker 后加: <div class="index">{{INDEX}}</div>
标题上方小字 → .hero 内 <h1> 前加: <div class="eyebrow">{{EYEBROW}}</div>
-->
</div>
</body>
</html>
<!--
Template: bauhaus (横屏族 / horizontal family)
风格: 包豪斯 - 米白底 + 三原色(红黄蓝)色块 + 圆/三角/方几何构成 + 粗网格 + 大写几何无衬线
支持比例: 16:9 (1920×1080, 默认) / 16:10 (1920×1200, 把 --cv-h 改 1200px)
适配: YouTube / Bilibili / 横屏短视频 ; 竖屏用 bauhaus-vertical.html
identity test: ①只用红黄蓝+黑(不加其他色相) ②画面有圆/三角/矩形几何元素参与构图 ③几何无衬线字体 ④构图基于网格
默认精简版 (element budget ≤ 5,见 SKILL.md 原则 1):
出厂只放 4 个元素 - kicker(.kicker) + 主标题(.title) + 副标(.sub) + 一处署名(.foot 作者·频道)。
红黄蓝几何色块(.geo) + 粗网格细线 = 包豪斯身份,保留(不是可删的装饰色块)。
右上编号(.index) / eyebrow / 底部日期 默认不放;
确有信息价值时从 body 末尾「可选 opt-in」注释取用(CSS 已保留)。
占位符 (默认): {{KICKER}} {{TITLE_LINE_1}} {{TITLE_LINE_2}} {{SUBTITLE_HTML}} {{AUTHOR_NAME}} {{CHANNEL}}
可选占位符: {{INDEX}} {{EYEBROW}} {{DATE}}
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" /><meta name="color-scheme" content="light">
<title>{{TITLE}} · Cover</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700;800&family=Noto+Sans+SC:wght@500;700;900&display=swap" rel="stylesheet">
<style>
:root{
--bg:#f2ece1;--ink:#1a1a1a;--red:#e63329;--yellow:#f7c52b;--blue:#1f4ed8;
--font-display:'Poppins','Noto Sans SC',sans-serif;--font-zh:'Noto Sans SC',sans-serif;
--cv-w:1920px; --cv-h:1080px;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{background:var(--bg);color:var(--ink);font-family:var(--font-display);-webkit-font-smoothing:antialiased;}
.cover{width:var(--cv-w);height:var(--cv-h);position:relative;background:var(--bg);overflow:hidden;}
/* 几何构成 - 参与构图的圆/三角/方 */
.geo{position:absolute;z-index:1;}
.g-circle{top:0;right:0;width:560px;height:560px;border-radius:50%;background:var(--blue);}
.g-quarter{top:0;right:0;width:280px;height:280px;background:var(--yellow);border-bottom-left-radius:280px;}
.g-tri{bottom:0;left:0;width:0;height:0;border-left:360px solid transparent;border-bottom:300px solid var(--red);}
.g-bar{bottom:120px;right:0;width:760px;height:36px;background:var(--ink);}
.content{position:relative;z-index:5;padding:96px 112px;height:100%;display:flex;flex-direction:column;}
.top{display:flex;justify-content:space-between;align-items:center;border-bottom:4px solid var(--ink);padding-bottom:24px;}
.kicker{font-weight:700;font-size:28px;letter-spacing:.12em;text-transform:uppercase;}
.kicker .sq{display:inline-block;width:22px;height:22px;background:var(--red);margin-right:14px;vertical-align:middle;}
.index{font-weight:600;font-size:24px;letter-spacing:.1em;text-transform:uppercase;}
.hero{flex:1;display:flex;flex-direction:column;justify-content:center;max-width:72%;}
.eyebrow{font-weight:600;font-size:28px;letter-spacing:.14em;text-transform:uppercase;color:var(--blue);margin-bottom:28px;}
.title{font-weight:800;font-size:252px;line-height: 1.1;letter-spacing:-.03em;text-transform:lowercase;color:var(--ink);}
.title .stack{display:block;}
.title .alt{color:var(--red);}
.sub{font-family:var(--font-zh);font-weight:700;font-size:60px;line-height:1.12;margin-top:36px;color:var(--ink);}
.sub .mark{color:var(--bg);background:var(--blue);padding:0 .14em;}
.foot{display:flex;justify-content:space-between;align-items:center;border-top:4px solid var(--ink);padding-top:24px;font-weight:600;font-size:24px;letter-spacing:.06em;}
.foot .name{font-family:var(--font-zh);font-weight:700;}
</style>
</head>
<body>
<div class="cover">
<div class="geo g-circle"></div>
<div class="geo g-quarter"></div>
<div class="geo g-tri"></div>
<div class="geo g-bar"></div>
<div class="content">
<div class="top"><div class="kicker"><span class="sq"></span>{{KICKER}}</div></div>
<div class="hero">
<h1 class="title"><span class="stack">{{TITLE_LINE_1}}</span><span class="stack alt">{{TITLE_LINE_2}}</span></h1>
<div class="sub">{{SUBTITLE_HTML}}</div>
</div>
<div class="foot"><span class="name">{{AUTHOR_NAME}} · {{CHANNEL}}</span></div>
</div>
<!-- 可选 opt-in (默认精简不放;仅当承载信息时取用,CSS 已保留):
右上编号 → .top 内 .kicker 后加: <div class="index">{{INDEX}}</div>
标题上方小字 → .hero 内 <h1> 前加: <div class="eyebrow">{{EYEBROW}}</div>
底部日期 → .foot 内末尾加: <span>{{DATE}}</span>
-->
</div>
</body>
</html>
<!--
Template: blueprint-vertical (竖屏族 / vertical family)
风格: 深蓝图 - 深藏青底 + 白色网格/线稿 + 等宽标注
支持比例: 9:16 (1080×1920, 默认) / 3:4 (1080×1440, 把 --cv-h 改 1440px)
适配: 抖音 / 视频号 竖屏短视频 ; 横屏用 blueprint.html
文字收在中央安全带 (加 body.show-guide 看辅助线)
identity test 同 blueprint.html
默认精简版 (element budget ≤ 5,见 SKILL.md 原则 1):
出厂只放 5 个元素 - kicker(.top span) + 主标题(.title) + 副标(.sub) + 一条蓝图标注线(.dimline) + 一处署名(.foot .name)。
网格线稿 + dimline 尺寸标注是蓝图身份语言,保留(网格不是可删的分隔线)。
eyebrow / 右上 REV index / dimline 里的 date / 右下 DWG·index(第二处品牌标) 默认不放;
确有信息价值时从 body 末尾「可选 opt-in」注释取用(CSS 已保留)。
Customization points: .top span(kicker) · .title(两行,第二行可 .alt) · .sub · .dimline · .foot .name(全图唯一署名)
可选 (默认不放): .eyebrow(标题上方小字) / 右上 REV index / dimline date / 右下 DWG·index
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" /><meta name="color-scheme" content="dark">
<title>{{TITLE}} · Cover</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&family=Noto+Sans+SC:wght@500;700;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#0a1f3c;--bg2:#0c2546;--ink:#eaf2ff;--ink-soft:#a9c2e6;--muted:#5f7aa6;
--line:rgba(160,200,255,.16);--line-strong:rgba(160,200,255,.4);--accent:#7fd3ff;
--font-display:'Inter','Noto Sans SC',sans-serif;--font-zh:'Noto Sans SC',sans-serif;--font-mono:'JetBrains Mono',monospace;
--cv-w:1080px; --cv-h:1920px;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{background:var(--bg);color:var(--ink);font-family:var(--font-display);-webkit-font-smoothing:antialiased;}
.cover{width:var(--cv-w);height:var(--cv-h);position:relative;overflow:hidden;background:linear-gradient(160deg,var(--bg2),var(--bg));}
.cover::before{content:"";position:absolute;inset:0;pointer-events:none;
background-image:
linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px),
linear-gradient(var(--line-strong) 1.5px,transparent 1.5px),linear-gradient(90deg,var(--line-strong) 1.5px,transparent 1.5px);
background-size:48px 48px,48px 48px,240px 240px,240px 240px;}
/* 中央压暗,把网格从标题区域擦掉,托住文字 */
.cover::after{content:"";position:absolute;inset:0;pointer-events:none;
background:radial-gradient(70% 42% at 50% 50%, rgba(7,20,40,.92) 0%, rgba(7,20,40,.72) 48%, transparent 80%);}
.content{position:relative;z-index:5;padding:90px 76px;height:100%;display:flex;flex-direction:column;justify-content:center;}
.top{position:absolute;top:90px;left:76px;right:76px;display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:25px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent);}
.eyebrow{font-family:var(--font-mono);font-size:26px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-soft);margin-bottom:26px;}
.eyebrow::before{content:"⌖ ";color:var(--accent);}
.title{font-weight:800;font-size:252px;line-height: 1.1;letter-spacing:-.02em;color:var(--ink);}
.title .stack{display:block;}
.title .alt{color:var(--accent);}
.sub{font-family:var(--font-zh);font-weight:500;font-size:52px;line-height:1.25;margin-top:34px;color:var(--ink-soft);}
.dimline{display:flex;align-items:center;gap:14px;margin-top:28px;color:var(--muted);font-family:var(--font-mono);font-size:24px;letter-spacing:.1em;}
.dimline .bar{flex:1;height:1px;background:var(--line-strong);position:relative;}
.dimline .bar::before,.dimline .bar::after{content:"";position:absolute;top:-5px;width:1px;height:11px;background:var(--line-strong);}
.dimline .bar::before{left:0;}.dimline .bar::after{right:0;}
.foot{position:absolute;bottom:90px;left:76px;right:76px;display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:25px;letter-spacing:.1em;color:var(--muted);}
.foot .name{font-family:var(--font-zh);color:var(--ink);font-weight:500;}
.guide{position:absolute;left:40px;right:40px;top:228px;bottom:228px;border:2px dashed rgba(255,120,120,.7);display:none;z-index:9;}
body.show-guide .guide{display:block;}
</style>
</head>
<body>
<div class="cover">
<div class="top"><span>{{KICKER}}</span></div>
<div class="content">
<h1 class="title"><span class="stack">{{TITLE_LINE_1}}</span><span class="stack alt">{{TITLE_LINE_2}}</span></h1>
<div class="sub">{{SUBTITLE_HTML}}</div>
<div class="dimline"><span>SCALE 1:1</span><span class="bar"></span><span>DWG</span></div>
</div>
<div class="foot"><span class="name">{{AUTHOR_NAME}} · {{CHANNEL}}</span></div>
<div class="guide"></div>
<!-- 可选 opt-in (默认不放;承载信息时取用,CSS 已保留):
标题上方小字 → .content 内 <h1> 前加: <div class="eyebrow">{{EYEBROW}}</div>
右上 REV index → .top 内 kicker span 后加: <span>REV {{INDEX}}</span>
dimline date → .dimline 末尾 <span>DWG</span> 换成 <span>{{DATE}}</span>
右下 DWG·index → .foot 内 .name 后加: <span>DWG · {{INDEX}}</span>
-->
</div>
</body>
</html>
<!--
Template: blueprint (横屏族 / horizontal family)
风格: 深蓝图 - 深藏青底 + 白色网格/线稿 + 等宽标注 (工程蓝图气质)
支持比例: 16:9 (1920×1080, 默认) / 16:10 (1920×1200, 把 --cv-h 改 1200px)
适配: YouTube / Bilibili / 横屏短视频 ; 竖屏用 blueprint-vertical.html
identity test: ①深藏青/蓝图蓝底 ②白色细网格 (细线 + 主线两层) ③有线稿装饰 (角标尺/标注线/十字) ④等宽标注字, 标题描边或半透白
默认精简版 (element budget ≤ 5,见 SKILL.md 原则 1):
出厂只放 5 个元素 - kicker(.top span) + 主标题(.title) + 副标(.sub) + 一条蓝图标注线(.dimline,领域装置) + 一处署名(.foot .name)。
网格线稿 + dimline 尺寸标注是蓝图身份语言,保留(网格不是可删的分隔线)。
eyebrow / 右上 REV index / dimline 里的 date / 右下 DWG·index(第二处品牌标) 默认不放;
确有信息价值时从 body 末尾「可选 opt-in」注释取用(CSS 已保留)。
Customization points (替换占位符):
1. .top span (kicker) - 顶部 kicker (品类 / 工具)
2. .title - 主标题,两行 <span class="stack">,第二行可加 .alt 高亮
3. .sub - 中文副标题
4. .foot .name - 作者署名 + 频道名 (全图只此一处署名)
可选 (默认不放): .eyebrow(标题上方小字) / 右上 REV index / dimline date / 右下 DWG·index
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" /><meta name="color-scheme" content="dark">
<title>{{TITLE}} · Cover</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&family=Noto+Sans+SC:wght@500;700;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#0a1f3c;--bg2:#0c2546;--ink:#eaf2ff;--ink-soft:#a9c2e6;--muted:#5f7aa6;
--line:rgba(160,200,255,.16);--line-strong:rgba(160,200,255,.4);
--accent:#7fd3ff;
--font-display:'Inter','Noto Sans SC',sans-serif;--font-zh:'Noto Sans SC',sans-serif;--font-mono:'JetBrains Mono',monospace;
--cv-w:1920px; --cv-h:1080px;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{background:var(--bg);color:var(--ink);font-family:var(--font-display);-webkit-font-smoothing:antialiased;}
.cover{width:var(--cv-w);height:var(--cv-h);position:relative;background:linear-gradient(135deg,var(--bg2),var(--bg));overflow:hidden;}
/* 细网格 + 主网格 */
.cover::before{content:"";position:absolute;inset:0;pointer-events:none;
background-image:
linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px),
linear-gradient(var(--line-strong) 1.5px,transparent 1.5px),linear-gradient(90deg,var(--line-strong) 1.5px,transparent 1.5px);
background-size:48px 48px,48px 48px,240px 240px,240px 240px;}
/* 左侧压暗,把网格从标题区域擦掉,托住文字 */
.cover::after{content:"";position:absolute;inset:0;pointer-events:none;
background:linear-gradient(90deg, rgba(7,20,40,.92) 0%, rgba(7,20,40,.74) 42%, rgba(7,20,40,.15) 72%, transparent 88%);}
.content{position:relative;z-index:5;padding:80px 96px;height:100%;display:flex;flex-direction:column;justify-content:space-between;}
.top{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:23px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent);}
.hero{flex:1;display:flex;flex-direction:column;justify-content:center;max-width:86%;}
.eyebrow{font-family:var(--font-mono);font-size:24px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-soft);margin-bottom:24px;}
.eyebrow::before{content:"⌖ ";color:var(--accent);}
.title{font-weight:800;font-size:244px;line-height: 1.1;letter-spacing:-.02em;color:var(--ink);}
.title .stack{display:block;}
.title .alt{color:var(--accent);}
.sub{font-family:var(--font-mono);font-weight:400;font-size:46px;line-height:1.2;margin-top:34px;color:var(--ink-soft);font-family:var(--font-zh);}
.dimline{display:flex;align-items:center;gap:14px;margin-top:24px;color:var(--muted);font-family:var(--font-mono);font-size:22px;letter-spacing:.1em;}
.dimline .bar{flex:1;height:1px;background:var(--line-strong);position:relative;}
.dimline .bar::before,.dimline .bar::after{content:"";position:absolute;top:-5px;width:1px;height:11px;background:var(--line-strong);}
.dimline .bar::before{left:0;}.dimline .bar::after{right:0;}
.foot{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:23px;letter-spacing:.1em;color:var(--muted);}
.foot .name{font-family:var(--font-zh);color:var(--ink);font-weight:500;}
</style>
</head>
<body>
<div class="cover">
<div class="content">
<div class="top"><span>{{KICKER}}</span></div>
<div class="hero">
<h1 class="title"><span class="stack">{{TITLE_LINE_1}}</span><span class="stack alt">{{TITLE_LINE_2}}</span></h1>
<div class="sub">{{SUBTITLE_HTML}}</div>
<div class="dimline"><span>SCALE 1:1</span><span class="bar"></span><span>DWG</span></div>
</div>
<div class="foot"><span class="name">{{AUTHOR_NAME}} · {{CHANNEL}}</span></div>
</div>
<!-- 可选 opt-in (默认不放;承载信息时取用,CSS 已保留):
标题上方小字 → .hero 内 <h1> 前加: <div class="eyebrow">{{EYEBROW}}</div>
右上 REV index → .top 内 kicker span 后加: <span>REV {{INDEX}}</span>
dimline date → .dimline 末尾 <span>DWG</span> 换成 <span>{{DATE}}</span>
右下 DWG·index → .foot 内 .name 后加: <span>DWG · {{INDEX}}</span>
-->
</div>
</body>
</html>