关于 pixelpact
pixelpact 将网页或 Figma 画板提取为设计契约,并测量实现页面与契约的差异。它面向需要按属性检查和调整 UI 的前端开发者与编码 Agent。
主要功能
- 01从网页或 Figma 画板提取 JSON 契约
- 02测量渲染属性并报告偏差
- 03记录悬停、焦点、动画和设计令牌
- 04按像素比较截图
- 05通过 MCP 服务提供测量能力
- 06在 GitHub Actions 中运行检查
适用场景
- 01引导编码 Agent 逐步修正 UI
- 02按参考设计验证新页面
- 03在拉取请求中审查 UI 测量结果
- 04在 CI 中检查预览部署
MCP 相关
pixelpact-mcp 向 MCP 客户端提供契约提取、实现检查、像素差异比较和契约摘要工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
4收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| url | string | 是 | 要从中提取视觉契约的参考 URL。 |
| wait | integer | 否 | 导航后额外等待的稳定时间,单位为毫秒。默认为 2000。 |
| masks | array | 否 | 要从遍历中排除的 CSS 选择器,例如广告或时间戳。 |
| timeout | integer | 否 | 导航超时时间,单位为毫秒。默认为 30000。 |
| fullPage | boolean | 否 | 捕获整个可滚动页面,而不仅是视口。默认为 true。 |
| headless | boolean | 否 | 以无头模式运行浏览器。默认为 true。 |
| selector | string | 否 | 将遍历范围限定到指定的 CSS 选择器。默认为 document body。 |
| maxStates | integer | 否 | 要探测悬停和焦点状态的交互元素最大数量。默认为 120。 |
| viewports | array | 否 | 要捕获的视口。默认为桌面端 1440x900、平板端 768x1024、移动端 390x844。 |
| outputPath | string | 是 | 用于写入 contract JSON 的文件系统路径,例如 ./contracts/home.json。 |
| maxElements | integer | 否 | 遍历的最大元素数,0 表示不设上限。默认值为 600。 |
| screenshotDir | string | 否 | 用于保存参考截图的目录。后续要让 diff_pixels 正常工作,此项为必填。 |
| freezeAnimations | boolean | 否 | 测量前冻结 CSS 动画。默认值为 true。 |
详细介绍
编码 Agent 写好 CSS 后便宣告完成,却从未看到最终效果。没有任何东西向它提供数据,因此工作循环无法闭合:Agent 猜测,你打开页面,再把结果反馈给它,它又猜一次。每一轮都耗费了 Agent 本该为你节省的唯一资源。
没有 Agent 时,也存在同样的问题:队友说某个区块已完成,却没有人测量过它。区别在于,至少人可以亲眼看看。
pixelpact 会读取参考页面,并记录实际渲染出的内容:尺寸、颜色、间距、排版、悬停和焦点状态、动画关键帧、设计令牌。这个文件就是契约。让 pixelpact 检查实现,它会针对每个发生偏差的属性返回一行结果,这正是 Agent 可以据此采取行动的内容。
npx pixelpact extract https://reference.example.com -o contract.json
npx pixelpact check contract.json http://localhost:3000
适用对象
| MCP 客户端 | Playwright | GitHub Actions | 任意框架 | Figma |
|---|---|---|---|---|
| Claude Code、Cursor,以及任何支持 stdio MCP 的工具 | pixelpact 驱动的引擎 | 在拉取请求中留下一条评论 | 它读取 DOM,而非你的技术栈 | 可按需将一个画板用作参考 |
只要浏览器能够渲染,pixelpact 就能测量。
面向编码 Agent
编写 UI 代码的 Agent 无法判断自己是否成功。pixelpact-mcp 为它提供测量数据,让工作循环无需人工介入即可闭合:只需提取一次契约,然后让 Agent 检查自己的工作、读取偏差列表、修复问题,再次检查。
// .mcp.json
{
"mcpServers": {
"pixelpact": { "command": "npx", "args": ["-y", "pixelpact-mcp"] }
}
}
提供的工具:extract_contract、check_implementation、diff_pixels、
read_contract_summary。
为什么这不是视觉回归测试
Percy、Chromatic、Applitools、BackstopJS 和 Pixeleye 都会将页面与先前经你批准的基线进行比较。这种模式适用于页面已经符合预期、你只想让它保持原样的情况。但在你仍在按设计构建页面时,并没有可供比较的基线;回归工具首次运行时,只会记录你做出的页面。
| 视觉回归工具 | pixelpact | |
|---|---|---|
| 比较对象 | 先前批准的快照 | 参考设计本身 |
| 适用场景 | UI 已经正确 | 正在构建 UI |
| 新页面首次运行 | 只记录,无法评判 | 根据参考设计进行测量 |
| 返回结果 | 需要人工检查的图像差异 | 每个属性的值及其差异 |
| 适合自主 Agent 使用 | 需要人工批准差异 | 数据让工作循环得以闭合 |
这两种模式互为补充。使用回归工具让已完成的页面保持完成状态,并用 pixelpact 让页面真正达到完成状态。
pixelpact 解决的问题
| 不使用 pixelpact | 使用 pixelpact |
|---|---|
| ❌ 编码 Agent 写好 CSS,宣布完成,然后还得有人打开页面,才能发现页面其实不符合要求。 | ✅ Agent 调用 MCP 服务,读取偏差,进行修复,然后再次检查。整个过程不需要人工介入。 |
| ❌ 有人说某个部分已经完成,你不同意,但你们谁都拿不出数据。最后谁的声音大就听谁的。 | ✅ 参考中的每个值都会与页面进行比对。失败项会列出预期值、实际值和差值。 |
| ❌ 回归工具没有新页面可供比对,因此首次运行时只能把你碰巧做出的页面记录下来。 | ✅ 从一开始就以参考页面为基准。工具无需等待审核通过就能发挥作用。 |
| ❌ 几乎没人会审核悬停、焦点和动画的值,因为手动检查既慢又枯燥。 | ✅ 它们属于约定的一部分,因此每次运行都会像其他值一样接受测量。 |
| ❌ 图像差异会告诉你有东西变了,却让你自己去找具体变化。 | ✅ 偏差会指出元素、属性、预期值和测量值。 |
| ❌ 设计存在一个代码审查时没人会打开的文件里。 | ✅ 约定以 JSON 形式提交,因此拉取请求会清楚显示哪些值发生了变化。 |
安装
pnpm add -D pixelpact playwright
pnpm exec playwright install chromium
playwright 是一个同级依赖,因此浏览器的下载由你掌控,而已经安装 Playwright 的项目无需安装任何额外内容。需要 Node 22.12 或更新版本。
快速入门
1. 从参考页面提取约定。
npx pixelpact extract https://reference.example.com \
--selector "main" \
--viewport desktop,mobile \
--screenshots .pixelpact/shots \
-o contract.json
2. 测量你的实现。
npx pixelpact check contract.json http://localhost:3000 --viewport desktop
3. 修复报告的问题,然后再次运行。 当所有值都在容差范围内时,命令会以 0 退出,否则以 1 退出,因此可以直接用于脚本或 CI 任务。
约定示例
约定是纯 JSON,易于阅读和比较,没有专有格式,也不依赖任何服务。
{
"version": 1,
"source": { "type": "url", "value": "https://reference.example.com" },
"root": "main",
"extractedAt": "2026-09-06T01:20:44.812Z",
"viewports": [{ "name": "desktop", "width": 1440, "height": 900 }],
"tokens": { "--brand-600": "rgb(11, 114, 133)" },
"keyframes": { "fade-up": [{ "offset": "0%", "css": "opacity: 0" }] },
"byViewport": {
"desktop": {
"documentHeight": 4218,
"elements": [
{
"selector": "main > header > a.cta",
"tag": "a",
"text": "Get started",
"box": { "x": 120, "y": 32, "w": 148, "h": 44 },
"styles": {
"background-color": "rgb(11, 114, 133)",
"font-size": "16px",
"border-radius": "8px"
},
"hover": { "background-color": "rgb(8, 90, 105)" },
"focus": { "outline": "2px solid rgb(11, 114, 133)" }
}
]
}
}
}
因为它是一个文件,你可以提交它、在拉取请求中审查它、交给另一位开发者,或交给 Agent。
检查输出示例
pixelpact check FAILED
target http://localhost:4173/impl.html
reference http://localhost:4173/ref.html
viewport desktop 1440x900
elements 14 matched, 0 missing of 14
checks 1056 passed, 10 failed (99.1% of 1066)
deviations (10)
SELECTOR PROPERTY EXPECTED ACTUAL DIFF
body > main > h1 font-size 48px 44px 4px
body > main > a box.width 117.75px 109.75px 8px
body > main > a padding-right 24px 20px 4px
body > main > a padding-left 24px 20px 4px
body > main > a background-color rgb(11, 114, 133) rgb(37, 99, 235) 60.1 (color)
body > main > a border-top-left-radius 8px 4px 4px
body > main > a border-top-right-radius 8px 4px 4px
body > main > a border-bottom-left-ra... 8px 4px 4px
body > main > a border-bottom-right-r... 8px 4px 4px
body > main > a focus.outline rgb(11, 114, 133) s... rgb(37, 99, 235) so... differs
这是对同一页面的两个副本进行的实际运行,其中四条声明被修改。四处编辑产生了十项测量偏差,因为内边距会改变盒子的宽度,而一个 border-radius 简写属性会设置四个角。对参考页面本身运行相同的检查,全部 1066 项断言都会通过,这才是关键:检查通过必须有实际意义。
添加 --json,即可将相同报告作为数据结构获取,CI 任务和 Agent 会读取这种格式。
并排运行的效果
check 会指出哪些值发生了变化。diff 会指出变化了多少像素。但两者都不会告诉人该看哪里。side 会将两个页面拆分为多个部分,并排放置,并用方框标出差异。
npx pixelpact side https://reference.example.com http://localhost:3000 --widths 1440,390
# SECTION WIDTH VERDICT DIFF
01 hero 1440px PASS 0.000%
02 features 1440px FAIL 0.675%
.pixelpact/side/1440/02-features.png
03 pricing 1440px PASS 0.000%
04 foot 1440px FAIL 1.265%
.pixelpact/side/1440/04-foot.png
![]()
这是对 examples/side 中两个文件进行的实际运行。这两个文件是同一页面的副本,其中卡片间距和圆角半径有所改动。运行以下命令即可复现:
npx pixelpact side "file://$PWD/examples/side/reference.html" \
"file://$PWD/examples/side/implementation.html" --widths 1440
在告诉任何人页面已经完成之前,应先运行此命令。
功能
| 以参考页面为准的契约 读取你要构建的目标页面,并记录它渲染的每个值。无需先行审批。 | 用数字说话,而非主观判断 每项偏差都包含预期值、测量值以及两者之间的差值。 | 关注状态,而不只是布局 交互元素会经过悬停和聚焦状态处理,并且只存储实际发生变化的属性。 |
| 设计令牌和关键帧 根元素上的自定义属性和命名动画关键帧都会包含在契约中。 | 像素对比 当所有值都通过检查但页面看起来仍不对时,对比截图并获取差异百分比。 | 适用于 Agent MCP 服务会提供相同的测量结果,让编码 Agent 能够自行完成闭环。 |
| 拉取请求检查 复合 Action 会运行检查,并更新拉取请求中的单条评论。 | 纯 JSON 契约是一个可读取、对比和审查的文件。无需服务、无需账户,也无需登录。 | 与框架无关 它测量渲染后的 DOM,因此 React、Vue、Svelte 和手写 HTML 对它来说都一样。 |
命令
| 命令 | 功能 |
|---|---|
pixelpact extract <url> | 读取参考页面并写入契约文件 |
pixelpact check <contract> <url> | 测量实现结果、打印偏差并设置退出代码 |
pixelpact diff <contract> <url> | 与契约中存储的截图进行像素对比 |
pixelpact side <reference> <url> | 按区块并排显示图片,并用方框标出差异 |
通用标志涵盖浏览器上下文(--viewport、--selector、--wait、--timeout、
--locale、--timezone、--channel、--headful)、输出(--out、--json、
--quiet),以及可限制的提取内容(--max-elements、--max-states、
--mask)。运行 pixelpact <command> --help 查看完整列表。
退出代码
| 代码 | 含义 |
|---|---|
0 | 所有内容都在容差范围内 |
1 | 发现偏差,或超出像素阈值 |
2 | 用法错误,例如标志无效或缺少契约文件 |
3 | 运行时故障,例如没有可用的浏览器或页面无法加载 |
编程式使用
import { extract, check, formatCheckReport, writeContract } from 'pixelpact-core'
const contract = await extract({
url: 'https://reference.example.com',
selector: 'main',
screenshotDir: '.pixelpact/shots',
})
await writeContract('contract.json', contract)
const report = await check(contract, { url: 'http://localhost:3000' })
console.log(formatCheckReport(report, { color: true }))
if (!report.ok) process.exitCode = 1
所有内容都有类型定义,并且契约和报告结构会在边界处经过验证,因此格式错误的文件会显示易读的错误消息,而不是堆栈跟踪。
在 CI 中使用
Action 会根据仓库中提交的契约测量预览部署,并持续更新拉取请求中的单条评论,而不是每次推送都新增一条。
- uses: jamalkamaladdin/pixelpact/action@v0
with:
contract: contract.json
url: ${{ steps.preview.outputs.url }}
viewport: desktop
tolerance: 1
它需要 permissions: pull-requests: write,除了自动提供的
GITHUB_TOKEN 外,不需要其他密钥。所有输入、输出和完整工作流都列在
action/README.md 中。
读取 Figma 画框
extract 会识别 Figma URL,并通过 REST API 读取画框。此步骤不会启动浏览器。
export FIGMA_TOKEN=figd_...
npx pixelpact extract "https://www.figma.com/design/KEY/Name?node-id=12-345" -o contract.json
npx pixelpact check contract.json http://localhost:3000
Figma 图层没有 CSS 选择器,因此 Figma 契约通过 data-contract 属性与标记绑定。将元素命名为对应图层的名称,匹配就不会再受设计中画框嵌套方式的影响:
<a class="btn btn-primary" data-contract="Hero/CTA">Get started</a>
没有匹配项的内容会报告为缺失,而不会根据标签名进行猜测。
已发布的样式会作为令牌传入:颜色样式会作为颜色,文本样式会作为 CSS 字体简写(例如 600 60px/72px Geist),效果样式会作为盒子阴影。渐变或图像填充没有单一的 CSS 值,因此不会纳入,而是列在警告中,不会进行近似处理。
工作原理
- Playwright 会在每个请求的视口中打开参考页面,等待字体、网络和绘制稳定下来,并关闭 Cookie 覆盖层。
- 页面内会执行一个函数。它会遍历根选择器下的 DOM,记录每个可见元素的计算样式,以及自定义属性、关键帧和每个盒子的几何信息。
- 可交互元素会被悬停并聚焦,只存储实际发生变化的属性,让契约保持小巧易读。
- 检查时会针对你的实现重复步骤 2,先按
data-contract属性匹配元素,再按选择器匹配,最后按标签和文本匹配,并逐项比较属性。长度使用像素容差,颜色使用感知距离,动画则按名称和时序比较,而非按字符串是否相等来比较。
仓库结构
| Package | Published as | What it is |
|---|---|---|
packages/core | pixelpact-core | 提取、检查和报告 |
packages/cli | pixelpact | pixelpact 命令 |
packages/mcp | pixelpact-mcp | 面向编码 Agent 的 MCP 服务 |
action | 用于 GitHub | 用于拉取请求检查的 Action |
常见问题
Agent 如何使用它? 安装 pixelpact-mcp,将 MCP 客户端指向它,提取一次契约,然后让 Agent 在每次编辑后调用 check_implementation,并查看返回的偏差表。
这与 Percy 或 Chromatic 有何不同? 它们会将你的页面与之前批准的快照进行比较,这意味着页面已经正确。而 pixelpact 会将页面与参考进行比较,这正是你在构建过程中实际拥有的内容。两者可以配合使用:用 pixelpact 让页面达到正确状态,再用回归工具保持这种状态。
我需要基线吗? 不需要。参考就是基线,这正是它的意义所在。
我的标记与参考结构不匹配。还能匹配吗? 元素匹配会先尝试 data-contract 属性,然后是选择器,最后是标签和文本。在你的元素上添加 data-contract="hero-cta",匹配就不会再受参考页面中 div 嵌套方式的影响。
页面内容每次加载都会变化。检查还能通过吗? 将它屏蔽。--mask ".carousel" 会将某个区域排除在像素比较之外,而 --max-elements 则可以避免长页面生成难以阅读的契约。
支持哪些浏览器? 通过 Playwright 使用 Chromium。刻意不支持跨引擎运行矩阵:此工具衡量的是与设计的一致性,而非浏览器之间的差异。
检查通过是否意味着页面正确? 这表示契约中的每个值都在容差范围内匹配。你的页面中存在、但参考中没有的元素不会被标记,因为契约只描述参考中包含的内容。如果不允许出现任何额外内容,请同时运行 diff。
状态
以上所述功能均已实现,展示的每个数值都来自真实运行:从实际网页和 Figma 提取数据、执行检查、生成像素差异、并排对比图片,以及 MCP 服务和 Action。
版本 0.3。已稳定到可以使用,但还不足以作出承诺:在 1.0 之前,契约格式还会增加字段。如果其中缺少你需要的值,请提交 issue 并说明是哪一个,因为这是让它尽快加入的最快方式。
贡献
开发环境设置、相关命令和拉取请求流程见 CONTRIBUTING.md。安全问题请通过 SECURITY.md 报告。
许可证
MIT © Jamal Kamaladdin
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。