关于 editorial-illustration
根据文章正文创作编辑类主视觉插图的技能,面向技术杂志风格的图像制作。它以自主流程引导从解读文章、筛选概念到制定美术方向并生成 SVG 成品。
主要功能
- 01提取文章主题与关键段落
- 02构思四类视觉概念
- 03评估内容契合度与视觉清晰度
- 04制定分层绘本风格美术方向
- 05生成完成的 SVG 插图
适用场景
- 01为技术文章制作主视觉
- 02将文章观点转化为场景图像
- 03在绘制前比较插图概念
- 04制作风格统一的编辑类 SVG
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ markomitranic/ jotdown --skill editorial-illustration安装后在 Claude Code 中输入/editorial-illustration确认已加载。
附带文件scripts/render.ts
详细介绍
编辑插画
捕捉文章试图传达给读者的想法,并创作一幅出色的主视觉编辑插画,让设计精良的技术杂志也引以为傲。杂志会委托艺术家创作插画,描绘一个场景,捕捉文章精髓,让读者一眼就能_感受到_要点。每篇文章的开头都会有一幅这样的插画,置于前置内容之后、摘要之前。
自主完成全部四个部分。“回复用户”表示发送进度更新:无需等待回复,继续执行。自行选择解读、概念、配色和修改方案。
第 1 部分:解读
目标: 确定你希望插画传达什么。
- 阅读文章的全部正文。
- 标出五段内容,其中包含目标、鲜明主张、矛盾、令人意外的细节或情绪时刻。
- 写出三种可能的解读,每种不超过 20 个词。每种都应针对主题提出一个观点。
- 选出你最想探索的一种解读,并写下你希望读者感受到的一种情绪。
示例: 对于一篇关于 AI 助手的文章,可能的解读包括:
- “把日常任务交出去,让我们能投入有意义的工作。”
- “便利可能会逐渐演变为依赖。”
- “我们正把越来越私密的责任交给陌生的系统。”
回复用户: 三句解读、选中的一句,以及一种期望引发的情绪。
第 2 部分:艺术风格
目标: 清晰定义插画采用的视觉风格。
视觉风格:线条、阴影、渐变、构图规则,以及光线、颗粒感、表面和边缘的绘制方式。我们的目标是打造一致的奇幻写实风格,采用风格化的 2D 绘本美学,呈现插画故事的质感、丝网印刷与 risograph 印刷的效果,并融入 Monument Valley、Global Village Coffeehouse 和 Nomada Studio 的 Gris 中的光线与表面质感。
海报美学、图形化剪影和数学构图。突出清晰度、大胆的形状、层次感和鲜明的视觉层级,引导观者的视线。构建一个可信的世界,让不可能之事悄然发生。以画家的纵深和讲述者的克制来呈现这个世界,让视线有所游历,也有一处可以停留。出色的艺术指导来自一致性。从配色到构图,每个元素都应强化视觉风格。
世界与纵深
- 光线至关重要:顶部最亮,朝向光线的一面为中间调,背光的一面最暗。
- 场景由 3 至 5 个层面构成,从最靠近镜头的部分延伸到主体后方;每层都是边缘自然、略带不规则的简单剪影,像纸艺剪贴层一样叠放。
- 距离由氛围呈现:远处的层面更亮、更朦胧、更冷,反差也更弱;近处的层面更暗、更清晰。仅凭这一点,就能赋予画面纵深。
- 层面之间始终留有空气感:用薄雾、雾气或辉光过渡,绝不生硬切断。
流动线条
- 一两条悠长流畅的曲线掠过场景,将视线引向主体:缎带、布料、烟雾、河流、电缆、光迹、道路、树根。无论场景中已有何物,都将其绘成新艺术运动风格的甩鞭曲线,但不加装饰。
- 流动曲线是具有自身正面的实体。它与其他物体一样有光照、纹理和线条,绝不是光秃秃的一笔。
- 曲线要足够宽,能显露一个正面;转向光线时逐渐变宽,两端收尖,并以 S 形曲线延展,而不是圆弧。
- 曲线至少扭转一次,让它的背面显露出正面的核心颜色。
- 曲线穿梭于各个平面之间,在主体后方和前方交替经过,以曲线本身展现纵深。曲线经过物体时要分段绘制;只描绘它的两条长边,确保分段处没有横跨曲线的接缝。
- 曲线进入受另一种光照的场景区域时,也要呈现该处的光色。
- 场景中的所有曲线都朝同一方向流动,仿佛受到同一阵风的推动。一条有力的曲线胜过几条细线;它旁边的细线则会被视为运动线。
线条
- 只有当轮廓与光线相交时才出现线条,且线条必须使用自身的颜色,绝不用黑色或深灰色。朝向光线的边缘使用比正面更浅、更暖的色调;背后有光源的边缘则使用轮廓光,也就是光晕的颜色。
- 背光的边缘,或与背景明度相近的边缘,不描线。线条只覆盖部分轮廓,并沿轮廓渐渐淡出,绝不勾勒完整外形。
- 整幅画板上的线条粗细保持一致:宽度为 960px 时,线宽为 1.5 至 2.5px;线条沿着所描绘边缘相同的起伏路径延伸。
- 线条只描绘轮廓。同一物体的两个正面相交处,通过颜色变化表现边缘。
- 线条始终保持实心、清晰。纹理绝不覆盖线条。
- 光线、空气、雾霭和云朵都不描线。
背景
- 场景后方绘制一层制图元素,仿佛画板覆盖在自身的构图草图之上:由圆环、十字准线和标记点组成的罗盘,围绕主体的点状罗盘,彼此以点状星座线相连的四尖星,以及小十字、远处形状周围的虚线轨道和其他装饰元素。
- 背景装饰保留在日光中,但要相应调整,并且几乎不可见。
- 使用一种符印颜色绘制,线条纤细且透明度较低。装饰元素应起到衬托主体的作用,绝不与主体交叠。背景装饰应低调、对比度低。
表面
- 每个表面都带有纹理。每个面从整体到平坦的中间区域都带有颗粒,因此没有任何填色看起来像干净的矢量图形。颗粒属于某个面,并随该面的受光情况变化;在整张画面上铺一层均匀噪点,是偷懒的做法。
- 一个面最多通过三种颜色渐变:朝向光源的一侧使用受光色,形体转向背光处使用更深的核心色,远侧边缘则使用反射色,从邻近物体上借色。核心色是该面本身更深、色相略有偏移的版本,绝不能让整个场景共用一种阴影色。
- 面上的每次颜色变化都由颗粒构成:新颜色以点状叠加在旧颜色上,从没有点逐渐过渡到完全铺满,让过渡最终落在纯色上,而不是消散成一片稀疏的点雾。
- 点的明度应接近底下的颜色。对比应体现在形状上,而不是颗粒上;跳出表面的点会显得像污渍。深色底上的亮点看起来像发光,可以稀疏一些。
- 颗粒应在轮廓内部表现形体。它顺着表面的转折变化,绝不能在物体旁边形成投影带。
- 实心物体应保持清晰、完整的轮廓。颗粒可以让面一直暗到边缘,但绝不能侵蚀轮廓。只有没有自身表面的东西——光和空气——才会在边界处消散成点。
- 平滑、无颗粒的渐变只用于天空和不同平面之间的氛围层。
- 物体应呈几何形态,但轮廓线不能笔直得像用尺子画出来。每条边都应以缓慢的波形向侧面偏移,幅度最多为 1px,约每 45px 起伏一次,绝不能像逐点抖动。每隔 10px 取样一次边缘,并根据点的位置从平滑场中获取偏移量,这样共用同一条边的两个面会以相同方式起伏,不会在接缝处裂开:
const hash = (x: number, y: number, k: number) => {
const s = Math.sin(x * 12.9898 + y * 78.233 + k * 37.719) * 43758.5453;
return (s - Math.floor(s)) * 2 - 1;
};
const ease = (t: number) => t * t * (3 - 2 * t);
const field = (x: number, y: number, k: number) => {
const gx = x / 45, gy = y / 45;
const x0 = Math.floor(gx), y0 = Math.floor(gy);
const tx = ease(gx - x0), ty = ease(gy - y0);
const top = hash(x0, y0, k) * (1 - tx) + hash(x0 + 1, y0, k) * tx;
const bottom = hash(x0, y0 + 1, k) * (1 - tx) + hash(x0 + 1, y0 + 1, k) * tx;
return top * (1 - ty) + bottom * ty;
};
const waver = ([x, y]: [number, number]) => [x + field(x, y, 1), y + field(x, y, 2)];
不要这样
- 不要追求写实,也不要尝试做出任何看起来写实的效果。
- 不要使用闪亮的矢量剪贴画风格:均匀填色、完美线条、没有光影。
- 不要让干净的平涂与点状颗粒块并排出现,也不要使用与表面颜色形成强烈对比的点。
- 不要让实心物体的边缘碎裂成点,仿佛正在蒸发。
- 不要在整张画面上覆盖均匀的噪点纹理。
- 不要使用黑色或深色轮廓线,也不要用同一种线色为每个物体勾边,让它们看起来像贴纸。
- 不要使用模糊或景深效果。远处应呈现雾霭,而不是模糊。
- 不要在画面中放文字。
- 不要用一个小人物来表示比例或代表读者。只有当主题与人物有关时,画面中才应出现人物;此时人物应足够近,能看清面部和姿势。
- 不要让每个物体都完整地居中摆放,并在四周留出过多空白。
回复用户: 写一段长段落,描述插画的四条实用规则。
第 3 部分:构思
目标: 为你选择的解读找到有力的视觉表达。
(3.1) 首先,列出与文章及你的解读相关的十种具体物体或动作。找到现实世界中已经发生这一方面的场景,然后加上一点变化:加入一个小小的不可能之处,让观者多看一眼,继而会心一笑。
只要能画出来,就优先从文章本身的名词入手。读者已经知道这些名词的含义,因此无需转换,而不可能发生的动作便能带来趣味。只有当文章中的名词无法画出来时,才使用代替物。
例如,对于“依赖”,可以包括:倚靠、搬运、喂食、跟随、拐杖、根、绳子、仪式、旅程、电池。
(3.2) 在回复中写出构思,每个构思用一段较长的文字,写出四种不同的构思,每种各使用一个提示词:
- 场景: 展示一个让这一想法变得可见的情境。
- 替换: 用一个会改变含义的物体替换人们预期会出现的物体。
- 融合: 将两个物体合并为一个。
- 反转: 交换控制、支撑、搬运或依赖对方的角色。
(3.3) 按照以下优先级对四种构思进行比较:
- 忠于文章:指出支持该构思的段落。舍弃暗示了文章未提出的主张的构思。
- 视觉机制:指出表达这一解读的可见动作、关系或变化。舍弃含义依赖文字描述、而非画面呈现的构思。当文章中的主体在行动时,代表它的物体也必须明显地行动:它的姿态、朝向或视线要体现这一动作。如果无法让某个物体行动,就换用另一个物体,而不要指望读者自行推断。
- 易于理解:趣味值得欢迎,困惑则不然。不可能发生的动作就是重点,因此要坚定地呈现它,绝不要用合理的机制来解释消解。必须成立的是关联:每个物体都承载着读者原本赋予它的含义,而那唯一的不可能之举将它们联系起来。想想一个陌生人只看图,是否会说出同一个想法。舍弃那些必须先转换物体含义,画面才能说得通的构思。
- 情感作用:指出尺度、姿态、构图或色彩如何支持预期情感。将情感效果视为一种推断,并以这些可见的选择为依据。
(3.4) 筛选出两个构思。分别用一句话记录每个构思最有力的视觉机制和主要弱点。
(3.5) 选择最符合上述优先级的构思。只有当从另一个构思中借来的元素能针对明确指出的弱点,且不会削弱核心视觉机制时,才加入该元素。
回复用户: 选出一个构思,用一段较长的文字进行详细描述。
第 4 部分:执行
**目标:**创作一幅完成的 SVG 插画,让其中的视觉决策服务于概念。
选择最能传达概念的视觉语言:场景中的透视、构图和空间关系。每幅作品的镜头角度都是一种叙事选择,都应重新构思。镜头距离决定读者与想法之间的距离,镜头高度决定主体是高高耸立于读者面前,还是低伏在读者脚下,镜头则决定周围环境有多重要。从画面所要表达的关系入手,把镜头放在能让这种关系充满画面的地方。取景时要捕捉想法发生的瞬间,而不是画面中每个物体的全貌:读者只凭事物的一部分就能认出它,而画面的边缘应裁掉所有无助于表达想法的内容。
- 按预期比例完善选定的草图。收集其中物体和环境所需的参考资料。
- 使用选定的风格和概念,从两个不同的镜头位置绘制两幅小幅探索稿:一幅容纳完整场景,另一幅只聚焦于想法发生的部分,让其余内容延伸到画面边缘之外。选择其中一幅。
- 重新审视并润饰选定的探索稿,完成作品。找出并修正所有妨碍读者理解核心关系的内容。
定义调色板
项目配置在 .jotdown/config.json 中提供了插画的初始调色板,即项目主题。以项目主题为基准,并根据场景或想要传达的情绪进行调整。
插画必须同时适用于深色和浅色模式,浅色模式应表现同一场景在“一天中的另一个时段”。切勿简单地将一种模式反相来得到另一种模式:反相会让辉光变成污渍,让阴影变成迷雾。应使用调色板,精心塑造每种模式的情绪和可读性。
颗粒密度也属于调色板的一部分。夜晚的阴影通常颗粒更密、辉光更强;白天受光的表面则有更多高光颗粒。两种模式下,每个表面都应有自己的固有色和反射色,每个实体物体也都应有自己的线条颜色。
**回复用户:**说明为深色和浅色模式定义的调色板,并解释颜色选择如何烘托场景和情绪。
将插画放到页面上
插画应是独立文件,像其他图片一样上传,并作为 Markdown 图片放在页面的 :::wide 区块中,紧接在 front matter 之后:
:::wide

:::
-
始终使用宽幅。 一幅作品像杂志跨页一样开篇引出故事,因此宽度应超出正文栏。
:::wide的宽度与工作室的宽度控制为图片设置的宽度相同,因此页面和编辑器会保持一致。 -
切勿将 SVG 粘贴到页面中。 一幅绘制完成的作品会包含数百 KB 的路径数据。以内联方式插入时,每个 Agent 读取页面时都会首先处理它;每次编辑都要重新编译,浏览器也无法缓存它。作为独立文件时,它只会让页面多出一行。
-
文件存放在
docs/_uploads/img/docs/中,也就是页面内编辑器放置上传文件的位置。文件名应根据其打开的页面命名为illustration-<page>.svg,并将文件夹名称也包含在内:docs/studio/branding.mdx对应illustration-studio-branding.svg。 -
替代文本应描述场景中发生的事情,供无法看到插画的人阅读。
-
标题应传达信息,而不是描述场景。它既是印在图片下方的说明文字,也是读者展开图片时显示的文字。
文件
<svg xmlns="http://www.w3.org/2000/svg" id="dark" class="pr" width="960" height="540" viewBox="0 0 960 540">
<style>
.pr { --pr-sky: #d9dcf7; --pr-face-lit: #c9b2e6; --pr-face-core: #9a82c9; --pr-face-bounce: #e6b8c9; --pr-texture: 0.2; }
.pr:target { --pr-sky: #0a0920; --pr-face-lit: #3a327f; --pr-face-core: #221c55; --pr-face-bounce: #5b3f7a; --pr-texture: 0.3; }
</style>
<rect width="960" height="540" fill="var(--pr-sky)" />
...
</svg>
- 两套调色板都放在同一个文件中。 每种颜色都是一个按用途命名的变量,在
<style>中设置两次:绘图类上的浅色调色板,以及:target下的深色调色板。深色模式下,页面会在#dark处绘制同一个文件,这样:target才会匹配,因此根元素保留id="dark"。在填充、描边和渐变色标中使用var(--pr-…)。 - 这是纯 SVG,不是 JSX。 属性名称遵循 SVG 的拼写方式:
class、stop-color、stroke-width、stroke-linecap、fill-opacity、color-interpolation-filters,以及 SVG 本身使用的驼峰命名属性:viewBox、baseFrequency、numOctaves、gradientUnits。 width="960" height="540"决定图片的形状;页面会将其缩放到:::wide区块的宽度。- 文件独立运行。 图片无法访问页面,因此其中的内容不能引用外部内容:不能使用
--jd-*变量、字体、其他文件的链接或脚本。这里可以使用十六进制颜色,因为图板是带有自身底色的艺术作品。 - 生成文件。 用临时脚本编写形状并输出 SVG,脚本不要放进项目。项目中只包含
.svg文件。 - 仅限静态内容。 不要使用动画或 CSS 过渡。可以使用滤镜。
用颗粒着色
每个图板使用多个滤镜(雾化、模糊、颗粒)。着色会按照像素的不透明度设定概率,让形状的每个像素完全保留或完全消失,然后以 35% 的不透明度将颗粒叠加到平滑的形状上,使纹理保持柔和:
<filter
id="pr-dz"
x="-5%"
y="-5%"
width="110%"
height="110%"
color-interpolation-filters="sRGB"
>
<feTurbulence
type="fractalNoise"
baseFrequency="1.6"
numOctaves="2"
seed="4"
result="noise"
/>
<feColorMatrix
in="noise"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.9 0 0 0 -0.45"
result="spread"
/>
<feComposite
in="SourceAlpha"
in2="spread"
operator="arithmetic"
k2="1"
k3="-1"
k4="0.49"
result="diff"
/>
<feComponentTransfer in="diff" result="mask">
<feFuncA type="discrete" tableValues="0 1" />
</feComponentTransfer>
<feComponentTransfer in="SourceGraphic" result="solid">
<feFuncA type="linear" slope="255" />
</feComponentTransfer>
<feComposite in="solid" in2="mask" operator="in" result="grain" />
<feComposite
in="grain"
in2="SourceGraphic"
operator="arithmetic"
k2="0.35"
k3="0.65"
/>
</filter>
使用 feFuncA 让颜色变为不透明,切勿在 feColorMatrix 中使用 255:macOS 上的 WebKit、桌面应用和 Safari 会丢弃如此大的矩阵值,导致什么也画不出来。确保 k2 和 k3 的和为 1;增大 k2 可以增强颗粒感。
先用受光颜色平涂面部,再按渐变的每个步骤将同一形状叠加一次,每层使用一种不透明度变化的颜色,并让每层都经过滤镜处理:
<linearGradient id="pr-face-core" x1="0" y1="0" x2="1" y2="0">
<stop offset="0.2" stop-color="var(--pr-face-core)" stop-opacity="0" />
<stop offset="0.7" stop-color="var(--pr-face-core)" stop-opacity="1" />
</linearGradient>
<linearGradient id="pr-face-bounce" x1="1" y1="0" x2="0" y2="0">
<stop offset="0" stop-color="var(--pr-face-bounce)" stop-opacity="1" />
<stop offset="0.3" stop-color="var(--pr-face-bounce)" stop-opacity="0" />
</linearGradient>
<polygon points="…" fill="var(--pr-face-lit)" />
<polygon points="…" fill="var(--pr-face-core)" fill-opacity="var(--pr-texture)" filter="url(#pr-dz)" />
<polygon points="…" fill="url(#pr-face-core)" filter="url(#pr-dz)" />
<polygon points="…" fill="url(#pr-face-bounce)" filter="url(#pr-dz)" />
- 第一层叠加是面部的纹理:使用核心颜色,并保持均匀且较低的不透明度,为 0.15 到 0.3。
- 每层叠加只改变不透明度,不改变颜色。色标的不透明度决定有多少颗点会保留:0 表示没有,1 表示完全保留。
- 每次过渡都要一直延伸到 1,这样才能过渡到下一种颜色。若某层叠加在不透明度为一半时停止,就会留下稀疏的斑点雾。
- 按渐变顺序堆叠各层:纹理、核心色、反射色,然后是任意高光或辉光。
- 将
k4保持在略低于0.5的值,否则渐变中完全透明的部分会漏出多余的斑点。
渲染并查看
export AGENT_BROWSER_SESSION="$(agent-browser session id --scope worktree --prefix task)"
bun .agents/skills/editorial-illustration/scripts/render.ts docs/_uploads/img/docs/illustration-<page>.svg
该脚本会绘制文件呈现在页面上的效果:一次为白天版本,一次为 #dark 版本,
并打印每张截图的路径。对于每条路径,调用 view_image agent
工具并传入该路径。根据以下标准评估返回的图像。
按需修改 SVG,然后再次渲染并检查。
如果修改能解决可观察到的问题或与文章内容不符之处,就进行修改。重新渲染并从头检查修改后的构图。最多进行两轮评估与修改,然后采用效果最好的版本,并报告任何尚未解决的问题。留意粗糙马虎的痕迹,必要时重做:
- 找出传达所选解读的可见关系或动作。检查它在页面宽度下是否清晰易读。如果其含义依赖于图片中没有直观呈现、必须通过解释才能理解的内容,就修改图像。
- 阴影是否与光源一致?
- 不可能的几何结构应显得浑然天成,其周围的空间逻辑应保持一致。
- 构图能否有效引导视线?
- 主体关系占据了画面的多大面积?如果主体完整入镜后四周仍有余裕,说明镜头太远。靠近后是否会丢失任何传达创意的元素?如果不会,就靠近一些。
- 物体看起来是否像它们本来的样子,而不是形状相似的其他物体?
- 配色是否和谐且适合场景?
- 场景是否有效传达了预期的情绪或氛围?
- 留白是否有效地平衡了构图?
- 浅色版本看起来是否像同一场景在另一个时段的样子,而不是反相图像?
- 轮廓是否有自然的起伏,同时不失几何形态,并且两面交界处没有接缝?
- 流动的曲线是否能引导视线到主体,并穿行于各个平面之间?
- 线条是否只出现在轮廓与受光面相交处,并沿轮廓逐渐淡出,既没有黑线,也没有给物体一圈完整描边?
- 放大观察颗粒纹理。每个面是否都有纹理?纹理点与填充色的明度是否接近?每次颜色过渡是否落在纯色上?每个纯色物体是否都保留了轮廓?