Icon Forge
绘制自定义图标,使其看起来像来自同一套专业图标库,并零运行时依赖地交付。当每个图标都通过 lint 检查、你已查看 16/20/24px 的预览图且其在明暗背景下清晰一致、以及生成的组件已通过无障碍用法接入时,任务才算完成。
<skill> 指包含此文件的文件夹(例如 .claude/skills/icon-forge、.cursor/skills/icon-forge、.agents/skills/icon-forge)。脚本需要 Node ≥18 且无需项目依赖。
何时使用 / 何时不用
- 使用场景: 侧边栏、导航栏、仪表盘、设置页面、空状态或工具栏需要图标;用户要求自定义或缺失的图标;用户希望用轻量级自定义图标集替换 emoji/占位图标或繁重的图标包。
- 不用场景: Logo 和品牌标识(商标问题;需要设计师处理)、favicon/应用图标、插画或点缀图、照片/位图、动效 Lottie。如果用户明确要求安装一个图标库(例如“直接用 Lucide”),请按他们的要求做;此技能仅服务于自定义路径。
硬性规则(及其原因)
- 每个图标自己绘制;不添加任何图标库依赖。 用户选择自定义图标是为了减小打包体积和获得独特外观。不要粘贴来自 Lucide、Heroicons、Material 等库的路径数据(涉及许可和原创性)。使用
references/glyph-cookbook.md(原创绘图)作为起始几何参考。 - 如果项目已使用某个图标库,则匹配其网格、笔触、端点和圆角半径(参见
scripts/detect.mjs和设计规则 §10)。新图标必须与现有图标放在一起而不显突兀。 - 一套图标,一种风格。 所有图标使用相同的网格、笔触宽度、端点/连接样式、半径比例、边距和细节密度。一致性比任何一个别出心裁的图标更重要。
- 表现属性只存在于根
<svg>上,颜色使用currentColor,源文件中不包含 id、样式、class、transform 或<g>。 否则,主题切换、暗黑模式、strokeWidth属性和内联方式都会失效。 - 切勿在不渲染并查看 PNG 的情况下声称图标没问题。 在代码中看起来正确的几何形状在 16px 时常常会出问题:模糊、闭合间隙、重心偏移、粗细不均。
- 运行脚本;不要手动重新实现它们。 Lint、优化、预览和代码生成都是确定性的。永远不要手动编辑生成的文件;修改
icons/src/然后重新构建。 - 不要修改用户的依赖。 预览工具会安装到
~/.cache/icon-forge(使用--install);SVGO 在本地缺失时通过npx运行。在向项目添加任何包之前先询问用户。 - 默认无障碍。 图标默认
aria-hidden,除非被标记;仅图标控件需设置aria-label;可交互目标 ≥44px;有意义的图标需 ≥3:1 对比度。
工作流
1. 检测技术栈和现有风格
node <skill>/scripts/detect.mjs .
注意框架、TypeScript、Tailwind、任何图标库(及其样式预设)、本地 SVG 图标以及建议的输出目录。如果存在现有图标集,打开其中的 2-3 个 SVG 并复制它们的根属性值。否则使用默认值:24 网格、笔画 2、圆角端点/连接。
2. 计划列表:含义 → 隐喻 → 文件名
在绘图前编写一个简短表格:标签 | 隐喻(绘制什么) | 文件名 | cookbook 起点。例如:Projects | kanban board | kanban.svg | kanban。选择主流隐喻(设计规则 §7)且每个含义只用一个隐喻。以 kebab-case 格式为对象命名文件。仅在标签有歧义且选择会改变 UI 含义时才询问用户;否则自行决定并说明选择。
3. 在 24 网格上绘图
创建 icons/src/<name>.svg,包含规范根元素(参见 svg-code-rules.md)。对于每个图标:
- 从匹配的关键线开始:圆形 r10,方形 18(x/y 3),竖屏 16×20,横屏 20×16。关键线是笔触的中心线。
- 保持笔触外缘距画布边缘 ≥1 单位。大角使用半径 2,小角使用 1–1.5;保持间隙 ≥2 单位的空白区域。
- 让部件要么精确连接,要么保持明显间隙。2px 笔画使用整数坐标。尽可能使用最少的元素(通常 ≤6 个)。
- 从最接近的
references/glyph-cookbook.md条目开始,然后进行调整。当字形完全适用时,直接从<skill>/assets/cookbook/<name>.svg复制几何形状。 - 使用几行 JS 计算重复几何形状(齿、射线、星点),而不是手动猜测。 先绘制整套图标,再逐步完善。一致性需要横向对比判断。
4. Lint 检查
node <skill>/scripts/lint.mjs icons/src
修复每个 ✖。将 ⚠ gap、density、scale 和 bounds 视为需要修复的设计问题,而非无关噪声。注意每个 ℹ gap 并在步骤 6 中检查;紧密的平行笔画(条、线)在 16px 时会模糊,除非它们相距 4 个单位。重新运行直到显示 PASS 且没有无法解释的警告。标志:--grid、--padding、--mode fill、--strict、--json。
5. 优化
node <skill>/scripts/optimize.mjs icons/src icons/svg
此步骤会四舍五入数字、运行 SVGO(本地安装或 npx svgo@4;离线时回退到内置规范化器)、重新排序根属性,并输出每个图标字节数及 gzip 总大小。也要对输出进行 lint 检查:node <skill>/scripts/lint.mjs icons/svg。
6. 预览并查看,然后迭代
node <skill>/scripts/preview.mjs icons/svg --out icons/preview.png \
--sidebar "dashboard:Dashboard,kanban:Projects" --active dashboard # 首次使用时添加 --install(如果没有 PNG 引擎)
使用你的图片查看工具按以下顺序查看 PNG:
icons/[email protected]:图标集条和侧边栏模拟图。它足够小,可以查看全部细节。icons/[email protected]:每个图标在不同尺寸下的展示,以及检查视图。它很高,如果查看器缩放显示请裁剪。icons/preview.png:真正的 1× 像素。
它们共同展示:
- 24/16px 明暗背景对比条;
- 带活动项和折叠侧栏的侧边栏模拟图;
- 每个图标在 16/20/24/48 尺寸下的行;
- 4× 检查视图,包含网格、蓝色关键线和红色 1px 边距限制。
回答设计规则 §12 中的自审问题。典型修复方案:
| 你看到的问题 | 修复方法 |
|---|---|
| 某个图标在条中显得更轻/更小 | 放大到其关键线,或添加框架/轴线(开放字形视觉上较轻) |
| 某个图标看起来更重/更拥挤 | 移除细节;≤3 条线,≤6 个齿,≤2 行文字 |
| 部件在 16px 处交汇处模糊 | 精确连接或将间隙打开至 ≥2 单位 |
| 检查视图中形状偏向一侧 | 重新居中,或为光学平衡微调 0.5–1 单位 |
| 折叠侧栏中隐喻不清晰 | 选择更直观的隐喻(步骤 2) |
重复步骤 3-6,直到第二次查看时找不到任何需要修复的问题。通常需要 1-3 轮。
7. 生成框架输出
node <skill>/scripts/build.mjs icons/svg --out src/components/icons --framework react --lang ts \
--labels "kanban:Projects,dashboard:Dashboard"
--framework react|vue|svelte|html|all;--lang ts|js;--package @app/icons 会写入一个 sideEffects:false 的 package.json。输出为每个图标一个可 tree-shaking 的模块、一个共享基础组件(支持 size、color、strokeWidth、absoluteStrokeWidth、title、className)、index 命名导出,以及 icons.json。HTML 输出提供 sprite.svg + usage.html。详情参见 references/output-formats.md。
然后对项目进行类型检查或构建(如果它有相应脚本:npx tsc --noEmit、npm run build),并修复任何错误。
8. 通过无障碍用法接入
使用 references/accessibility-and-usage.md 中的模式:
<nav aria-label>;aria-current="page"以及活动项的颜色和背景;- 图标使用
size-5 shrink-0且为装饰性; - 仅图标按钮设置
aria-label;退出按钮作为<button>; - 行高 ≥44px;使用仍能达到 3:1 对比度的柔和颜色。
输出格式
最后给用户一份简短报告:
- 图标: 表格
标签 → 组件 → 隐喻以及文件夹路径(icons/src、icons/svg、生成目录)。 - 检查: lint 结果(错误/警告)、优化总计(
平均字节/图标、gzip 总计),以及你检查过的预览路径,还有你查看后修改的内容。 - 使用代码片段(针对用户的框架),包含无障碍属性。
- 备注: 风格决策(匹配的网格/笔触/预设)、需要镜像的 RTL 图标,以及你无法验证的任何内容。
示例
用户:“我的 Next.js + Tailwind 应用需要侧边栏图标:仪表盘、项目、团队、设置。”
→ detect:React (Next) + TS + Tailwind,无图标库 → plan:仪表盘/磁贴、看板、用户、设置/齿轮 → 从 cookbook 绘制并调整 → lint PASS → optimize:4 个图标,平均 ~340 B,~0.4 KB gzip → 预览:项目图标看起来轻了,所以添加横条;重新检查 → build --framework react 到 src/components/icons → 在导航中使用 <DashboardIcon className="size-5 shrink-0" /> 并设置 aria-current。
参考文档
| 当你需要……时打开 | 阅读 |
|---|---|
| 选择尺寸、关键线、半径、间隙、隐喻,或修复预览问题 | references/design-rules.md |
| 编写或调试 SVG 标记,或理解 lint 错误 | references/svg-code-rules.md |
| 从经过验证的几何形状开始绘制常见 UI 图标(56 个已绘制字形) | references/glyph-cookbook.md + assets/cookbook/*.svg |
| 生成或手动适配 React/Vue/Svelte/Angular/HTML 输出,或减小打包体积 | references/output-formats.md |
| 编写使用代码片段:导航/侧边栏模式、激活状态、标签、对比度、RTL | references/accessibility-and-usage.md |
| 更改生成组件的形状(props、类名、无障碍默认值) | templates/<framework>/,build.mjs 会读取;在此处编辑,绝不在输出中编辑 |
| 测试对此技能的更改 | evals/evals.json |
验证清单(报告完成前必须全部为真)
- 未添加任何图标库;每个图标均为本次任务绘制或从 cookbook 改编而来。
- 整组图标的风格与项目现有图标(或默认预设)匹配。
-
lint.mjs在icons/src和icons/svg上返回 PASS;剩余警告已在报告中说明理由。 - 你在最后一次修改后查看了预览 PNG;16px 明暗背景行清晰;没有图标比同级图标更重、更轻或更大。
- 组件由
build.mjs生成;项目类型检查/构建通过(如果可用)。 - 使用方面:装饰性图标已隐藏,仅图标控件已标记,激活状态不单独依赖颜色,目标区域 ≥44px,对比度 ≥3:1。
- 报告包含尺寸信息(平均字节/图标、gzip 总计)和预览路径。