Skip to content
dsh.fish
Bundle

dsh-design-audit

DeepSeek Harness 的 UI/UX 审查与优化闭环:无头 Chrome 取证 → 确定性判据(WCAG 2.2 / 平台指南)→ 可粘贴 CSS 修复 → 复测差值验证

Source
gongyijie85
License
MIT
Updated
Updated 5 hours ago

Readme

# dsh-design-audit

DSH 的 **UI/UX 审查与优化闭环**:截图取证 → 确定性设计判据 → CSS 修复 → 复测验证。

## 为什么做这个(生态定位)

2026-09 的 DSH 插件生态调研结论:

| 方向 | 现状 |
|---|---|
| 设计**生成**(从 0 到 1) | 已被 [deepseek-design](https://github.com/Devin-AXIS/deepseek-design)(★978,含 Studio/模板市场)覆盖 |
| 截图 → UI **还原** | 已被 [agent-vision-toolkit](https://github.com/Anionex/agent-vision-toolkit)(★1195)覆盖 |
| 设计知识清单 | uckkk 系列 5 个插件,0–2★(纯参考、无工具化) |
| **审查与优化闭环** | **空白** ← 本插件做这个 |

不做生成器、不做还原器:只做"量出问题 → 给出可粘贴的修复 → 证明修好了"。

## 核心设计决定:测量与判据分离

```
页面内取证(collect.js)        判据与阈值(checks.js)
  · 几何 / 计算样式             · WCAG 条款号
  · 对比度比值(页面内算)       · 平台指南(44×44)
  · 可访问名称 / 遮挡命中测试    · 分级 + 具体 CSS 修复
  = 全是"量出来的"              = 全是"定下来的"
```

测量错、判据错可以分别审计,互不掩盖。

**机器只做可测量的判断,审美交给模型看图** —— 工具不越界假扮评审官:它把客观证据与截图路径摆好,把"视觉层级/一致性/品牌感/呼吸感"留给模型的视觉复核(报告里显式列为"未覆盖")。

## 三个工具

| 工具 | 作用 |
|---|---|
| `design_capture` | 无头 Chrome 打开 URL/本地 HTML,多视口整页截图 + 采集客观测量值。产物落 `<工作区>/.design-audit/<时间戳>/` |
| `design_review` | 跑确定性判据,出分级问题 + 逐元素 CSS 修复 + 自包含 HTML 报告;带 `compare` 时输出复测差值 |
| `design_fix` | 把问题转成可粘贴的 CSS 补丁(可选直接追加到样式文件,自动备份原文件) |

### 判据覆盖(13 组)

- **无障碍**:对比度(1.4.3)、焦点可见(2.4.7)、目标尺寸(2.5.8)、可访问名称(4.1.2)、图片替代文本(1.1.1)、表单标签(3.3.2)、页面语言(3.1.1)、标题层级、地标区域
- **排版**:字号下限、行高(1.4.12)、行长(measure)、字号/字族阶梯
- **布局/交互**:横向溢出、间距基准网格、色彩数量、图片尺寸预留(CLS)、**浮层遮挡(可见 ≠ 可点)**
- **动效(2.3.3,两条独立判据)**:
  - `interaction/reduced-motion` —— CSS 动效(`animation`/`transition`)缺 `@media (prefers-reduced-motion: reduce)` 兜底
  - `interaction/js-driven-motion` —— **JS 驱动的动效**(GSAP 默认机制=逐帧写内联 `transform`/`opacity`、WAAPI、手写 rAF)在 reduce 偏好下仍在动。这类动效**不产生任何 CSS 动画**,上面那条 CSS 兜底对它无效(`animation-duration: 0.01ms !important` 管不到 JS 写的内联样式),所以修复建议直接给 JS 侧写法:检测到 GSAP 时给 `gsap.matchMedia()` 分支,否则给 `window.matchMedia` 分支

### 闭环怎么用

```
design_capture target=... label=before
design_review                            # 出报告 + 问题清单
read_image <首屏截图>                     # 模型做视觉复核(工具刻意不做)
design_fix apply=true cssFile=styles.css # 生成并应用补丁(备份原文件)
design_capture label=after
design_review compare=<before 目录>       # 差值:已修复 / 仍存在 / 新引入
```

`report.md` 中 `## 视觉复核` 之后的段落**在复测时会被保留**,便于把模型批注累加进报告;
不传 `compare` 时,自动基线**只认同一个目标**的上一次审计(跨目标比较毫无意义)。

## 实测证据

**端到端闭环测试**(`node test/e2e.mjs`,**42/42 PASS**):

- before 检出 16 条问题(对比度 / 目标尺寸 / alt / label / lang / 焦点 / 溢出 / 字号 / 行高 / 行长 / 间距 / CLS / **遮挡**)
- 应用自动补丁后:**修复 7 条、新引入 0 条**,剩余为需改标记的项
- 横向溢出只在 mobile 视口标出(多视口确实在工作)
- HTML 类修复被分流,不会污染 `.css`
- **JS 驱动动效必须报出**:单变量 fixture(只有 rAF 动效、没有任何 CSS 动效)报出 `interaction/js-driven-motion`,同时 CSS 兜底规则**不得**跟着误报
- **反向用例不得误报**:脚本里已处理 reduced-motion 的页面零告警;断言同时钉住"捕获环境处于 reduce 偏好"这一前提,Chrome 若改了默认值会立刻失败而不是语义悄悄漂移
- **不许假阴性**:单元层直接喂真实 evidence、只翻转"声明过兜底"这一位 —— 声明了却仍有元素在动,必须报出(low 并说明"兜底没盖住"),而不是查到字符串就闭嘴

**真实项目验证**(BookRank3,4 个页面,数据库指向副本、真实库零改动):

| 页面 | 首审 | 修复后 |
|---|---|---|
| `/` | 11(高1/中3/低7) | **7(全 low)** |
| `/awards` | 8(高1/中2/低5) | **5(全 low)** |
| `/new-books` | 7(高1/中2/低4) | **4(全 low)** |
| `/publishers` | 7(高2/中1/低4) | **4(全 low)** |

最低对比度 3.78–3.95:1 → **4.57–4.74:1**(越过 AA)。

**真实项目揪出的 3 处遮挡**(全为既有缺陷,A/B 实测已排除自身改动归因):搜索建议浮层盖住视图切换器、移动端 `#sidebar-toggle` 整块被顶栏压住、new-books 面包屑被固定顶栏覆盖 —— 三者的 `display/opacity/rect` 全部"正常",**只有元素中心命中测试能发现**。

## 开发过程中被真实使用反推修掉的缺陷

1. 修复只覆盖最严重实例 → 改逐元素展开
2. `<a><img alt="x">` 被误报无可访问名称 → accname 的 name-from-content 包含内嵌图 alt
3. 代码块被套用散文行长判据 → `pre`/`code` 排除
4. 行长按容器宽度折算(中文高估约一倍、宽容器短文本必误报)→ 改**实测**「字符数 ÷ 渲染行数」
5. 同一选择器在补丁里重复 8 次(相对父级生成导致塌缩)→ 按选择器去重,补丁 11.2KB → 7.6KB
6. 复测重写 `report.md` 会抹掉批注 → 保留 `## 视觉复核` 段落
7. 自动基线取到无关页面 → 只认同目标
8. 可见 ≠ 可点(盲点)→ 新增遮挡命中测试
9. **差值误报**:同一规则"最差元素"漂移时被判成"已修复 + 新引入"(假回归)→ 二次归类为"选择器漂移",单独列出前后元素
10. **补丁重复块**:跨 finding 的同一选择器生成多份(含注释的复合段会绕过归并)→ 全局按选择器归并(`flattenCssBlocks`),实测 4 条修复 → 2 个选择器 + 1 段原样规则

11. **动效盲区(假阴性)**:取证脚本原来只统计计算样式里的 `animation`/`transition`,于是 GSAP 那类"逐帧写内联 transform"的页面**一条都不报** —— 实测同一页面(整屏 rAF 无限位移、完全无视 `prefers-reduced-motion`)拿到"问题总数 0",而它旁边只多一个 CSS 过渡元素的对照页却报了 1 条且归因给了那个 CSS 元素。修法:**跨时间采样**(隔 250ms 比较几何/透明度变化,排除有 CSS 来源的元素),并把该类动效的修复建议从纯 CSS 改成 JS 侧 `matchMedia` / `gsap.matchMedia()`

## 已知边界

- 只覆盖**可测量**判据;审美、交互态(hover/focus 视觉)、内容与信息架构、真实性能采样不在范围内(报告里显式列为"未覆盖")
- SPA 需传 `waitFor` 指定标志性选择器;元素数 < 20 时会告警"可能只抓到空壳"
- 遮挡判据用元素**中心点**命中测试:部分遮挡但中心可点的目标不会报(保守取值,避免误报)
- 动效判据需要**跨时间采样**:每次取证多花约 250ms(两视口约 0.5s);采样窗内没动 = 不报,短于采样窗且不重复的一次性动画可能采不到
- **无头 Chrome 默认就是 `prefers-reduced-motion: reduce`**:因此"观察到在动"实际等价于"该元素**无视**了 reduce 偏好"——这正是要找的违规。证据里记录该状态(`motion.prefersReducedMotion`),报告实测值里也会标注
- 脚本里已经声明 reduce 分支、却仍有元素在动时,判据**不会静默放过**(那会造成假阴性):降级为 low 并提示"兜底没盖住这些元素"(GSAP 常见原因:补间建在 `matchMedia` 分支之外)
- 依赖本机 Chrome/Edge(`DSH_DESIGN_BROWSER` 可覆盖);**零 npm 依赖**,只用 Node 内置模块 + 全局 WebSocket(Node ≥ 22)
- 被审项目若采用"哈希名 + 运行中进程缓存构建清单"的打包方式(如 Flask + `dist_url`),**改完 CSS/JS 都必须重启其服务**,否则页面仍加载旧资源(本插件的复测会如实显示"已修复 0")

## 安装

```sh
# 已安装 dsh 命令
dsh plugin --profile web add dsh-design-audit
# 或不经全局安装
npx @deepseek-ai/dsh plugin --profile web add dsh-design-audit
```

本包是 **bundle 形态**:`package.json` 声明 `dsh.bundle.patch: ./cordis.patch.yml`,
patch 里以**包名自引用**(`name: dsh-design-audit`)插入插件。
`dsh plugin add` 会把它写进 profile 的 dependencies + bundles,启动时由 bundles 组合装配。

### 开发期(本机源码)

```powershell
dev_inject_plugin dir=D:\plugins\dsh-design-audit   # 运行时注入(改源码即生效)
dev_reload_package packageName=dsh-design-audit     # 源码改动后热重载
```

### 一条踩坑记录(留给后来者)

本插件**尚未声明** `dsh.bundle` 时,曾被误写入 `dsh.profile.bundles` → **profile 下次启动硬崩**:
`dsh-app-boot` 会对每个 bundles 条目校验 `dsh.bundle` 声明,缺失即 throw(报错原文:
`profile bundle "..." declares no dsh.bundle in its package.json`)。
现在包已声明 `dsh.bundle`,bundles 就是**正确**路径 —— 但这条教训对所有"纯 host 工具型"插件仍然成立:
**要么声明 bundle,要么用 patch 装配,不要只往 bundles 里塞名字。**

依赖解析:`node_modules/@deepseek-ai/dsh-tools` 是指向当前 dsh 安装体的 junction(与 dsh-skill-audit / dsh-agent-frugality 同一物化方式)。纯 ESM 直出,无构建步骤。

## 结构

```
lib/cdp.js     无头浏览器驱动(CDP over WebSocket,零依赖;含渲染就绪门控)
lib/collect.js 页面内取证脚本(自包含,序列化后注入页面;含遮挡命中测试与动效跨时间采样)
lib/checks.js  判据引擎(纯函数,可单测)
lib/report.js  Markdown / 自包含 HTML 报告(首屏图内嵌可读、整页长图链接存档)
lib/index.js   三个工具的注册与编排
test/e2e.mjs   端到端闭环验证(42 断言,含 JS 动效正/反用例与判据分支单元断言)
test/smoke.mjs CDP 通路冒烟
test/fixtures/ 有意植入缺陷的靶站(含遮挡用例、JS 动效用例、已处理 reduced-motion 的反向用例)
```

Install

dsh plugin --profile web add github:gongyijie85/dsh-design-audit

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source