Skills 仓库插画技能工艺规范深度解析:如何让 AI 手绘 SVG 卡片达到商业插画包水准
Skills 仓库插画技能工艺规范深度解析:如何让 AI 手绘 SVG 卡片达到商业插画包水准
本文围绕 Skills 仓库中 illustration-vintage-emblem 技能(Agent skills for designers and builders,适用于 Codex、Claude、Cursor 等 AI 编程代理)的 craft.md 工艺规范展开。该规范源自 42 张手写 SVG 卡片在盲评(blind judge)中与商业插画包的对比结果,以及设计师在特写检查中反复抓到的缺陷,是这套 illustration-* 系列风格通用的"底线规则"。读完本文,你将掌握一套可直接执行的文件契约、手部与姿态绘制检查表、"渲染—评审—修复"闭环流程与五维评分体系,并能在 emblem-kit.mjs、lint.mjs、render.mjs 等源码层面验证每一条规则背后的实现。
一、规范来源与适用范围:每一张卡片的硬底线
craft.md 开篇明确:这些规则在每个 illustration-* 风格中都成立,不是复古徽章风格的专利。它们的产生过程本身就是一个可靠的证据链:
- 来自 42 张手写 SVG 卡片,由盲评评委(不透露作者与风格名)对商业插画包进行打分对比;
- 来自设计师在特写(close-up)中持续发现并纠正的缺陷;
- 每条规则都点名它预防的失败——这是整套规范最可操作的地方:规则不是审美口号,而是"出了这个问题→因为没做这件事"的一一映射。
因此,后续所有小节都遵循同一写作逻辑:先给规则,再给"Failure prevented"(此规则预防的失败),读者可以据此自检。
二、卡片文件契约:自包含、可移植、防冲突
2.1 SVG 根元素与画布尺寸
每张卡片是一个自包含的:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 360">
- 4:3 卡片,以 480 宽为基准绘制;风格规格里的所有数字都假定该宽度(例如 emblem-kit.mjs 中
OUT = 3.6、KEY = 15、CROP = 229、SUN = [240, 114, 88]均按此画布标定)。 - 禁止项:
<style>、class、<script>、<image>、<foreignObject>、外部href或url();只允许使用表现属性(presentation attributes)。 - 预防的失败:卡片被粘贴进一个同样给
.hand或path写了 CSS 的页面时丢失颜色,或在沙箱化嵌入(sandboxed embed)中直接损坏。
这套契约在 lint.mjs 中被逐条机器校验,对应源码片段:
- 第 22–23 行正则检查根
<svg>必须带viewBox="0 0 480 360"与xmlns="http://www.w3.org/2000/svg"; - 第 24–25 行用正则逐一拒绝
<style>、class、<script>、<image>、<foreignObject>、(xlink:)?href="(?!#)...与url((?!#)...(仅允许指向本文件内#id的引用); - 违规即
ERROR并以退出码 1 结束(第 51–52 行),保证"进不了页面"的文件先被拦截。
2.2 id 前缀:每张卡片一个,贯穿所有资源
为每张卡片选择一个短的 id 前缀(如 dk-、cr2-),并让所有 id 都以它开头:渐变、图案、滤镜、clipPath、mask 全部包含。
- 预防的失败:一页上有两张内联卡片共享
id="grain",第二张卡片会静默渲染成第一张卡片的滤镜效果。 - 实现验证:
lint.mjs第 28–30 行收集全部id="...",若某个 id 不以--prefix指定值开头即报ERROR;第 31–32 行还检测重复 id;第 33–36 行检查url(#id)/href="#id"是否指向不存在的 id(悬空引用)。命令示例:node lint.mjs card.svg --prefix rk-。
2.3 全幅背景矩形:方角交给页面容器
当风格有全幅背景时,它是一个**方形满出血(full-bleed)**的 <rect width="480" height="360">,不带 rx;圆角由页面卡片容器负责。
- 预防的失败:双重圆角,四角各出现一条白色缝隙。
- 在复古徽章风格中对应 SKILL.md 的硬性要求:"Card: full-bleed
<rect width="480" height="360" fill="#f8f0e3">, square corners",且 style.json 将#f8f0e3标记为 Card 角色色。
2.4 文字与重复标记
- 文本使用
font-family="ui-sans-serif, system-ui, sans-serif"或ui-monospace;字标(wordmark)和大号字母必须画成路径;字要少,"卡片不是幻灯片"。 - 当风格重复某种标记(排线 hatching、半调圆点 halftone dots、抖动笔刷轮廓、草叶、砖块簇)时,写一个小的生成器(Node 或 Python),人物和主体对象手工放置;生成器必须有种子(seeded),保证重跑得到同一张卡片。
- 实现验证:复古徽章风格的
emblem-kit.mjs自带rng(seed)(第 20 行,线性同余生成器s = (s * 16807) % 2147483647),puffs()、foam()等所有随机形状都以它为源;wordmark.mjs则把所有字标渲染成填充路径(详见下文"字标与版式")。
三、原创性:绝不描摹参考图
- 每个场景必须是原创的设计或编码场景,配原创角色与道具。
- 禁止临摹或重组参考图:不沿用它的姿势、道具或版式。若参考图左侧有梯子上的人,你的图上就不能在左侧放一个梯子上的人。
- 完成前把卡片与看过的参考图对比,自问"别人会不会说这是那张图的重绘?"——会,就重排(recompose)。
四、手:每个风格里最先被抓的缺陷
craft.md 直言:手是评委和设计师在任何风格中第一个抓到的缺陷。它给出了完整的绘制协议:
4.1 构造顺序
袖口 → 渐细的前臂 → 比手更窄的手腕 → 手掌 → 手指 → 拇指。
- 禁止:拳头直接贴到袖子上、连指手套式的一团、手指画成盒子上的平行条纹。
- 对应源码支持:
emblem-kit.mjs提供了tube(pts, wfn)(第 94–105 行,沿采样点生成渐细管状路径)与blob()(第 60–69 行,Catmull-Rom 平滑闭合团块),正是"手腕窄于手掌、手臂渐细"这类构造的几何原语。
4.2 手指
- 手指渐细、长度不同:中指最长;小指最短且位置更低。
- 手指要成组,不要像耙子一样等距排开;露三到四根手指是正常的。
4.3 大小基准
- 手大约与下巴到眉头的脸长等长;更大就看起来像手套。
4.4 抓握
- 手指绕物体包裹,重叠顺序沿握持方向变化;拇指从另一面合拢成环。
- 两只手在同一物体上是两个独立的拳头,中间留缝。
4.5 打字/休息
- 手掌平放或弓起,指尖接触表面,拇指可见。
4.6 指向
- 食指伸出,其余手指弯曲且指节可见,拇指扣在中指上方。
4.7 张开/挥手
- 手指微微张开,拇指以约 45° 向外张开。
4.8 风格匹配
手指间隙的绘制方式必须跟随风格本身:
- 描边(outlined)风格 → 内轮廓线;
- 扁平(flat)风格 → 间隙或色调阶梯;
- 单线(one-line)风格 → 单个环。
五、左右手:把拇指放在正确的一侧
设计师曾因拇指放错边而否掉整批手。craft.md 强调:"除非被明确告知,Agent 永远不会推理这件事。"因此在画每只手之前,必须先写下三个决定:
- 是左手还是右手——沿手臂回溯到肩膀:
- 3/4 或侧面朝向 右 的人物,近侧(最靠近观众)是右手,近侧手臂通常从躯干前方穿过;朝向 左 则近侧是左手;
- 正对观众的人物,其右手在观众的左侧;
- 重叠顺序(在躯干前还是后)是最强线索;扁平风格中较深、带阴影的袖子通常是远侧手臂。
- 观众看到的是手掌还是手背(指节或指甲)?
- 手指指向哪边(腕到指节的方向)?
5.1 拇指方向查表
| 手 | 可见面 | 手指朝上时拇指在哪侧 | 通用规则 |
|---|---|---|---|
| 右手 | 手背 | 左 | 拇指 = 手指方向逆时针旋转 90° |
| 右手 | 手掌 | 右 | 拇指 = 手指方向顺时针旋转 90° |
| 左手 | 手背 | 右 | 拇指 = 手指方向顺时针旋转 90° |
| 左手 | 手掌 | 左 | 拇指 = 手指方向逆时针旋转 90° |
具体情形推演:
- 右手、可见手背、手指朝右 → 拇指在上;同样位置的左手 → 拇指在下。
- 侧视(edge-on)时转入 3D 推理:设 x 向右、y 向上、z 朝向观众,f 为手指方向、p 为手掌朝向、t 为拇指方向,则右手
t = f × p,左手t = p × f。右手掌心向下、手指朝右时,拇指在远侧(被隐藏);左手同姿势则拇指在近侧(可见)。 - 食指永远紧挨拇指;小指在对侧边缘。
- 抓握的手,手指与拇指在物体相对的两面;拇指在哪面由查表决定,"而不是看哪里画着方便"。
- 棒/柄握持(bat or handle grip)且双手持物时,两个拇指都指向工作端(business end)。
5.2 拇指证明(Thumb proof)
- 复制一份卡片(用完即删),把拇指填成
#ff0000、食指填成#0000ff; - 以 8x 渲染该手;
- 对照你写下的三个决定逐一检查。
这正好匹配 render.mjs 的区域放大渲染能力:node scripts/render.mjs card.svg hand.png --zoom x,y,w,h --scale 8(见下文"渲染闭环")。
六、姿态与接触:让画面"立得住"
- 给姿态重量:前倾、屈膝、对立平衡(contrapposto)、对抗拉力时扭转躯干。笔直对称的站姿读起来像剪贴画。预防的失败:一个正面对称重画的拉拽人物,比分比扭转版本低了整整 1 分。
- 坐姿必须由座位承重:坐豆袋椅时髋低于膝、大腿透视缩短、坐垫被压缩。预防的失败:"坐姿读起来像站着"。
- 一切有依托的物体都要接触:脚踩地面/地线,物体在表面上,杯子在桌上;没有东西"意外漂浮"。刻意漂浮的装饰(纸屑、闪光)要与人物保持距离。
- 禁止相切(tangent):两条轮廓恰好相接触会读作失误;要么清楚重叠,要么清楚分离。
- 手臂必须连通:每只手都能回溯到前臂、肘、肩。预防的失败:"一只手没有手臂"。
- 腿从髋到踝渐细;盒状 Π 形裤腿显得僵硬。
- 小腿进鞋的位置在鞋口(collar)、后跟上方,绝不能从鞋尖进入。脚平放地面时,小腿最多偏离垂直约 20°;脚更靠后时脚尖必须向下点;两条小腿大致等长。预防的失败:一条后收的远腿画成一条长斜线,直接插进鞋尖。
- 先量家具再放脚:一个凳子的脚环位于座面下方 35 px 处,而膝盖在立柱前 70 px,那脚根本踩不上去;把这只脚放地上。
- 头必须有脖子。
七、构图:一眼读懂
- 主体组居中,四周留出均衡、慷慨的边距:没有任何东西贴卡片边缘,也没有一个象限是死空的。
- 只有一个焦点:主体必须在 1x(480 px 宽) 下一眼可读。
- 匹配风格的密度:稀疏风格的杂乱卡片与稠密风格的空荡卡片一样失败。
- 画之前先命名主体,让每个道具都服务于它。当笑话需要落地时(如 "stack overflow"),在 1x 下测试:如果你需要读字幕才懂,说明画没落地。预防的失败:"stack-overflow 的笑话在 1x 下读不出来。"
复古徽章风格对构图的落实(见 SKILL.md "Composition" 小节):太阳圆盘 r 86–88 位于 (240, 114–118);场景被 <clipPath><rect width="480" height="229"/></clipPath> 裁剪,一切终止于 y 229 的硬水平边;徽章整体 276–328 px 宽、约 205 px 高;字标顶 y 258–260、居中 x 240,scale 0.68–0.95、cap 38–53;lockup 用 <g transform="translate(240 180) scale(0.95–0.97) translate(-240 -cy)">,cy = (徽章顶 + 标语基线) / 2,已发布卡片整体跨度 y 30–331(占卡片高度 84%)。
八、渲染—评审—修复闭环:只走无头浏览器
craft.md 明确规定:仅无头(headless)渲染,绝不打开可见浏览器窗口检查卡片。闭环共六步:
- 2x 渲染:
node scripts/render.mjs card.svg card.png - 与风格样例并排:
node scripts/render.mjs --sheet sheet.png examples/*.svg card.svg。问题不是"好看吗",而是**"同一只手、同一套牌吗"(same hand, same set?)**。 - 放大缺陷高发区,且从矢量重渲而非放大位图:
node scripts/render.mjs card.svg hand.png --zoom x,y,w,h --scale 8(x, y, w, h 为 SVG 单位)。每只手至少 3x 检查,外加脸、脚和每个接触点。 - 1x 可读性检查:主体必须在 480 px 宽下落定。
- 按下面五个标准写苛刻评审,修复后重渲;一张卡片诚实拿到 8 分前,通常要四轮以上。
- Lint:
node scripts/lint.mjs card.svg --prefix xx- --palette style.json。修复每个 ERROR;调色板外的 WARN 条目必须每一条都是有意的。
8.1 render.mjs 的底层行为
- render.mjs 通过
loadPlaywright()(第 22–35 行)依次尝试当前目录、工作目录、全局 npm root 解析playwright/playwright-core,缺依赖时提示npm i -D playwright-core;Chromium 按$CHROME_PATH→ Playwright 自带 →~/Library/Caches/ms-playwright→ 系统 Chrome 的顺序查找(第 37–50 行)。 --zoom x,y,w,h会替换 viewBox(第 85–89 行)而不是放大位图,这正是"缺陷从矢量重渲"的保证;--scale N通过deviceScaleFactor(第 62 行)实现像素级倍率。--sheet模式把多张卡片以 1x 并排渲染并标注文件名(第 71–79 行),支撑"是否属于同一套"的横向对比。- 页面 console error 会被捕获输出(第 63、94 行),避免静默吞错。
8.2 lint.mjs 的检查清单
除第二节的契约检查外,--palette 模式(第 38–46 行)会解析 style.json 的 palette 数组,把卡片里出现的所有 #rrggbb / #rgb 归一化后比对,列出调色板外颜色及其出现次数(如 #333 x4),并额外放行 #ffffff / #000000;它属于 WARN 而非 ERROR——"有意的色调没问题,意外的 #333 不行"。
九、评分:五个标准,每项 0 / 1 / 2
每张卡片的及格线是 8/10:"打磨过、有意图,只有小瑕疵"。核心判词是:"商业插画包的买家会把这张卡当作新卡收进包里吗?"
- 风格保真(Style fidelity):调色板、线宽、描边与填充规则、装饰词汇与密度全部匹配风格。
- 角色绘制(Character drawing):符合风格的体例比例、讨喜的脸、读得懂的手、脚与鞋、头发、服装褶皱。
- 姿态与物理感(Pose and physicality):手势一眼可读,有重量感与接触感,没有任何东西意外漂浮或相交。
- 构图(Composition):清晰焦点、均衡重量、风格密度、慷慨边距,不拥挤、不裁切。
- 主体清晰度与工艺(Subject clarity and craft):主体一眼可读;边缘干净、线宽一致、无多余杂点。
在复古徽章风格里没有人物,"角色绘制"即指主体对象(hero object)的构造——SKILL.md 的 Workflow 第 8 步明确写了这一点("character drawing = the hero object's construction")。
十、多人评审时的操作规程
- 评委漂移:同一渲染图,单评委可能波动最多 1.5 分。用两位新评委,每张卡取两人中的低分。
- 给每位评委每张卡的独立主题:一个只写了第一张卡主题的评分表,会让评委拿错误主题去给后面的卡打分。
- 比较两个版本时:让同一位评委以随机顺序看两个版本;评委之间的绝对分差约 1 分。
- 修复归属:评委意见发回画师时,发给画这张卡的同一画师;全新重画很少比针对性修复更有效。
- 单点修复:只修一个元素(一只手、一个头)时,diff 修复前后渲染图,确认只有该元素发生变化。若设计师说某个部件"之前更好",就地恢复该部件、保留其余修复。
十一、风格级落实:从工艺规范到 emblem-kit 源码
craft.md 的规则是通用的,复古徽章风格则通过 SKILL.md、style.json 与 scripts/ 落地为可复现参数:
11.1 七色调色板(style.json)
| 角色 | Hex | 用途 |
|---|---|---|
| Card 奶油 | #f8f0e3 |
全幅背景(占渲染卡 78–85%)、keyline、奶油高光、镂空水滴、云涌 |
| Ink 墨绿黑 | #1f2b25 |
描边、阴影楔、飞溅带、海鸥、字标、标语(占非奶油像素 45–51%) |
| Terracotta 陶土 | #c8693f |
太阳圆盘;主体上一处重复(鼻锥、甲壳、舷窗环、火焰) |
| Sand 沙 | #d9a873 |
主体主填充(鳍、冠、镜片缘、焰心) |
| Tan 棕褐 | #b5824f |
较暗填充:树干、握把、沙色下方的阴影调 |
| Pale 淡 | #ecd2a8 |
高光条、岩面、玻璃 |
| Teal 青 | #3f8273 |
仅点缀:每卡一条带/环/领/节点圆点,占非奶油像素 2–4% |
硬性禁令:无渐变、无透明度、无纹理、无排线、无滤镜、无纯黑或纯白、无第六个色相;陶土绝不触碰太阳(主体上任何陶土元素都在 keyline 内);青色绝不填充大形状(第一张卡的青色鳍组被整组裁掉)。
11.2 线条与填充参数(emblem-kit.mjs 常量)
- 主体描边 3.6 ink、
stroke-linejoin="round"(OUT);内部小件 2.2–2.8(喷嘴、衣领,舷窗环 2.0)。 - 联合轮廓(union outline):读作一个整体的多个形状(树干+树根、一团云)共用一条轮廓——7.2 宽的 ink 底衬 + 填充色,由
unionOutline(ds, fill)(第 113–115 行)实现。 - Keyline:主体剪影形状再画一遍,填+描 15 宽奶油(7.5 px 奶油间隙),画在太阳之后、主体之前;每个瓣、手柄、尖刺都要进 keyline,否则主体会"融化"进圆盘。对应
keyline()(第 117 行)与card()中keyline(key, ...)的注入点。 - 阴影 = 在颜色里挖 ink,位于右/下侧(左上打光):
wedge()木刻凿痕宽 1.8–4.2(树干上 10),钝底尖头;圆形件内crescent()月牙厚 2–4.2;蓬松团块用双调偏移puffs()(tan 在下、sand 偏移 (−2.6, −7.6) 在上,tan 露出为右下月牙)。 - 高光 = 左上方的 pale/cream 条:
streak()半宽 1–2.4、两端尖;每对象 2–5 条;窄部件可用短圆头 pale 短线(1.8–3.6)。 - 海鸥、飞溅带、水滴、字标不上描边(字标仅用 2.2 同色描边圆角)。
11.3 字标与版式(wordmark.mjs)
- 字标是无字体、无依赖的填充路径:cap 高 56、竖干 12.2、横杠 9.4、圆角 2.2,支持 A–Z、0–9 与
- . ! ';CLI:node scripts/wordmark.mjs "RELEASE DAY" --tagline "TAG • SHIP • REST" --prefix rd-输出<g>、标语<text>与 lockup 数值;--max-width 280(默认)决定 scale,--sheet alphabet.svg渲染全部字形。它能精确复现已发布的 OPEN SOURCE 与 DEBUG CLUB 字标。 - 只有 12.5 px 的标语允许用
<text>(Georgia bold、tracking 2、"•" 分隔,见taglineText()),如 Ship It。 card()(emblem-kit.mjs 第 195–219 行)自动组装整个 SVG 壳:卡片矩形、裁剪、太阳、keyline、lockup 居中、字标、标语;当主体高过太阳(火箭鼻、弓)时传top:参数,否则 lockup 会以太阳为中心而整体偏低。
11.4 自测卡
node scripts/emblem-kit.mjs demo.svg 会写出一张覆盖所有 helper 的自测卡(云团 puffs()、飞溅 foam()、海鸥 gull()、高光 streak()、字标等),用来验证工具链本身是否完整可用。
十二、失败模式速查(源自已发布卡片的教训)
SKILL.md 的 Failure modes 小节是 craft.md 规则在风格层的具体回声,可与上文逐条互证:
- 主体被道具淹没:火箭旁加发射塔分散焦点——一个 hero 就够(对应"一个焦点")。
- 青色泛滥:青色鳍让徽章读成另一套调色板——青色只留一条带/一个环。
- 底边太忙:单独描边的云团排成一排像气泡膜——中央用一条**合并(
merge: true)**的奶油云涌,两侧用带镂孔的 ink 瓣。 - 扁平云香肠:等高云团排成一排像靠枕——云岸做成土丘形,hero 下方最高。
- 字标太小:13 字母单词在默认宽度下 cap 只有 33——先
--max-width 320加宽,再缩短文字。 - 树冠读成球/蘑菇:不要用裸圆当 foliage——用凸起环+顶部凸起下的 ink 卷曲+底部凿痕+双调偏移(
puffs())。 - 树根/线条读成涂鸦:自由斜线——按带垂直切线的通道(
gstep,git-graph 式)排布并加节点。 - 主体融进太阳:某个瓣或手柄漏了 keyline——每个剪影形状都进 keyline 组。
- 字标像系统字体:
<text>里的系统字体会随机器变化——用wordmark.mjs生成路径。 - 字标笔画折叠:圆角半径大于内宽一半会在圆形字形内部出现奶油细缝——
wordmark.mjs的cr()会钳制它(第 92 行),自定义字形也要遵守。 - Lockup 偏心:缩放时没重新居中——
card()按徽章顶与标语基线计算 cy。
十三、端到端工作流(在技能目录下执行)
将 craft.md 的循环与 SKILL.md 的 Workflow 合并,得到可直接照做的 9 步(命令均在技能目录运行;render.mjs 需要 playwright:npm i -D playwright-core 于本目录或全局):
- Brief:命名主体,选一个能说明它的 hero 对象,写 1–3 词字标 + 三拍标语(
•分隔)。 - Hero:用 3–8 个大形状勾剪影;决定它如何破太阳圆、飞溅带里放什么底座。
- Words:
node scripts/wordmark.mjs "WORD" --tagline "A • B • C" --prefix xx-看 scale 与 cap 高;cap 小于 38 就先--max-width 320重跑,再考虑缩短文字。 - Block:写生成器(
card.gen.mjs),按绝对路径 importemblem-kit.mjs;先剪影(喂给 keyline),再填充、ink 阴影、pale 高光、飞溅、海鸥;用K.card()做壳。 - Render:
node scripts/render.mjs card.svg card.png(2x)。 - Sheet:
node scripts/render.mjs --sheet sheet.png examples/*.svg card.svg——墨量相当?调色板占比相当?lockup 大小相当? - Zoom:
node scripts/render.mjs card.svg z.png --zoom x,y,w,h --scale 6,专查 keyline、雕刻、飞溅镂孔与字标接缝。 - Critique:按 craft.md 五标准苛刻自评(角色绘制 = hero 构造),修复并重渲,预期 3–4 轮。
- Lint:
node scripts/lint.mjs card.svg --prefix xx- --palette style.json,应零警告通过。
最终验收清单(SKILL.md Verify 小节)可作为 craft.md 规则的收尾自检:仅七色且 --palette 无 WARN;奶油全幅方角矩形;陶土太阳 r 86–88 在 hero 身后且 hero 破圆;7.5 px 奶油 keyline 分隔 hero 每一部分;每个 hero 形状有 3.6 ink 描边;无渐变/透明度/排线/纹理/滤镜;场景止于一条硬水平边并带镂孔飞溅带与甩出水滴;2–4 只海鸥且不触 hero;青色只出现一次或两次且很小;字标出自 wordmark.mjs、cap 38–53、居中、最宽 310 px、标语 12.5 衬线加粗带 •;lockup 垂直居中(上下差 ±6 px);1x 下不看字也能读出主体。
结语:把"工艺"变成可执行清单
craft.md 的价值在于它把审美判断翻译成了可验证的工程约束:文件契约有 lint.mjs 兜底,拇指方向有查表与叉积公式,姿态问题有"接触—相切—连通"三问,质量有 0/1/2 五维评分与 8/10 及格线,迭代有 render.mjs 的无头闭环。对使用 Codex、Claude、Cursor 等 AI 编程代理的开发者而言,这组规则既是给 Agent 的提示词约束,也是一份可以反向用于评审任何 AI 生成插画的质检协议——从"这张图好看吗"到"这张图能进商业插画包吗",差异就在于是否逐条落实了这些工艺规范。
