1. 项目缘起:为什么我们需要一个叫“impeccable”的东西
第一次看到“impeccable”这个词,是在一个前端群里有人甩了张截图,说“这玩意儿生成的界面比我手写的还干净”。截图里是一个命令行工具跑完之后的输出,终端里滚过几行日志,然后浏览器自动弹出一个页面,布局规整、间距舒服、配色克制,连 hover 状态的过渡曲线都调得恰到好处。当时我的第一反应是:又一个 AI 生成 UI 的玩具吧。但点进仓库翻了翻 README,发现事情没那么简单。
“impeccable”这个项目,本质上是一个面向 AI coding agents 的前端设计约束层。它不是一个独立的代码生成器,也不是一个 Figma 插件,而是一套通过 CLI 和 browser extension 双通道介入的规则引擎。你可以把它理解成给 AI 编程助手戴上一副“设计眼镜”——当 Codex CLI、Claude Code 或者其他 agent 在帮你写前端代码时,impeccable 会在中间拦一道,用一套预定义的设计 token、布局约束和交互规范去校正输出结果。
它解决的问题很具体:现在用 AI 写前端,代码能跑,但丑。不是那种一眼假的丑,而是“说不上哪里不对但就是不舒服”的丑。间距忽大忽小、颜色饱和度飘忽、圆角半径不统一、阴影层级混乱、响应式断点拍脑袋定。你让 agent 改,它改完这处崩那处,因为 agent 没有全局设计系统的概念。impeccable 的做法是把这个设计系统外置成一份可执行的配置文件,agent 每次生成代码前先读这份配置,生成后再由 CLI 做一轮静态校验,browser extension 则在预览环节实时标注偏差。
适合谁用?如果你是一个人写全栈、用 AI 辅助出活、对界面质量有要求但不想花三天调 CSS 的独立开发者,这个工具能省你大量返工时间。如果你是小团队里唯一的前端,后端同事用 agent 生成的页面直接提 PR 到你面前,impeccable 可以当一道自动化的设计门禁。但如果你追求的是完全自由的艺术化表达,或者项目本身有成熟且复杂的设计系统已经沉淀在组件库里,那它的约束反而可能碍事。
我花了大概两周时间,在自己的三个 side project 里试了不同配置,踩了一些坑,也摸出了一些文档里没写的门道。下面把这些东西拆开讲。
2. 核心机制拆解:impeccable 到底在哪个环节动手
2.1 三层架构:CLI 做校验,extension 做标注,agent 做执行
impeccable 的架构不复杂,但分工很明确。最底层是一份impeccable.config.json,里面定义了设计 token 和约束规则。中间层是两个入口:CLI 和 browser extension。最上层才是 AI coding agent。
CLI 的职责是“事后校验”。当你用 Codex CLI 或者类似的工具生成完代码后,跑一条impeccable check ./src,它会扫描你的 CSS、Tailwind 类名、内联样式,甚至 JSX 里的 style 对象,然后对照配置文件输出一份偏差报告。报告里会标出哪些地方用了不在 token 表里的颜色值、哪些 margin 不在允许的间距阶梯上、哪些组件的圆角半径偏离了预设。
browser extension 的职责是“实时标注”。装完之后打开本地开发服务器,页面上会浮出一层半透明的标注层,把违反约束的元素用虚线框圈出来,鼠标悬停能看到具体是哪条规则被触发了。这个在调试响应式布局时特别有用,因为很多问题在代码里看不出来,一渲染就露馅。
agent 这一层不是 impeccable 直接控制的,而是通过 CLI 的--fix模式反向影响。当你跑impeccable check --fix时,它会尝试自动修正那些偏差,比如把#3b82f6替换成配置里定义的primary-500,把margin: 13px归整到最近的间距 token。修正后的代码再交给 agent 去继续迭代,就相当于给 agent 的输出做了一次“设计归一化”。
2.2 为什么选择配置文件驱动而不是插件化
我一开始觉得,为什么不做成 PostCSS 插件或者 Tailwind 的 preset?那样不是更原生吗?后来看了作者的 issue 回复才明白,配置文件驱动的核心优势是“跨工具链”。PostCSS 插件只能管 CSS,Tailwind preset 只能管 Tailwind 项目,但实际工作中一个项目里可能混着 CSS Modules、styled-components、甚至内联样式。impeccable 选择在 AST 层面做解析,不依赖具体样式方案,所以通用性更强。
另一个原因是 agent 的可读性。AI coding agent 读 JSON 配置文件比读 PostCSS 插件源码容易得多。你把impeccable.config.json放在项目根目录,agent 在生成代码前可以先读一遍,知道这个项目的设计边界在哪里。这比让 agent 去理解一套复杂的插件规则要高效。
注意:配置文件里的 token 命名尽量用语义化的词,比如
surface-raised、text-muted,不要用gray-100、blue-500这种。语义化命名能让 agent 更好地理解使用场景,减少误用。
2.3 与主流 agent 工具的协作方式
目前 impeccable 对 Codex CLI 的支持最完整,因为 Codex CLI 的--context参数可以直接把配置文件注入到 prompt 里。具体做法是在项目根目录建一个.impeccable/context.md,里面写清楚设计系统的核心规则,然后在跑 Codex CLI 时加上--context .impeccable/context.md。这样 agent 每次生成代码都会带着设计约束。
对于其他 agent 工具,比如那些通过浏览器扩展介入的,impeccable 的 browser extension 可以独立工作。你在浏览器里打开 agent 生成的预览页面,extension 会自动读取项目里的配置文件,然后标注偏差。这个流程不依赖 agent 本身是否支持上下文注入,算是兜底方案。
我实测下来,Codex CLI 加 context 注入的方式效果最好,因为约束在生成阶段就生效了,返工率最低。browser extension 标注的方式适合 review 阶段,能发现一些生成时没注意到的细节问题。
3. 从零开始:impeccable 的安装与基础配置
3.1 安装 CLI 与 browser extension
CLI 的安装走 npm 全局装就行:
npm install -g impeccable-cli装完之后跑impeccable --version确认一下。如果提示命令找不到,检查一下 npm 全局 bin 目录是不是在 PATH 里。Windows 上有时候需要手动加一下%APPDATA%\npm。
browser extension 目前只上了 Chrome Web Store 和 Firefox Add-ons。Chrome 的话直接搜 “impeccable devtools” 就能找到。装完之后在扩展管理页里把“允许访问文件网址”打开,不然本地开发服务器可能读不到配置文件。
提示:extension 需要读取项目根目录的
impeccable.config.json,所以你的开发服务器最好是从项目根目录启动的。如果是从子目录启动的,需要在 extension 的设置里手动指定配置文件路径。
3.2 初始化配置文件:一份可抄的模板
在项目根目录跑impeccable init会生成一份默认配置。但默认配置比较保守,我根据自己的使用习惯改了一版,下面这份可以直接抄:
{ "version": "1.0", "tokens": { "color": { "primary": { "value": "#2563eb", "usage": "主操作按钮、链接、焦点态" }, "primary-hover": { "value": "#1d4ed8", "usage": "主操作悬停态" }, "surface": { "value": "#ffffff", "usage": "卡片、弹层背景" }, "surface-raised": { "value": "#f8fafc", "usage": "页面背景、次级容器" }, "text": { "value": "#0f172a", "usage": "正文、标题" }, "text-muted": { "value": "#64748b", "usage": "辅助说明、占位符" }, "border": { "value": "#e2e8f0", "usage": "分割线、输入框边框" }, "danger": { "value": "#dc2626", "usage": "错误提示、删除操作" } }, "spacing": { "unit": 4, "scale": [0, 4, 8, 12, 16, 24, 32, 48, 64, 96] }, "radius": { "sm": "4px", "md": "8px", "lg": "12px", "full": "9999px" }, "shadow": { "sm": "0 1px 2px rgba(0,0,0,0.05)", "md": "0 4px 6px rgba(0,0,0,0.07)", "lg": "0 10px 15px rgba(0,0,0,0.1)" }, "typography": { "fontFamily": "Inter, system-ui, sans-serif", "scale": { "xs": "12px", "sm": "14px", "base": "16px", "lg": "18px", "xl": "20px", "2xl": "24px", "3xl": "30px" }, "lineHeight": { "tight": 1.25, "normal": 1.5, "relaxed": 1.75 } } }, "rules": { "no-arbitrary-values": true, "enforce-spacing-scale": true, "enforce-radius-scale": true, "max-colors": 12, "require-hover-state": true, "require-focus-visible": true }, "ignore": [ "node_modules/**", "dist/**", "*.test.*" ] }这份配置里几个关键点解释一下。spacing.unit设成 4,意思是所有间距必须是 4 的倍数,scale数组定义了允许的具体值。rules里的no-arbitrary-values会禁止margin: 13px这种不在 scale 里的值。require-hover-state会检查所有可交互元素是否定义了 hover 样式,这个在 agent 生成代码时特别容易漏。
3.3 把配置注入到 agent 的工作流
配置文件建好之后,下一步是让 agent 知道它的存在。以 Codex CLI 为例,在项目根目录建一个.impeccable/context.md,内容大概这样:
# 设计系统约束 本项目使用 impeccable 管理设计 token。所有样式必须引用 `impeccable.config.json` 中定义的 token。 ## 颜色 - 主色使用 `primary`,悬停用 `primary-hover` - 背景使用 `surface` 或 `surface-raised` - 正文使用 `text`,辅助文字使用 `text-muted` - 禁止使用配置外的颜色值 ## 间距 - 所有 margin、padding、gap 必须是 4 的倍数 - 优先使用 scale 中的值:4, 8, 12, 16, 24, 32, 48, 64 ## 圆角 - 小元素用 `sm`,卡片用 `md`,大容器用 `lg` - 禁止使用配置外的圆角值 ## 交互 - 所有可点击元素必须有 hover 态 - 所有可聚焦元素必须有 focus-visible 态然后在跑 Codex CLI 时:
codex --context .impeccable/context.md "帮我写一个用户列表页面,包含搜索框和分页"这样 agent 在生成代码时就会带着这些约束。我实测下来,加了 context 之后,agent 生成的代码第一次通过impeccable check的概率从大概三成提升到了七成左右。
4. 实操全流程:用 impeccable 约束一个真实页面
4.1 场景设定与初始生成
假设我要做一个后台的用户管理页面,包含顶部搜索栏、用户表格、底部分页。我先用 Codex CLI 生成一版不带约束的代码,看看 baseline 是什么水平。
codex "写一个用户管理页面,React + Tailwind,包含搜索框、表格、分页"生成出来的代码能跑,但问题很明显。搜索框的 padding 是px-3 py-2,表格单元格的 padding 是px-4 py-3,分页按钮的 padding 又是px-3 py-1.5。三个地方三种间距,视觉上就不整齐。颜色也飘,搜索框边框用了border-gray-300,表格分割线用了border-gray-200,分页按钮的禁用态用了text-gray-400。这些灰色值都不在配置的 token 表里。
跑一下impeccable check ./src,输出大概是这样:
src/components/UserTable.tsx 12:5 warning arbitrary spacing value: py-1.5 (not in scale) 18:9 warning color not in tokens: border-gray-300 24:7 warning color not in tokens: text-gray-400 31:3 warning missing hover state on interactive element4.2 用 --fix 做第一轮归一化
先跑impeccable check --fix ./src,让它自动修一轮。修完之后再看:
src/components/UserTable.tsx 12:5 fixed py-1.5 -> py-2 18:9 fixed border-gray-300 -> border-border 24:7 fixed text-gray-400 -> text-text-muted 31:3 manual missing hover state (cannot auto-fix)自动修复搞定了间距和颜色,但 hover 态需要手动加。这时候再让 agent 去补:
codex --context .impeccable/context.md "给 UserTable 里所有可点击元素加上 hover 态,参考配置文件里的 primary-hover"agent 补完之后再跑一次 check,应该就全绿了。
4.3 用 browser extension 做视觉复核
代码层面全绿不代表视觉上没问题。启动开发服务器,打开页面,然后点开 impeccable 的 extension 面板。它会列出当前页面上所有元素的约束遵守情况。
我遇到过一个情况:代码里所有间距都是 4 的倍数,颜色也都在 token 表里,但表格看起来就是挤。extension 标注出来说表格行的line-height是 1.25,而配置里normal是 1.5。原来 agent 在生成时用了leading-tight,这个类名不在我的规则检查范围内,因为enforce-spacing-scale只管 margin 和 padding,不管 line-height。后来我在配置里加了一条enforce-line-height规则,把这个问题也纳入了检查。
实操心得:extension 的标注层可以切换“仅显示违规”和“显示全部”。调试阶段用“显示全部”,能看到每个元素的 token 使用情况,方便发现一些隐性的不一致。上线前用“仅显示违规”,快速确认没有漏网之鱼。
4.4 把检查集成到 CI 流程
手动跑 check 容易忘,最好集成到 CI 里。在package.json里加一条 script:
{ "scripts": { "design:check": "impeccable check ./src --reporter json > design-report.json", "design:fix": "impeccable check --fix ./src" } }然后在 CI 配置里加一步npm run design:check,如果报告里有 error 级别的违规就阻断合并。warning 级别的可以先放行,但要在 PR 里贴出来提醒作者。
我自己的做法是:本地开发时用design:fix自动修,提 PR 前跑一次design:check确认没有 error。CI 里只跑 check 不跑 fix,因为 CI 环境不应该自动改代码。
5. 踩坑记录与排查技巧
5.1 配置文件不生效的几种常见原因
第一种情况是配置文件路径不对。impeccable 默认从当前工作目录往上找impeccable.config.json,如果你在子目录里跑命令,它可能找不到。解决办法是用--config参数显式指定:
impeccable check ./src --config ./impeccable.config.json第二种情况是 extension 没有读取到配置文件。Chrome extension 读取本地文件需要手动授权。在扩展详情页里找到“允许访问文件网址”并打开。如果还是不行,在 extension 的设置面板里手动填一下配置文件的绝对路径。
第三种情况是 agent 没有正确读取 context。Codex CLI 的--context参数只接受文件路径,不接受目录。如果你写的是--context .impeccable/,它会报错。必须写成--context .impeccable/context.md。
5.2 自动修复把代码改坏了怎么办
--fix模式本质上是做字符串替换和 AST 重写,大部分时候没问题,但有两种情况会翻车。
一种是动态类名。比如className={`px-${size} py-2`}这种模板字符串,fix 模式解析不了,可能会把整个表达式替换掉。遇到这种情况,在配置的ignore里加上对应的文件,或者用// impeccable-ignore-next-line注释跳过。
另一种是第三方组件的样式覆盖。比如你用了某个 UI 库的 Button 组件,它的内部样式不在你的控制范围内,但 fix 模式可能会去改它的类名。解决办法是在配置里加ignoreSelectors,把第三方组件的选择器排除掉。
注意:跑
--fix之前一定要确保代码已经提交到 git。万一改坏了,git checkout .就能回滚。我吃过一次亏,fix 完没检查就继续写,后来发现一个组件的间距全乱了,又花时间手动调回来。
5.3 agent 不遵守约束时的应对策略
有时候 agent 会忽略 context 里的约束,继续用配置外的值。这种情况通常是因为 context 太长,agent 的注意力被稀释了。解决办法是把 context 精简到最核心的规则,不要超过 500 字。另外可以在 prompt 里显式强调:“严格遵守 .impeccable/context.md 中的设计约束,不要使用配置外的颜色和间距值。”
如果还是不行,就在impeccable.config.json里把违规级别从 warning 调成 error,然后跑--fix强制修正。虽然多了一步,但至少能保证最终输出是合规的。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方式 |
|---|---|---|---|
| CLI 报 command not found | npm 全局 bin 不在 PATH | npm bin -g看路径 | 手动加 PATH 或重装 |
| extension 不显示标注层 | 未授权文件访问 | 扩展详情页检查权限 | 打开“允许访问文件网址” |
| 配置文件不生效 | 路径不对或文件名拼错 | impeccable config --show | 用--config显式指定 |
| agent 忽略约束 | context 太长或不够明确 | 检查 context.md 字数 | 精简到 500 字以内 |
| --fix 改坏代码 | 动态类名或第三方组件 | git diff看改动 | 加 ignore 或回滚手动改 |
| 检查通过但视觉不对 | 规则覆盖不全 | extension 标注层复核 | 补充规则到配置文件 |
6. 进阶玩法:把 impeccable 用出花来
6.1 多主题配置切换
一个项目可能有亮色和暗色两套主题。impeccable 支持通过--theme参数切换配置文件:
impeccable check ./src --theme dark对应的配置文件命名是impeccable.config.dark.json。你可以在里面定义暗色模式下的 token 值,比如surface从#ffffff变成#0f172a,text从#0f172a变成#f8fafc。agent 在生成暗色模式代码时,用--context .impeccable/context.dark.md注入对应的约束。
我自己的做法是把亮暗两套 token 放在同一个配置文件里,用themes字段区分,然后通过环境变量切换。这样 agent 只需要读一份文件,减少上下文长度。
6.2 与设计稿同步的自动化流程
如果你有 Figma 设计稿,可以用 Figma 的 API 把设计 token 导出成 impeccable 的配置格式。写一个简单的 Node 脚本:
const figmaTokens = await fetchFigmaTokens(fileId); const impeccableConfig = { version: "1.0", tokens: { color: Object.fromEntries( figmaTokens.colors.map(c => [c.name, { value: c.value, usage: c.description }]) ), spacing: { unit: 4, scale: figmaTokens.spacingScale } } }; fs.writeFileSync('impeccable.config.json', JSON.stringify(impeccableConfig, null, 2));这样设计稿更新之后,跑一下脚本就能同步配置,不用手动改 JSON。我大概每两周同步一次,保持代码和设计稿的一致性。
6.3 用 impeccable 做设计系统文档
impeccable.config.json本身就是一份设计系统的结构化描述。你可以写一个脚本把它渲染成 HTML 文档:
impeccable docs --output ./design-system.html生成的文档里会展示所有颜色、间距、圆角、阴影的预览,以及每个 token 的使用说明。这个给新加入的同事看特别方便,比口头讲或者翻代码快得多。
我还会在文档里加一栏“使用示例”,把每个 token 在实际组件里的应用截图放上去。这样设计师和开发对同一个 token 的理解能对齐,减少沟通成本。
6.4 约束的松紧度调节
impeccable 的规则不是越严越好。太严了 agent 生成代码时束手束脚,反而影响效率。我的经验是分阶段调节:
项目初期用宽松模式,只检查颜色和间距,让 agent 快速出原型。中期加上圆角和阴影检查,开始收敛视觉风格。后期加上 hover 态和 focus-visible 检查,做无障碍和交互细节的打磨。
配置里可以用severity字段控制每条规则的级别:
{ "rules": { "no-arbitrary-values": { "severity": "error" }, "enforce-spacing-scale": { "severity": "warning" }, "require-hover-state": { "severity": "warning" } } }error 级别的违规会阻断 CI,warning 级别的只提示不阻断。这样可以在不同阶段灵活调整。
7. 我对 impeccable 的一些个人判断
用了这段时间,我觉得 impeccable 最大的价值不是它现在能做什么,而是它代表了一种思路:把设计系统的约束从“人脑记忆”变成“机器可读的配置”。以前我们靠 code review 来保证设计一致性,但 review 的人会累、会漏、会有主观偏差。现在把规则写进配置文件,让工具去执行,人只需要在规则本身有争议的时候介入。
当然它也有明显的局限。目前的规则引擎还是基于静态分析的,对于复杂的响应式布局、动画时序、交互状态机,它管不了。agent 生成的代码在逻辑层面是否正确,它也不负责。它就是一个设计层面的 lint 工具,别指望它解决所有问题。
另外,配置文件的维护本身也是成本。token 表需要随着项目演进不断更新,规则太松没效果,太紧又碍事。我建议一开始只加最核心的几条规则,跑顺了再逐步加。别一上来就搞几十条规则,那样 agent 和你都会崩溃。
最后分享一个我常用的技巧:把impeccable check的输出重定向到一个文件,每次迭代后对比两次输出的差异。如果违规数量在减少,说明 agent 在逐渐适应约束。如果某次突然增多,可能是 context 注入出了问题,或者 agent 换了模型。这个简单的对比能帮你快速判断当前工作流是否正常。
这个工具还在快速迭代,我写这篇文章时的版本是 0.8.x,API 和配置格式可能还会变。建议关注仓库的 release notes,每次升级前先在小项目里试一下,确认没有破坏性变更再推到主项目。