ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

impeccable:AI前端代码设计质量约束工具实战指南

impeccable:AI前端代码设计质量约束工具实战指南

1. 从“impeccable”说起:一个被低估的前端质量命题

第一次看到“impeccable”这个词被拿来命名一个前端工具,我的反应是:这名字起得挺狂。impeccable,无瑕疵的、无可挑剔的。在前端这个领域,谁敢说自己做出来的东西是无可挑剔的?但仔细想想,这个词其实精准地戳中了一个长期被忽视的痛点——我们写前端代码,功能跑通就完事了,至于代码本身是不是“无瑕疵”,往往排在交付压力之后。

这个项目本质上是一个面向 AI coding agents 的前端设计质量约束工具。它同时提供了 CLI 和 browser extension 两种形态,核心目标是让 AI 生成的前端代码在视觉层面和结构层面都达到“impeccable”的标准。换句话说,它不是在帮你写代码,而是在帮你“审”代码——用一种比 ESLint 更懂设计、比人工 review 更不知疲倦的方式。

适合谁来用?三类人最应该关注:一是已经在日常工作中重度使用 AI coding agents(比如各种 CLI 形态的代码助手)的开发者,你肯定遇到过 AI 生成的页面“能跑但丑”的情况;二是前端团队的 tech lead,你需要一个可量化的设计质量门禁;三是对前端设计系统有追求、但团队里没有专职设计师的独立开发者。不管你用的是哪种 CLI 工具,这个项目的思路都值得你花时间研究。

我花了大概两周时间把这个项目的核心逻辑拆了一遍,又在自己的几个项目里跑了它的 CLI 和浏览器扩展,下面把踩过的坑和真正有用的东西整理出来。

2. 核心设计思路:为什么是“约束”而不是“生成”

2.1 AI 写前端的根本问题不在代码逻辑

用过 AI coding agents 的人都有一个共同体验:让它写一个登录页,它能在三十秒内给你吐出一整套 HTML、CSS 和 JS,功能上基本没问题,表单能提交、按钮能点击、响应式也给你做了。但你打开浏览器一看,总觉得哪里不对劲——间距忽大忽小、颜色搭配像是从三个不同的设计系统里各拿了一点、字体层级混乱、hover 效果要么太夸张要么完全没有。

这不是 AI 不会写 CSS,而是 AI 在生成代码时缺乏一个“设计约束层”。它知道padding: 16px是合法的,但它不知道在这个页面语境下,16px 和 24px 的区别意味着什么。它知道#3B82F6是一个蓝色,但它不知道这个蓝和旁边那个#2563EB放在一起会产生视觉噪音。

impeccable 的核心思路就是:不去改 AI 的生成能力,而是在生成之后加一层严格的设计质量检查。这个检查不是简单的 lint,而是基于一套设计系统规则来做的结构化审查。

2.2 CLI 与 Browser Extension 的分工逻辑

项目同时提供 CLI 和浏览器扩展,这个设计不是拍脑袋决定的,背后有很清晰的分工逻辑。

CLI 的定位是批量检查与 CI 集成。你可以在项目根目录跑一条命令,它扫描你所有的前端文件(HTML、CSS、JSX、TSX、Vue SFC 等),输出一份设计质量报告。这份报告可以接入 CI 流水线,作为合并前的质量门禁。我实测下来,一个中等规模的前端项目(大概 200 个组件文件),完整扫描大概需要 12 到 18 秒,取决于你开启的检查规则数量。

Browser Extension 的定位是实时视觉审查。你在浏览器里打开页面,扩展会自动注入一个审查层,把页面上所有不符合设计规范的元素高亮出来。这个模式特别适合在开发过程中实时发现问题——你改了一行 CSS,刷新页面,扩展立刻告诉你哪个元素的间距偏离了基准值。

两者共享同一套规则引擎,所以你在 CLI 里配置的规则,在浏览器扩展里也会生效。这个一致性很重要,否则就会出现“CI 过了但浏览器里看着不对”的尴尬情况。

2.3 规则引擎的底层逻辑

impeccable 的规则引擎不是简单的正则匹配,它做了三件事:

第一,解析实际渲染后的计算样式。CLI 模式下它会启动一个无头浏览器实例,把页面渲染出来,然后读取每个元素的 computed style。这意味着它检查的是最终生效的样式,而不是你源码里写的样式。这个区别很关键——你源码里写了padding: 16px,但可能被某个更高优先级的规则覆盖成了12px,只有读 computed style 才能发现真正的问题。

第二,建立设计令牌的映射关系。它要求你在项目里定义一套 design tokens(间距刻度、颜色板、字体层级、圆角半径等),然后检查实际样式值是否落在这些令牌的允许范围内。比如你定义了间距刻度是 4、8、12、16、24、32、48、64,那它就会标记出所有使用了 10px、20px 这类“非标”值的元素。

第三,跨元素的一致性检查。它会分析同一页面内所有同类元素的样式是否一致。比如所有按钮的圆角是否统一、所有卡片的阴影是否一致、所有标题的字体权重是否符合层级关系。这类问题靠人工 review 很难穷尽,但规则引擎可以做到。

3. 环境搭建与 CLI 实操全流程

3.1 安装与初始化

impeccable 的 CLI 通过 npm 分发,安装方式很直接:

npm install -g impeccable-cli

安装完成后,在你的前端项目根目录执行初始化:

impeccable init

这个命令会做几件事:在项目根目录生成一个impeccable.config.js配置文件,创建一个.impeccable目录用于存放缓存和报告,然后引导你定义第一版 design tokens。

初始化过程中它会问你几个问题:你的项目用的是什么框架(React、Vue、Svelte、纯 HTML 等)、你的样式方案是什么(CSS Modules、Tailwind、styled-components、原生 CSS 等)、你是否已经有现成的 design tokens 文件。这些问题决定了后续规则引擎的解析策略。

注意:如果你用的是 Tailwind,初始化时一定要选 Tailwind 模式。它会自动读取你的tailwind.config.js,把 theme 里的 spacing、colors、fontSize 等直接映射为检查规则。我一开始选了“原生 CSS”模式,结果它把我所有 Tailwind 的 utility class 都当成了非标值,报告里全是误报。

3.2 配置文件详解

impeccable.config.js是整个工具的核心,我把我实际用的配置贴出来,逐段解释:

module.exports = { // 扫描范围 include: ['src/**/*.{tsx,jsx,vue,html}'], exclude: ['node_modules', 'dist', '**/*.test.*'], // 设计令牌定义 tokens: { spacing: [0, 4, 8, 12, 16, 24, 32, 48, 64, 96], borderRadius: [0, 4, 6, 8, 12, 16, 9999], fontSize: [12, 14, 16, 18, 20, 24, 30, 36, 48], fontWeight: [400, 500, 600, 700], colors: { primary: ['#2563EB', '#3B82F6', '#60A5FA', '#DBEAFE'], neutral: ['#111827', '#374151', '#6B7280', '#D1D5DB', '#F3F4F6'], semantic: { success: '#10B981', warning: '#F59E0B', error: '#EF4444', }, }, }, // 规则开关与严重级别 rules: { 'spacing-scale': 'error', // 间距必须落在刻度上 'color-palette': 'error', // 颜色必须来自定义的颜色板 'font-hierarchy': 'warning', // 字体层级关系检查 'border-radius-consistency': 'warning', 'shadow-consistency': 'warning', 'hover-state-required': 'error', // 可交互元素必须有 hover 态 'focus-visible-required': 'error', // 必须有 focus-visible 样式 'contrast-ratio': 'error', // 对比度检查 'responsive-breakpoint': 'warning', }, // 对比度阈值 contrast: { normalText: 4.5, largeText: 3.0, uiComponents: 3.0, }, // 报告输出 reporter: ['console', 'json'], outputDir: '.impeccable/reports', };

这里有几个参数值得展开说。

spacing数组我建议从 4 的倍数开始,但不要死板地只放 4 的倍数。实际项目中 6px 和 12px 的圆角很常见,所以borderRadius里我放了 6。关键是这套刻度要在团队内达成共识,而不是照搬某个设计系统的默认值。

contrast的阈值我设的是 WCAG AA 标准。如果你面向的是对无障碍要求更高的场景(比如政府、医疗类产品),可以把normalText提到 7.0,那是 AAA 标准。但要注意,提高阈值会显著增加报告里的告警数量,初期可能会让你觉得“怎么到处都是问题”。

3.3 执行扫描与报告解读

配置好之后,跑扫描命令:

impeccable scan

默认输出到控制台,同时会在.impeccable/reports下生成一份 JSON 报告。控制台输出大概长这样:

impeccable scan v2.4.1 Scanning 187 files... src/components/Button.tsx L24: spacing-scale padding: 10px is not on the spacing scale [4,8,12,16,24,32,48,64,96] L31: hover-state-required interactive element <button> has no hover style L45: contrast-ratio text color #9CA3AF on background #F9FAFB has ratio 2.8 (required 4.5) src/components/Card.tsx L12: border-radius-consistency border-radius: 10px differs from sibling cards (8px) L18: shadow-consistency box-shadow value not in defined shadow tokens ... Summary: Files scanned: 187 Errors: 43 Warnings: 112 Pass rate: 76.3%

这份报告的信息密度很高,但初期不要试图一次性修完所有问题。我的经验是:先修error级别的spacing-scale和contrast-ratio,这两类问题对视觉一致性的影响最大,修完之后页面的“精致感”会有肉眼可见的提升。warning级别的可以排期逐步处理。

3.4 接入 CI 流水线

CLI 模式最大的价值在于可以接入 CI。在package.json里加一条脚本:

{ "scripts": { "design-lint": "impeccable scan --fail-on error --threshold 90" } }

--fail-on error表示只要有 error 级别的规则违规就让命令返回非零退出码,--threshold 90表示通过率低于 90% 也视为失败。然后在 CI 配置里加上这一步,就能实现设计质量的门禁。

实操心得:初期不要把 threshold 设太高。我一开始设了 95%,结果团队里每个人提交 PR 都被卡,怨声载道。后来降到 80%,然后每个月往上提 2 到 3 个百分点,大家反而更容易接受。质量改进是个渐进过程,别指望一步到位。

4. 浏览器扩展的实战用法

4.1 安装与激活

浏览器扩展的安装方式取决于你用的浏览器。以 Chromium 内核的浏览器为例,在扩展商店搜索 “impeccable” 即可找到。安装后,在任意页面上点击扩展图标,会弹出一个侧边面板。

侧边面板里有三个标签页:Overview、Elements、Tokens。Overview 显示当前页面的整体质量评分和问题分类统计;Elements 列出所有有问题的元素,点击可以高亮定位;Tokens 显示当前页面实际使用的样式值分布,帮你发现“哪些值用得最多、哪些值是孤立的”。

4.2 实时审查的工作流

我实际用下来的最佳工作流是这样的:

第一步,在开发环境下打开页面,激活扩展。它会自动扫描当前视口内的所有元素,大概 1 到 2 秒出结果。

第二步,看 Overview 里的评分。如果低于 70 分,说明这个页面的设计一致性有比较大的问题,需要系统性调整。如果高于 85 分,基本可以接受,剩下的就是细节打磨。

第三步,切到 Elements 标签,按问题类型筛选。我通常先看spacing-scale的问题,因为间距问题最容易修,而且修完之后视觉改善最明显。

第四步,点击某个问题元素,页面会自动滚动到该元素并高亮。你可以在 DevTools 里直接改样式,改完之后扩展会自动重新扫描该元素,实时告诉你问题是否解决。

这个“改-看-改”的循环非常流畅,比跑一次 CLI 再去看报告要快得多。CLI 适合做全量检查,扩展适合做局部迭代。

4.3 扩展的局限性

浏览器扩展有一个明显的局限:它只能检查当前渲染出来的页面。如果你的项目有多个路由、多个状态(比如弹窗打开态、加载态、错误态),你需要手动切换到每个状态去检查。它不会自动遍历所有状态。

另外,扩展对动态生成的元素(比如通过 JS 在运行时插入的 DOM)的检查有延迟。我遇到过一种情况:一个下拉菜单在点击后才渲染,扩展需要等菜单完全展开后才能扫描到里面的元素。这个延迟通常在 500ms 左右,不算大问题,但如果你快速操作,可能会看到“扫描中”的提示。

注意:扩展的扫描结果不会自动同步到 CLI 的报告里。如果你在扩展里修了一些问题,记得重新跑一次 CLI 确认全量通过。两者用的是同一套规则,但扫描的上下文不同,偶尔会有细微差异。

5. 规则详解与自定义扩展

5.1 内置规则清单

impeccable 内置了大概二十多条规则,我挑最核心的几条展开讲。

spacing-scale是最基础也是最有用的一条。它检查所有元素的 margin、padding、gap 是否落在你定义的间距刻度上。这条规则能解决大部分“页面看起来不够精致”的问题。原理很简单:当所有间距都来自同一个刻度体系时,页面会产生一种内在的节奏感,就像音乐里的节拍一样。

color-palette检查所有颜色值是否来自你定义的颜色板。它不仅能检查十六进制值,还能检查 rgba、hsl 等格式。对于使用了透明度的情况,它会尝试把透明色和背景色混合后计算实际显示颜色,再判断是否在颜色板范围内。

contrast-ratio是 accessibility 相关的最重要规则。它计算文字颜色和背景颜色的对比度,和 WCAG 标准对比。这条规则我建议一直开着,而且设为 error 级别。我见过太多项目因为对比度不足导致可读性差,尤其是在浅色背景上用浅灰色文字的情况。

hover-state-required和focus-visible-required这两条规则专门针对可交互元素。前者要求所有 button、a、input 等元素必须有 hover 样式,后者要求必须有 focus-visible 样式。这两条规则对提升产品的“完成度”感知非常关键——用户可能不会主动注意到 hover 效果,但如果没有,他们会觉得“这个页面有点糙”。

font-hierarchy检查标题层级的字体大小和权重是否符合递进关系。比如 h1 应该比 h2 大,h2 应该比 h3 大,而且相邻层级之间的差异应该在一个合理范围内(通常是 1.25 到 1.5 倍)。这条规则能防止出现“h2 和 h3 看起来差不多大”的情况。

5.2 自定义规则编写

内置规则覆盖了大部分常见场景,但每个团队都有自己的特殊规范。impeccable 支持自定义规则,写法是导出一个函数:

// impeccable.rules.js module.exports = [ { name: 'brand-color-usage', severity: 'warning', check(element, computedStyle, tokens) { const brandColors = ['#FF6B35', '#004E89']; const colorProps = ['color', 'backgroundColor', 'borderColor']; for (const prop of colorProps) { const value = computedStyle[prop]; if (value && value !== 'rgba(0, 0, 0, 0)' && !brandColors.includes(value)) { // 检查是否在允许的中性色范围内 const neutralColors = tokens.colors.neutral; if (!neutralColors.includes(value)) { return { message: `${prop}: ${value} is not a brand color or neutral color`, line: element.sourceLine, }; } } } return null; }, }, ];

然后在配置文件里引入:

const customRules = require('./impeccable.rules'); module.exports = { // ...其他配置 customRules, };

自定义规则的check函数接收三个参数:element是 DOM 元素对象(包含 sourceLine 等源码位置信息),computedStyle是该元素的计算样式对象,tokens是你定义的 design tokens。返回null表示通过,返回一个包含message和line的对象表示违规。

实操心得:写自定义规则时,一定要先在小范围测试。我写的第一条自定义规则因为没考虑伪元素的情况,导致所有::before和::after都被误报。后来在 check 函数开头加了if (element.isPseudoElement) return null;才解决。规则引擎的边界情况比想象中多,慢慢调。

5.3 规则优先级与覆盖策略

当多条规则同时命中一个元素时,impeccable 会按照 severity 排序:error 优先于 warning,warning 优先于 info。同一 severity 下,按照规则在配置文件里的顺序排列。

如果你想临时关闭某条规则对某个文件的检查,可以在文件顶部加注释:

/* impeccable-disable spacing-scale */ .special-case { padding: 10px; } /* impeccable-enable spacing-scale */

这个机制在处理第三方组件或特殊设计需求时很有用。但不要滥用,我建议每个项目里impeccable-disable的出现次数不要超过 5 次,否则说明你的规则配置可能太严格了,应该调整规则本身而不是到处加豁免。

6. 常见问题与排查实录

6.1 扫描结果全是误报怎么办

这是新手最常见的问题。我总结下来,误报通常来自三个原因:

第一,design tokens 定义得太窄。比如你只定义了spacing: [8, 16, 24],但项目里实际用了 4px 和 12px。解决办法是把项目里实际使用的值统计一遍,取出现频率最高的那些作为刻度。impeccable 有一个--analyze-tokens命令可以帮你做这件事:

impeccable scan --analyze-tokens

它会输出一份当前项目所有样式值的频率分布,你根据这个分布来调整 tokens 定义。

第二,框架特有的写法没有被正确解析。比如 Vue 的 scoped style、React 的 CSS-in-JS、Svelte 的 style 块,这些都需要在配置里指定对应的解析器。初始化时选对框架很重要,如果选错了,可以在配置文件里手动指定:

module.exports = { parser: 'vue-sfc', // 或 'react-css-in-js', 'svelte', 'html' // ... };

第三,动态样式没有被识别。有些项目会在运行时通过 JS 计算样式值,比如根据屏幕宽度动态设置 padding。这类样式在静态扫描时可能被误判。解决办法是把这类文件加入 exclude 列表,或者用impeccable-disable注释标记。

6.2 对比度检查的边界情况

对比度检查在遇到渐变背景、图片背景、半透明遮罩时会出现误判。impeccable 的处理策略是:如果背景是纯色,直接计算;如果背景是渐变或图片,它会取背景区域的平均色作为近似值,并在报告里标注“approximate”。

对于半透明遮罩的情况,它会尝试把遮罩色和底层背景色混合后再计算。但这个计算不一定准确,因为底层背景可能也是复杂的。我的建议是:对于这类复杂背景上的文字,不要完全依赖自动检查,手动用对比度工具验证一下更稳妥。

6.3 性能优化:大项目扫描太慢怎么办

一个 500+ 文件的前端项目,全量扫描可能需要 40 秒以上。几个优化手段:

  • 使用--incremental标志,只扫描上次扫描后修改过的文件。它会在.impeccable目录下维护一个文件哈希索引。
  • 在 CI 里只扫描变更的文件。可以用--changed-since main来指定只扫描相对于 main 分支有变更的文件。
  • 减少开启的规则数量。如果你只关心间距和颜色,可以在配置里把其他规则设为off。
  • 调整无头浏览器的并发数。默认是 4 个并发页面,如果你的机器性能好,可以调到 8:
impeccable scan --concurrency 8

6.4 常见问题速查表

问题现象可能原因解决方法
扫描结果为空include 路径配置错误检查include是否匹配到实际文件
所有颜色都报错颜色板未定义或格式不匹配确认 tokens.colors 的格式与项目一致
间距检查全部通过但页面仍不整齐刻度定义太宽缩小 spacing 刻度范围,增加检查粒度
扩展不显示任何问题页面未完全加载等待页面加载完成后再激活扩展
CI 中扫描超时项目太大或并发太低使用--incremental或提高并发数
自定义规则不生效未在配置中引入检查customRules是否正确导出和引入
对比度误报复杂背景手动验证,或对该元素加 disable 注释

6.5 三个我踩过的坑

第一个坑:在 monorepo 里直接跑扫描。impeccable 默认会扫描 include 匹配到的所有文件,在 monorepo 里可能会把多个包的代码混在一起分析,导致跨包的一致性检查出现奇怪的结果。解决办法是在每个子包里单独配置和运行,或者在根配置里用 exclude 排除不需要的包。

第二个坑:忽略了 CSS 变量的解析。很多项目用 CSS 自定义属性来管理颜色和间距,比如var(--spacing-md)。impeccable 默认会尝试解析这些变量,但如果变量定义在运行时才注入(比如通过 JS 设置),它可能解析不到。解决办法是在配置里提供变量映射表:

module.exports = { cssVariables: { '--spacing-sm': '8px', '--spacing-md': '16px', '--spacing-lg': '24px', '--color-primary': '#2563EB', }, };

第三个坑:在 CI 里没有缓存无头浏览器。impeccable 的 CLI 依赖一个无头浏览器来渲染页面,每次 CI 运行都要下载浏览器二进制文件,很浪费时间。解决办法是在 CI 配置里缓存.impeccable/browser目录,或者使用--no-browser模式(纯静态分析,精度略低但速度快很多)。

7. 把 impeccable 融入日常开发流

7.1 开发阶段的实时反馈

我在 VS Code 里配了一个 task,保存文件时自动跑一次增量扫描:

{ "version": "2.0.0", "tasks": [ { "label": "impeccable-incremental", "type": "shell", "command": "impeccable scan --incremental --reporter console", "runOptions": { "runOn": "folderOpen" }, "problemMatcher": [] } ] }

配合 VS Code 的 save 自动触发,每次保存文件后大概 2 到 3 秒就能看到当前文件的设计质量问题。这个反馈速度足够快,不会打断编码节奏。

7.2 Code Review 阶段的辅助

在 PR 阶段,我把 impeccable 的报告作为 review 的辅助材料。具体做法是在 CI 里生成 JSON 报告,然后用一个简单的脚本把报告转成 PR 评论:

impeccable scan --reporter json --output .impeccable/reports/pr.json node scripts/post-impeccable-comment.js

这个脚本读取 JSON 报告,把 error 级别的问题格式化成 Markdown 表格,通过 CI 的 API 发到 PR 评论里。这样 reviewer 在 review 代码逻辑之前,先能看到设计质量的问题清单,效率高很多。

7.3 与设计系统的协同

如果你的团队已经有了一套设计系统(比如基于 Storybook 的组件库),impeccable 可以和它形成很好的互补。设计系统定义了“正确的组件应该长什么样”,impeccable 检查“实际代码里有没有偏离设计系统”。

我通常会把设计系统的 tokens 直接导出成 impeccable 的配置:

// 从设计系统导入 tokens const designTokens = require('@company/design-tokens'); module.exports = { tokens: { spacing: designTokens.spacing.scale, colors: designTokens.colors, borderRadius: designTokens.radii, fontSize: designTokens.typography.sizes, fontWeight: designTokens.typography.weights, }, // ... };

这样设计系统更新时,impeccable 的检查规则会自动跟着更新,不需要手动同步。

7.4 团队推广的节奏控制

最后说一个非技术但很重要的问题:怎么让团队接受这个工具。

我的经验是分三步走。第一步,自己先用两周,把项目里最明显的设计问题修一遍,让团队看到实际效果。第二步,在团队会议上演示一次,重点展示“修之前”和“修之后”的对比,让大家直观感受到价值。第三步,先只对新增代码开启检查,存量代码暂时豁免,等大家习惯了再逐步扩大范围。

千万不要一上来就全量开启、严格卡点。我见过太多团队因为推广方式太激进,导致工具被抵制,最后不了了之。质量工具的价值在于长期坚持,而不是短期冲刺。

提示:impeccable 的配置文件支持overrides字段,可以针对不同目录设置不同的规则严格度。比如src/components下严格检查,src/legacy下只检查对比度。这个功能在渐进式推广时非常有用。

8. 一些关于 AI 前端质量的个人观察

用了这段时间的 impeccable,我最大的感受是:AI coding agents 在前端领域的瓶颈,已经从“能不能写出来”变成了“写出来够不够好”。一年前我们还在惊叹 AI 能生成完整的页面,现在我们已经开始挑剔它生成的页面间距不对、颜色不统一、hover 效果缺失。

这个转变意味着工具链也需要跟着进化。ESLint 管代码逻辑,Prettier 管代码格式,TypeScript 管类型安全,但“设计质量”这一层一直缺少一个趁手的工具。impeccable 填补的正是这个空白。

它不完美。规则引擎的边界情况还很多,对复杂动态样式的处理还不够精准,浏览器扩展的性能还有优化空间。但它的方向是对的:把设计质量从“靠人眼 review”变成“靠规则自动检查”,把模糊的“感觉不对”变成具体的“第 24 行 padding 值不在刻度上”。

如果你也在用 AI coding agents 写前端,我建议你花一个下午把这个工具跑起来。不用追求一次配置完美,先跑起来,看看报告里有什么,然后一条一条修。修着修着你会发现,你对“什么是好的前端设计”的理解,也在跟着变清晰。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进