全部技能 / 设计 / frontend-visual-qa
设计 · daymade/claude-code-skills

frontend-visual-qa

Audits already-rendered web, landing-page, HTML deck/slide, browser tool/game, dashboard/admin, design-system, and desktop UIs using real-browser or native-app journeys, inspected screenshots, DOM geometry, responsive or projection viewports, and a bundled Playwright sweep. Use after UI implementation to find typography, wrapping, overlap, overflow, responsive, route, overlay, map, transient-state, data-visualization, browser-output, file-dialog, PDF/print, or Electron-shell defects, or to compare a rendered artifact with a visual reference. Do not use for greenfield UI design, extracting a design system from screenshots, general QA-program setup, or nonvisual code debugging.

风险提醒:橙色 · 评估后使用AI 侦查报告
作者 daymadeGitHub daymade/claude-code-skills ↗Stars 1385许可 MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)commit d5c4678cb5
agent 宿主通常会约束 skill 执行权限;风险提醒为 AI 侦查观点,不构成质量或安全保证。第三方 skill 仅作拆解与展示,安装使用风险自负,版权归原作者。

1实现原理 · 为什么它能做到

整个 skill 的骨架是「渲染证据分级制」:A=真实可见浏览器/原生窗口驱动,B=同态 DevTools 或项目自有 E2E,C=自带脚本的全新无头机械扫描,D=源码/lint/build/单测;并显式禁止把低层级证据升格成强结论。自带 Playwright 扫描被明确定位为 Level C,不是最终裁决。

frontend-visual-qa/SKILL.md
| C | Fresh headless mechanical sweep from the bundled script | Overflow, wrapping, images, basic layout heuristics, screenshot candidates |
注:紧接其后是 'Do not promote a lower level into a stronger conclusion:' 与六条禁止升格的例子(未打开过的截图路径没有视觉证据价值、无头空态不能验证用户的填充态、renderer URL 不能验证 Electron 外壳、handler 调用不能验证 Chrome 打印预览、指针/hover 样式不能证明点击有效)。这把「怎么算看过」写成了可判定的规则,而不是凭 agent 自述。

自带脚本刻意不带任何 npm 依赖:Playwright 从**被审计项目**的 node_modules 解析,先用 createRequire(被审计项目/package.json),再回落到 skill 自身目录;项目里没有 Playwright 时不是去安装,而是降级并如实上报。

frontend-visual-qa/scripts/visual_layout_audit.mjs
createRequire(`${process.cwd()}/package.json`), // the audited project
注:上方注释写明跨安装布局的坑:pnpm workspace 下 playwright-core 是 @playwright/test 的非提升传递依赖、不在顶层 node_modules,因此候选顺序为 playwright-core / playwright / @playwright/test。找不到时抛出的错误文本直接写着 'Do not change the audited project's dependencies unless that mutation is explicitly authorized.' ——零依赖是设计意图,不是疏漏。probe 脚本同策略(先找 <项目>/node_modules/playwright/index.js,再回落 import('playwright'),仍失败则 exit 2 并提示可跳过该探测。

扫描不是静态分析:整个 auditPage 检测函数被 page.evaluate 注入真实 Chromium 页面内执行,用 getBoundingClientRect / scrollWidth-clientWidth / getComputedStyle / checkVisibility 在真实排版引擎上取值后回传结构化 issues。

frontend-visual-qa/scripts/visual_layout_audit.mjs
const result = await page.evaluate(auditPage, {
注:调用侧先 chromium.launch({headless:true})(executablePath 由 findChrome() 在 Chrome/Chromium/Edge 候选路径中解析,可被 CHROME_PATH 覆盖),按默认三档视口矩阵(desktop-wide 1700x1000、desktop 1440x900、mobile 390x844 + deviceScaleFactor 2 + isMobile + hasTouch)逐档 newPage → goto → 截图 → evaluate。所以「为什么能实现」的答案是:把缺陷判定下沉到浏览器真实布局引擎,而不是解析 HTML 猜。

「文字有没有真的渲染出来」是逐字符判定的:visibleTextFromNode 用 Range 把每个字符单独取框,再逐层减去祖先 overflow:hidden/clip 的裁剪矩形,被裁掉的字符替换为空格;因此 --forbid/--require 检查的是真实可见文本,不是 textContent。

frontend-visual-qa/scripts/visual_layout_audit.mjs
range.setStart(node, index);
注:配套的 rectSurvivesClips 与 collectNonScrollableAncestorClips 做矩形求交;isVisible 优先用 el.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true }),不支持时回落到 display/visibility/opacity 判断。这是它区别于「读 DOM 文本」类检查最关键的一处。

溢出与裁切是数值计算而不是「列出可疑元素」:元素级 overflowX = el.scrollWidth - el.clientWidth,页面级用 documentElement.scrollWidth - clientWidth,区块级另算 scrollWidth-clientWidth 与 rect.width - documentElement.clientWidth 两个不同口径,超阈值才产出带 px 数值的条目。

frontend-visual-qa/scripts/visual_layout_audit.mjs
const overflowX = el.scrollWidth - el.clientWidth;
注:同族项:page-horizontal-overflow(>1px,error)、element-horizontal-overflow(>2px 且 overflowX!==visible,error)、text-clipped(overflowY>2 且 overflowY 为 hidden/clip 且无 line-clamp/ellipsis,error)对 text-truncated(有 line-clamp/ellipsis,降为 warning)、section-horizontal-overflow、section-wider-than-viewport。阈值与 severity 都是显式常量。

媒体缺陷按状态拆分而不是通过/失败:selected / rendered / notRendered / loaded / broken / stillLoading / deferredLazy / missingSource,并把「首屏外 + lazy + 尚未请求」单独归入 deferredLazy,避免把没请求当成坏图。

frontend-visual-qa/scripts/visual_layout_audit.mjs
const isDeferred = !mediaWalkRequested
注:条件为 !mediaWalkRequested && !inViewport && isLazy && (!img.complete || naturalWidth === 0 || !source)。--scroll-visible-media 会先走一遍渲染出的媒体行预热懒加载;不加该 flag 时报告记 deferred coverage 而不记缺陷。另有 image-aspect-mismatch(object-fit 非 cover/contain/scale-down 且显示比与自然比差 >0.08,error)与 image-heavy-crop(cover 且差 >0.25,warning)。

找「静默降级」用可证伪的测量而不是信任 API 自述:font 模式渲染含拉丁字符的探针串,与一个故意不存在的字体族宽度做差;document.fonts.check() 只作对照输出(它在完全没有 @font-face 时也返回 true)。

frontend-visual-qa/scripts/silent_degradation_probe.mjs
const sentinel = measure('"NoSuchFamily__PROBE__"');
注:探针串是 'Handgloves 0123456789 幅',文件头注释解释为什么必须含拉丁字符:CJK 在任何字体里都是全角,全 CJK 探针测不出替换。测量前会 await document.fonts.load(...) 与 document.fonts.ready,否则「项目内置但本页未用」的字体看起来与从未发布的一模一样。

font 模式给五种互斥结论(platformGeneric / healthy / hostProvidedOnly / declaredButUnusable / absentStackEntries),只把 hostProvidedOnly 与 broken asset 判为缺陷——因为把「内置且正常」与「从未发布」混为一谈,会导致用户删掉本来可用的 fallback。

frontend-visual-qa/scripts/silent_degradation_probe.mjs
const hostOnly = custom.filter((f) => f.actuallyRenders && !f.shippedByPage);
注:actuallyRenders = |宽度 - 哨兵宽度| > 0.5;shippedByPage = document.fonts 里存在同名 face。退出码:hostOnly 或 brokenAsset 命中即为 1,两者皆无则 0。

class 模式检测「组件库大版本改名后整组 CSS 规则永不匹配」这类不报错的降级:从项目 CSS 抽出被引用的 .ant-* 类名,逐个到安装的库 CSS 里查是否存在,零命中即 dead。

frontend-visual-qa/scripts/silent_degradation_probe.mjs
Rules targeting them match nothing and fail silently — the component keeps the library's stock look.
注:两侧 CSS 都先过 stripCssComments(逐字符状态机,尊重引号与转义),因此注释里的旧类名不会造成假阳性、注释里写着的类名也不会造成假阴性——tests/test_silent_degradation_probe.mjs 第一个用例正好覆盖这两个方向。脚本自身也提示:selector group 里若另一分支仍能匹配,死类名不构成代价,需人工确认后再改。

证据纪律被写进代码而不只写在提示词里:报告中的 URL 一律结构化脱敏(只留 origin + /<redacted-path> + 查询键名 + #<redacted-fragment>),并把目标相关的敏感 token 从渲染文本中打码;header 只能经环境变量间接传入,原样 --header 在启动浏览器之前就被拒绝。

frontend-visual-qa/scripts/silent_degradation_probe.mjs
process.stderr.write('--header is disabled; use --header-env for every header value\n');
注:visual_layout_audit.mjs 侧另有 redactUrlForEvidence / protectRenderedEvidence / targetSensitiveTokens(把 URL 的 path、query 值、密码片段逐个从页面文本抹成 <redacted-target-value>),targetEvidence 对 file: 目标算 contentSha256、对 web 目标只留 targetStringSha256 指纹与查询键名列表。这种默认封闭的证据处理在同批 skill 里少见。

2核心能力

01真实浏览器 Level C 机械扫描:三档默认视口矩阵(1700x1000 / 1440x900 / 390x844@DPR2+isMobile+hasTouch)逐档截图并落 JSON 报告,退出码 0/1/2(干净 / 存在 error 或 --fail-on-warning 下的 warning / 输入或运行失败)
02几何与排版缺陷计算:页面/区块/元素三级横向溢出、文本裁切与有意截断的区分、孤立标题行、控件被折行、短表头折行、中文断行边界不当、图片与浮层文字碰撞、卡片左侧彩条或内阴影候选(card-side-rail-ai-slop)
03可访问性机械候选:可见交互 role 元素不可聚焦、aria-hidden 的可见交互元素、焦点环被 outline:none 抹掉且无 :focus/:focus-visible 替代(error)、完全没有 focus 规则(warning)、页面有动画但无 prefers-reduced-motion 分支
04移动端有效视口核验:meta viewport 缺失、innerWidth 或 visualViewport.width 相对请求宽度偏差超过 24px、scale 偏离 1 超过 0.08 均判 error,防止「名义移动端其实是缩放过的桌面布局」
05媒体状态取证与懒加载预热:五态分类、--scroll-visible-media 访问渲染出的媒体行以触发懒加载、图片纵横比/object-fit/重裁剪判定、--media-selector 限定媒体范围
06页面类型契约启发式:landing 首屏标题与主行动、dashboard 任务面存在性、app/tool/game 交互存在性、deck 幻灯片结构与画布尺寸一致性、design-system 六大信号、live-artifact 框架与交互密度、类型漂移与卡片密度
07静默降级探测三模式:font(哨兵字体宽度证伪字体是否真渲染)、class(项目 CSS 引用的类名在已安装库 CSS 中是否已死)、shot(按元素裁剪截图作为发现自带证据,尺寸以 CSS px 上报而非设备像素)
08注意力清单 attentionInventory:统计第一屏浅层可见文本节点、完全重复文本组、同容器内的标签回声,并显式输出 necessityVerdict: "not_evaluated"——只让文本总体可检查,绝不认证内容必要性

3外部依赖

类型依赖
packagePlaywright(playwright-core / playwright / @playwright/test 三选一,从被审计项目解析)
cli本机 Chrome / Chromium / Edge 可执行文件(findChrome 的候选路径,可用 CHROME_PATH 覆盖)
network被审计目标 URL 及其子资源(唯一网络目的地,由调用者经 --url 指定)
cliNode.js 运行时与内置模块(node:crypto/fs/os/path/url/module);自带回归用 node --test

4风险提醒 风险提醒:橙色 · 评估后使用

风险提醒:橙色 · 评估后使用
  • 凭证与登录态:--header-env 会把环境变量里的值作为请求头发给目标 origin,--storage 会读入 Playwright 登录态文件(cookie/localStorage)。 — 默认不触发,需调用者显式写出 flag;但如果把生产 token 放进环境变量或复用真实账号的 storage-state 去审生产站,凭证就会随审计请求离开本机。用前确认目标站与账号范围。
  • TLS 降级开关存在:probe 的 --ignore-https-errors 会关闭该上下文的证书校验。 — SKILL.md 只承诺『TLS stays enabled except for an explicitly trusted self-signed origin』,而代码里同一开关可用于任意 URL;自签本地环境以外不要用,且要清楚这只在自建 context 内降级。
  • 模型侧提示注入面没有防护。 — 脚本不吃页面内容当代码,但审计 agent 会把渲染文本、标题、DOM 摘要读进上下文;被审计页面(尤其是第三方或用户提供的 HTML)可以埋针对 agent 的指令文本。审陌生人给的页面时该项风险实际由宿主与 agent 的通用防护承担,本 skill 未加过滤。
  • 正则入口无回溯上限。 — --forbid/--require 由调用者提供,validatePattern 只校验能否编译;灾难性回溯的模式会让 page.evaluate 长时间挂住。属自伤面(输入来自自己),但在 CI 里可能表现为超时而非报错。
  • --out 未被限制在临时区。 — 脚本只要求该目录不存在或为空,因此把 --out 写进被审计项目就真的会写进去;『不要为了跑扫描去改被审计项目』目前只是提示词层纪律。
  • 证据为本地敏感物且默认不脱敏图片像素。 — 报告里的 URL 与渲染文本做了脱敏,但截图 PNG 是原样像素(可能含客户邮箱、订单号等);SKILL.md 明确要求 raw 截图只当临时本地证据、对外交付须做最小化裁剪衍生件,这一步依赖使用者自觉。
风险提醒:橙色,评估后使用。判定依据(触发条件已标注):默认路径(只给一个本地 URL)很温和——本机 headless Chromium 访问调用者指定的目标、写临时目录,不外发第三方。但代码里确实具备四类需评估的能力:① 凭证从环境变量读取并**主动发给目标 origin**(--header-env,值不进 argv);② 读取磁盘上的 Playwright 登录态文件(--storage,内含 cookie/localStorage)并注入上下文;③ 依赖第三方包 Playwright(从被审计项目解析,不随 skill 携带);④ probe 提供 --ignore-https-errors 关闭该上下文的 TLS 校验(SKILL.md 的自述是 TLS 默认开启、仅对明确信任的自签 origin 例外——即降级要求调用者显式写 flag)。另注意:脚本本身不做任何写操作,默认 audit-only,且不重启/不部署目标;它不会读 cookie 数据库、keychain 或 .env。若只用于本地 fixture/本地 dev server 且不使用凭证类 flag,实际接触面接近蓝色。

5第二遍独立确认

  • [ok] external_deps 逐条反查调用点是否真实存在 — grep 工具全量扫 scripts/:Playwright 候选数组(playwright-core / playwright / @playwright/test)、findChrome 的 process.env.CHROME_PATH、page.goto(url, { waitUntil, timeout: 30_000 })、SKILL.md 的 node --test 四处均逐字定位到文件行;网络 token 扫描只命中两处——probe 的 route.fetch({ headers: requestHeaders, maxRedirects: 0 }) 与 visual_layout_audit.mjs 用法字符串里的 http://localhost:port/ 示例,无第三方端点,无 axios / XMLHttpRequest / WebSocket / child_process / new Function。
  • [discrepancy] 安全结论反例搜索:是否存在未披露的外发/降级/隐藏脚本 — 主体结论(无第三方外发、无遥测、默认只读)成立,但发现两处 SKILL.md 未在安全语境点明的代码级能力:① probe 经 route.fetch 把 --header-env 提供的凭证以请求头形式发到目标 origin(值取自 process.env,不进 argv),文档只强调『argv 只含名字不含值』,未说『值会被转发给目标站』;② probe 有 --ignore-https-errors(ignoreHTTPSErrors: has('ignore-https-errors')),会关闭该上下文的 TLS 校验——SKILL.md 只在 sweep 段落自述『TLS stays enabled except for an explicitly trusted self-signed origin』,未提 probe 侧同一开关。两者都是显式 opt-in、需调用者自己写出 flag;未发现混淆内容、后门或隐藏脚本。
  • [ok] 凭证读取与文件写入面逐条复核 — credential_reads 三条、file_writes 三条均落到代码行:process.env[envName](probe)、storageState: flag('storage') || undefined、findChrome 的 CHROME_PATH/PROGRAMFILES/LOCALAPPDATA;写入侧为 outDir(默认 os.tmpdir()/frontend-visual-qa-<ISO>-<pid>)、frontend-visual-qa-report.json、截图 PNG。脚本不读 cookie 数据库、不读 keychain、不读 .env,也无删除行为。
  • [ok] 文件清单 glob 全量核对(含隐藏文件) — find -type f 得 24 个文件:SKILL.md 1 + scripts 3 + tests 2 + references 7 + evals/evals.json + evals/trigger-evals.json + evals/files 13(12 HTML + static-review.png);find -name ".*" 在 skill 目录内为空(无隐藏文件、无 .env、无嵌套仓库)。与 SKILL.md『Bundled Resources』清单一一对应,没有未列出的可执行文件。
  • [ok] 『实现原理』是否夸大:功能声明 vs 实际代码能力 — SKILL.md 称自带脚本为 'Playwright-powered mechanical viewport, layout, and media-state sweep with screenshots and JSON evidence',代码确为 chromium.launch + 三档视口 + 截图 + JSON 报告;report.checked 列的七项(navigation status and final URL、effective viewport and meta viewport、page and section overflow、rendered text wrapping and clipping candidates、custom interactive-role focusability candidates、rendered image request/load state and aspect candidates、page-type contract heuristics)在 auditPage 内逐条可定位到具体计算与 push。反向也不夸大:脚本在 report.notChecked 里主动列出做不到的项(真实浏览器 chrome 与打印预览、下载/剪贴板/弹窗/原生对话框、键盘旅程与焦点外观、完整 WCAG、GIS 交互、主观 parity),自述边界严于对外宣称。
  • [discrepancy] SKILL.md 关于『报告只写进唯一临时运行目录』的措辞与代码是否一致 — 默认路径确实唯一(os.tmpdir()/frontend-visual-qa-<ISO 时间戳>-<pid>,且拒绝非空 outDir 以防陈旧证据),但 --out 允许任意路径,脚本层不限制它落在被审计项目内;SKILL.md 的『Do not mutate the audited project merely to run the sweep』只是提示词层约束,代码不强制。属措辞与实现的轻微落差,非缺陷。
  • [ok] 注入面复核:被审计页面内容是否会进入可执行路径 — 四个注入浏览器的函数体全部来自 skill 自带文件(auditPage / collectAttentionInventory / primeVisibleMedia / collectScreenshotSections),页面内容只作为数据被 Range 测量、正则匹配、结构描述,不参与代码执行;无 eval、无 new Function、无 child_process,也没有把页面文本拼进 argv/shell 的路径(probe 的 --select 只交给 page.locator)。残余风险在模型侧(页面可埋针对审计 agent 的指令文本,本 skill 无过滤)与用户输入侧(--forbid/--require 正则无回溯上限)。
  • [ok] evals/ 的定位:是否确为自带评测夹具,能否在本地复跑出分数 — evals/evals.json 是 21 条 prompt + expectations 的行为回归集(12 条带 fixture 文件),evals/trigger-evals.json 是 24 条 should_trigger 路由集(14 true / 10 false),schema 与仓库内 daymade-skill/skill-creator/references/eval_pipeline_schemas.md 一致,且 skill-creator 的打包逻辑默认排除 root evals/(自述 'excluded by default as a development asset'),与 SKILL.md 的『excluded from packaged runtime content』吻合。但 skill 目录内没有 runner/评分器,仓库 .github/workflows/ci.yml 与 benchmark.yml 也未引用本 skill 的 tests 或 evals(grep 无命中),因此这些用例目前只在手动执行时生效。

6结论

  • 把「视觉 QA」从主观描述变成可复核的证据链:先写可证伪的 scope contract,再按 A–D 证据分级取证,并明令禁止把弱证据升格成强结论(未打开的截图、无头空态、renderer URL 都被逐一点名不能支撑对应结论)。
  • 真实计算而非清单输出:溢出用 scrollWidth-clientWidth 的 px 差值、裁切用逐字符 Range 与祖先裁剪矩形求交、图片用显示比与自然比的偏差,阈值与 severity 都写死;附带 --require/--forbid 与退出码 0/1/2,可直接接进 CI 或 agent 循环。
  • 专攻「一切门禁都绿但像素是坏的」这一类:silent_degradation_probe 的 font 模式用哨兵宽度差值证伪字体是否真加载(并明确 document.fonts.check() 会假阳性),class 模式找库改名后永不匹配的规则,且五判定里只有两种算缺陷——避免让用户删掉健康的 fallback。
  • 默认只读且权限粒度极细:source fix / interaction / target mutation 三种授权互不可推导,脚本不重启不部署、不安装依赖、不改被审计项目;证据落盘前做结构化脱敏(URL 只留 origin+路径占位+查询键名,# 片段整段打码)。
  • 自带评测夹具是刻意构造的负样本(溢出页、死点击画廊、700ms 消失的错误条、陈旧下载/空分享链接),把「这一刻门禁该怎么判」沉淀成 21 条行为用例 + 24 条路由用例,而不是只留一份说明文档。
  • 适合:适合「UI 已经跑起来、需要有人真的看一眼」的团队:前端/设计系统维护者在合并前做一次渲染态巡检;QA 想给『构建通过但页面不对』的缺陷补机械证据;以及需要对 Electron/原生窗口、打印/PDF、下载/分享、地图、deck/幻灯片这类浏览器输出做真实路径验证的场景。也适合把 visual_layout_audit.mjs 当 CI 卡口(本地 dev server 或 fixture + 退出码),或把 silent_degradation_probe 的 font/class 两模式加进设计系统回归。对中文界面尤其有价值(CJK 断行边界与中英混排的语义分类)。
    不适合:不适合需求阶段或从零设计 UI(description 明说不做 greenfield UI design)、不适合从截图反向抽取设计系统(那是设计 skill 的活)、不适合搭整套 QA 程序与发布门禁(应交给 qa-expert)、也不适合非视觉的代码调试(hydration 报错定位之类)。此外它不做完整 WCAG 认证、不做主观品味裁决、不做 GIS 交互行为验证、不代替人工对参考图的主观对齐;没有 Playwright 的项目里只能退到 Level A/B,自带脚本会直接跳过而不是替你装依赖。
    安装 agent 直装可复制
    ① 本站镜像 更新 2026-09-15
    方式 A · 人下载镜像包下载 frontend-visual-qa.tar.gz
    sha256: 48ff3a56b28549a2…
    方式 B · JSON 格式安装指南,复制给 agent
    安装指南
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② 上游 GitHub · 原始来源
    能访问 GitHub?直接去上游安装(实时版,可能已更新)GitHub 原始 ↗
    本页镜像锁定 commit d5c4678cb5;上游为实时仓库。
    来源信息 GitHub 原始
    作者 / 仓库daymade / daymade/claude-code-skills
    Stars1385
    最近推送2026-09-09
    本 skill commitd5c4678cb5
    许可MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)
    本站信息
    收录日期2026-09-06
    分类设计
    侦查报告AI 侦查 · 2 遍 · 2026-09-06
    本站镜像与 GitHub 原始是不同来源:本站锁定 commit 快照经 /r2 分发;GitHub 为实时上游,内容可能已更新。
    同分类邻近