Skip to the content.

界面规范

本文是 code-association 工作台的唯一权威视觉与交互规范。 界面实现映射 记录本文到文件、类名与数据契约的对应关系; UiContractTest 把本文的关键契约固化成断言。

改 UI 之前先读本文,改完之后同步本文。 规范与实现漂移比没有规范更糟: 下一个人会照着错的规范改,而测试会替这个错误背书。


目录

章 内容 什么时候看
1 定位、视觉方向、四条签名特征 第一次接触,或怀疑”这个界面到底想做成什么”
2 双主题、三态偏好、画布取色 改配色或主题相关逻辑
3 令牌分组、文字三级、语义色、对比度实测 选颜色
4 字体栈、字号阶梯、字重字距、行高 加文字或调层级
5 骨架、栅格、尺寸表、断点、滚动 加区块或改宽度
6 圆角四级、阴影四级 新建容器
7 26 类组件的结构/变体/状态/禁忌 最常查的一章
8 五种状态、错误分级、破坏性操作、进度 写异步逻辑
9 令牌、允许与禁止、降低动效 加动画
10 焦点、键盘、ARIA、触屏、对比度 加交互控件
11 语气、用词、标签映射、句式、禁忌 写界面文字
12 可勾选的自检表 提交前
13 规范与现状的差异清单 改到相关组件时
14 相关文档 —

1. 定位

1.1 这是什么界面

一个证据审阅台,不是仪表盘,也不是数据录入系统。

使用者打开它时心里有一个具体问题:「我这次改动,别的程序要不要跟着动?」 界面上的每一块都必须服务于这个判断,或者服务于”为什么我该相信这个判断”。

1.2 视觉方向

低饱和薰衣草色工作台 + 白色业务卡片 + 可折叠单侧栏 + 任务优先的非对称布局。

1.3 是 / 不是

是 不是
审阅台:判断、证据、路径、置信度 监控大屏:滚动数字与实时曲线
任务优先:主任务占更大宽度 等分布局:每块一样大
克制的紫:只做品牌与选中态 装饰性渐变与彩色卡片
语义色只表达语义 用红绿做美化
信息密度高但分层清晰 大留白 + 大图标 + 少信息

1.4 四条签名特征

这四条是识别本产品的视觉锚点,改动它们等于换产品:

  1. 可折叠的紫色单侧栏:图标与文字菜单在同一列,展开显示标签与分组,收起只剩图标列。
  2. 页头方块徽章 + 27px 页面标题:每页开头一个渐变紫方块图标 + 大标题 + 一行副标题。
  3. 风险色阶驱动的模块地图:节点大小=风险分,颜色=风险等级,紫色描边=本次变更模块。
  4. 逐跳路径(path)组件:从变更起点开始的一列圆点 + 竖线,每跳标注使用方式。

2. 主题

2.1 双主题,单一令牌源

浅色与深色共用同一套布局规则,只在 :root[data-theme="dark"] 里覆盖颜色令牌。 深色块不新增任何选择器、不修改任何尺寸。

这条纪律的收益是双向的:加一个组件只需要写一次布局;改一次深色只需要改令牌。

2.2 三态偏好

偏好 行为
system(默认) 跟随操作系统 prefers-color-scheme,系统切换实时生效
light 固定浅色
dark 固定深色

2.3 画布颜色必须运行时读取

依赖图(Cytoscape)把颜色烘进 SVG 场景,无法继承 CSS 变量。 因此 app.js 的 themeColor(name, fallback) 读令牌计算值再传给图实例, 主题切换时重建图。

禁忌:在 JS 里硬编码颜色。fallback 参数只用于令牌缺失的兜底,不是第二套调色板。

2.4 切换动效

调色板切换必须像调光,不能像闪光。参与主题切换的容器 (body、.app、.sidebar、.card、.stat、.modal、.drawer、.toast、.code、.map) 统一挂 background-color / border-color / color 的 --t-base 过渡。


3. 色彩

3.1 令牌分组

颜色只有一个来源:style.css 的 :root。禁止在组件规则里写十六进制字面量。

分组 令牌 用途
表面 --app-bg --app-bg-tint --rail-bg --nav-bg --surface --surface-2 --surface-3 --surface-hover 画布、侧栏、卡片、卡内嵌块、悬停
品牌 --primary --primary-600 --primary-700 --primary-50 --primary-100 主操作、选中、链接、品牌填充
文字 --text --text-2 --text-3 --text-inverse 正文 / 次要 / 弱化 / 反白
线条 --border --border-strong 分隔线 / 控件描边
状态 --success --warning --danger --danger-strong 及各 -bg 语义反馈
风险色阶 --risk-critical --risk-high --risk-medium --risk-low --risk-info 只用于风险等级
依赖图 --map-edge --map-arrow --map-edge-label --map-label-bg 只被 themeColor() 读取
代码 diff --code-add-fg/bg --code-del-fg/bg 增删行
浮层 --overlay-bg --shadow-drawer --toast-* --scrollbar-* 遮罩、抽屉、轻提示、滚动条

完整取值见界面实现映射 · 令牌映射。

3.2 文字三级纪律

令牌 用在什么上 不能用在什么上
--text 正文、标题、表格主值、被选中的项 —
--text-2 次要说明、标签、副标题、未选中的导航项 正文段落主体
--text-3 元信息、时间戳、占位符、大写小标签、图标 任何需要被读懂的正文

--text-3 是”可以看不见”的层级。把解释性文字放在这里,等于让用户看不见它。

3.3 语义色纪律

3.4 对比度实测

下表是当前令牌对的实际对比度(WCAG 2.1,相对亮度公式实算):

文字对表面(4.5:1 为 AA 正文门槛)

组合 浅色 深色
--text on --surface 13.41 ✅ 13.41 ✅
--text-body on --inset-bg 8.66 ✅ 10.97 ✅
--nav-item-fg on 侧栏 7.19 ✅ 8.48 ✅
--primary-700 on --primary-50 6.44 ✅ 8.58 ✅
--text-2 on --surface 4.58 ✅ 临界 6.78 ✅
--text-2 on --surface-3 4.07 ❌ 6.43 ✅
--primary on --surface 4.57 ✅ 临界 6.63 ✅
--on-primary on --primary 4.57 ✅ 临界 7.37 ✅
--text-3 on --surface 3.30 ⚠️ 仅大字 4.66 ✅
--text-3 on --surface-2 3.10 ❌ 4.42 ❌ 临界
--text-3 on --surface-3 2.93 ❌ 4.14 ❌

语义色对语义底

组合 浅色 深色
--warning-fg on --warning-bg 4.53 ✅ 8.66 ✅
--danger-strong on --danger-bg 4.21 ❌ 临界 7.60 ✅
--danger-strong on --btn-danger-bg 4.53 ✅ —
--success on --success-bg 3.57 ❌ 6.64 ✅
--danger on --danger-bg 3.20 ❌ 5.93 ✅
--warning on --warning-bg 2.79 ❌ 最低 6.64 ✅

风险色阶(圆点与图形,按 3:1 非文本门槛)

令牌 对 --surface(浅色)
--risk-critical 3.70 ✅
--risk-low 4.04 ✅
--risk-medium 3.11 ✅ 临界
--risk-high 2.73 ❌
--risk-info 2.02 ❌ 最弱

结论:深色主题整体优于浅色。 浅色的 --text-3、--warning、--success、--danger 在小字号(10.5–11px,正是 pill 与标签的字号)下不满足 AA 4.5:1; --risk-high 与 --risk-info 连非文本 3:1 都不到。

处理原则(不要假装达标):

  1. 正文与关键数据必须 ≥ 4.5。任何承载判断依据的文字(影响路径、符号名、文件行号、 置信度)不得使用 --text-3,也不得放在 --surface-2 / --surface-3 上。
  2. --text-3 只用于装饰性元信息与全大写小标签。这些位置对比度不足是可接受的已知债务, 但新增用法时不要再往里塞新信息。
  3. 语义文字优先用 -strong / -fg 变体:警告文案用 --warning-fg(4.53)而不是 --warning(2.79)。这是现有代码已经在做的事,规范把它固定下来。
  4. 风险等级不能只靠颜色区分。 --risk-high 与 --risk-info 对白底只有 2.73 / 2.02, 低于非文本 3:1。因此风险等级必须同时有文字标签(严重/高/中/低/信息), 颜色只做辅助。图例也必须带文字,不能只有色点。
  5. 修 --text-3 与 --warning 的对比度是一次独立改动,需要同时改浅色与深色两套值, 并重跑本文的实测表。不要顺手改。

4. 排版

4.1 字体栈

--font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
        "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
--mono: "SFMono-Regular", "JetBrains Mono", "Menlo", Consolas, monospace;

4.2 字号阶梯

实际取值(来自样式表,不是设计稿):

字号 用途 代表选择器
30px 版本号大数字 .upd-version-num
27px 页面标题(≤1180px 降为 22px) .page-title
21px 统计块数值 .stat .value
20px 页头指标值、进度环百分比 .head-metric .value .ring-label .pct
18px 弹窗标题 .modal-title
16px 侧栏标题 .sidebar-context h2
15px 卡片标题、流程卡值 .card-title .flow-value
13px 正文基准 body、.row-title
12.5px 控件、按钮、导航项、副标题 .btn .nav-item .page-sub
12px 次要行文字、行描述 .row-desc、.sidebar-foot .nav-item
11.5px 元信息、卡片副标题、标签值 .card-sub .stat .label .meta-row
11px 徽章、chip、标签 .pill .chip .tag-chip
10.5px 全大写小标签、导航分组标题、计数 .eyebrow .nav-group-title .head-metric .label
10px 键盘提示、极弱计数 .search kbd .ring-label .cap
9.5px 合并标记(唯一一处) .commit-merge

规则:新增字号必须落在上表里。上表已经够密,再插一个 12.25px 只会让层级失效。

4.3 字重与字距

主配方(六处,字距略有差异):

选择器 字距
.eyebrow 0.09em
.nav-group-title 0.08em
.section-label 0.07em
.flow-label 0.06em
.tr.head 0.06em
.nav-tag-head 0.04em

统一为:10.5px + uppercase + font-weight: 600 + --text-3。

两个变体(尺寸或字重不同,因为所处位置不同):

选择器 差异 为什么
.head-metric .label 0.04em、无 600 页头指标标签紧挨 20px 数值,加粗会与数值抢注意力
.ev-type 10px、等宽、0.05em、--primary 证据类型是标识符不是结构标签,用品牌色区分

4.4 行高


5. 布局

5.1 主骨架

.app  ── grid: var(--sidebar-w) minmax(0, 1fr)
 ├─ nav.sidebar   288px 紫色渐变单侧栏(展开:图标 + 文字 + 分组)
 └─ main.workspace      滚动容器,内边距 26px 30px 48px
     └─ section.page    max-width 1460px,居中

5.2 卡片栅格

类 列定义 用途
.grid-2 minmax(0,1.5fr) minmax(0,1fr) 主任务 + 辅助栏(非对称,任务优先)
.grid-2-even repeat(2, minmax(0,1fr)) 两栏同权
.grid-3 repeat(3, minmax(0,1fr)) 三个并列块
.stats repeat(4, minmax(0,1fr)) 统计块带
.flow repeat(3, minmax(0,1fr)) 三段式结论(变更 → 传播 → 影响)
.rv-layout minmax(0,1fr) minmax(0,1.05fr) 影响审核:发现列表 + 证据面板(≤1180px 折单列)
.stack 单列 + 16px 间距 纵向卡片序列

.grid-2-even 与 .grid-3 目前没有使用者(只有定义与断点降级规则)。 它们是备用的等分栅格:需要两栏同权或三栏并列时可以直接用,但不要为了”用上新类”而套一层栅格。 当前主任务页都用非对称比例(.grid-2 或影响审核页的 .rv-layout),这是有意的——任务优先,主栏更宽。

minmax(0, …) 不是可选项。 少了它,网格项的最小内容宽度会撑破容器, 表现为”卡片里文字不换行、整列被裁掉”。

5.3 尺寸表

量 值
侧栏展开宽 288px(≤1180px 248px,≤860px 236px)
侧栏收起宽 72px
工作区内边距 26px 30px 48px(≤1180px 20px 20px 40px)
页面最大宽 1460px
卡片内边距 20px(紧凑 15px)
卡片间距 16px(统计块 14px)
页头下间距 22px
控件高度 按钮 30px 级、输入框 34px 级、小按钮 26px 级

5.4 响应式断点

断点是固定尺寸的对应物,必须与上表同改。断点写死数值是有意的: 它们是”这些尺寸加起来超了”的临界点,不是设备分类。

五个断点,分两档:布局断点(1400 / 1180 / 860)改变骨架,局部断点(1024 / 720)只调整单个组件。

断点 档 改动
≤1400px 布局 统计块 4→2 列;.grid-2 主栏比例 1.5→1.35
≤1180px 布局 侧栏宽 288→248;.app 加 min-width: 0(否则 1180px 下限把右列裁掉且无滚动条);.grid-2/-even/-3、.flow 与复核页 .rv-layout 全部单列(粘性证据栏恢复文档流);页面标题 27→22px;.tr-history 与 .tr-sources 收缩固定轨道;代码源列表隐藏标签与同步时间两列;剩余溢出在卡片内横向滚动
≤1024px 局部 版本号 30→25px;发布记录行改为纵向堆叠
≤860px 布局 侧栏宽 248→236;统计块单列;.tr .cell.optional 隐藏;.tr-changes .tr-history .tr-commits .tr-sources 各自减少轨道;版本台账保留六列改为横向滚动;步骤条改两列换行;标签单元与筛选条收窄
≤720px 局部 提交范围行去掉 meta 列;更新横幅动作组占满整行

降级原则:先砍列,再收缩,最后在卡片内横向滚动。永远不要靠 body { overflow: hidden } 藏掉内容—— 用户没有任何办法把被裁掉的右列拿回来。

5.5 滚动约定


6. 几何与阴影

6.1 圆角四级

令牌 值 用途
--r-card 16px 卡片
--r-tile 14px 统计块
--r-control 10px 按钮、输入框、下拉
--r-pill 999px 徽章、标签、分段控件

另有固定的中间档:12px(行卡片、流程卡、callout、时间线块)、10px(代码块)、 9px(行内图标块、证据行)、8px(小按钮、导航项)、6px(chip)、4px(合并标记)。 外壳与弹窗更大:.app 22px、.modal 18px、.title-badge 13px、.sidebar-logo 12px。

层级对应:越大越外层的容器圆角越大。把卡片做成 8px 而按钮做成 16px,层级会读反。

6.2 阴影四级

令牌 用途
--shadow-soft 极轻:选中态导航项、激活 tab
--shadow-card 卡片与统计块默认
--shadow-hover 行卡片悬停
--shadow-pop 弹窗(唯一的大阴影)

另有 --shadow-drawer(抽屉,向左投射)、--app-shadow(外壳)、 --brand-shadow*(品牌按钮与徽章的彩色投影)、--toast-shadow。

纪律:


7. 组件

组件按”从外到内”排列。每项给出结构 / 变体 / 状态 / 禁忌。

7.1 应用外壳 .app

结构:nav.sidebar + main.workspace。

7.2 侧栏 .sidebar

结构:.sidebar-top(Logo + 品牌名 + 折叠按钮)→ .sidebar-context(eyebrow + 页面标题 + 添加)→ .search → .nav-scroll(主功能 + 代码源 / 状态 / 最近分析)→ .sidebar-foot(历史 / 设置 / 已忽略 / 主题 / 头像)。

7.3 侧栏收起态 .nav-collapsed

这是默认态。 首次打开就是图标列;展开后写入 localStorage 的 ca.nav,下次首帧保持展开。

7.4 页头 .page-head

面包屑(工作区 / 页面)
[渐变方块徽章 40px] 页面标题 27px [状态 pill]
副标题一行(次要信息,可含分隔点)
                                …右侧:指标组 + 操作按钮组

7.5 卡片 .card

7.6 统计块 .stats / .stat

7.7 徽章与 chip

组件 尺寸 用途
.pill 11px / 3px 9px / --r-pill 状态、风险等级、模式
.pill .dot 5px 圆点 与文字同色的状态点
.chip 11px / 2px 7px / 6px 圆角 键值元信息
.tag-chip 11px / 550 字重 / 品牌浅底 可删除的标签
.tag-pill 10.5px 行内标签(可点击过滤);.active = 当前筛选,实心紫底

变体:success warning danger neutral brand outline。 禁忌:不要用徽章表达长句;超过 6 个字的判断放正文。

7.8 按钮 .btn

变体 外观 用途
默认 白底 + 描边 次要操作
.btn-primary 紫底白字 + 品牌阴影 每个视图只应有一个主操作
.btn-ghost 透明底无描边 弱化的第三级操作
.btn-danger 浅红底 + 红字 破坏性操作
.btn-sm 11.5px / 5px 10px / 8px 圆角 卡片头、行内
.btn-block 100% 宽 表单主提交

状态:默认 / hover(浅紫底 + 紫字)/ active(下移 1px)/ disabled(opacity: .5 + not-allowed)/ loading(内嵌 .spinner,11px)。

7.9 表单

7.10 列表与行卡片

组件 结构 用途
.list-row 行内分隔线、12px 圆角 连续列表
.card-row 独立卡片、12px 圆角 发现列表(每条是独立判断单元)
.tr 网格行、10px 圆角 数据表格
.tr.head 10.5px 全大写表头 列标题

分段控件 .tabs / .tab 目前只用于影响审核页的发现分组(#rv-tabs:待审核 / 已确认 / 已忽略)。 它是胶囊底 + 白色激活块的形态,.tab .n 承载计数。不要为页面切换另造一套 tab—— 页面切换由左侧导航负责。

影响审核页只有一处影响清单。 对比程序不单独占卡:发现列表标题下用一行 .peer-strip 汇总(每个程序一枚 .pill:程序 · 契约数 · 影响处数),逐条影响只在下方出现一次; 右侧证据面板在未选中发现时承载完整契约命中与对方使用点,选中后切换为证据链。 总览的合并卡复用同一个 .peer-strip。

影响清单被截断时必须说明。 一次大变更可能产生数十万条跨程序影响,报告按风险只保留前 N 条 (code-association.max-report-impacts,默认 5000)。发现列表上方用一块 .callout.warning (#rv-impacts-note,图标 #i-alert-circle,文案取 report.impactsNote)说明截断了多少; 未截断时整块 .hidden,不占位。页头副标题的「N 项影响」用全量的 report.impactsTotal, 列表条数只用于标签计数与分页——截断而不说,等于让用户以为影响只有这么多。

栅格模板族(列数 = 单元格数,断点降级时同步改):

模板 默认列 用途
.tr-sources 76px / 1.7fr / 96 / 150 / 128 / 92 / 76(7 列) 代码源列表
.tr-history 48px / 1.4fr / 96 / 150 / 130 / 58(6 列) 分析历史
.tr-changes 124px / 1fr / 168 / 26(4 列) 变更清单
.tr-commits 116px / 1fr / 130 / 110(4 列) 提交范围
.tr-ledger 110px / 1fr ×5(6 列,可横向滚动) 版本台账
.tr-impacts 已废弃(无使用者) 见第 13 章 #10

降级开关 .cell.optional:≤860px 时统一隐藏。标 .optional 等于声明”这一列可以为了宽度让路”。 必须保留的列不要标 optional——分析历史的状态列就是例子:失败原因才是打开这个列表的理由。

断点里的轨道数必须恰好等于该行剩余单元格数。多一条轨道会把尾列推到第二行, 整行变成两行高;少一条轨道则最后一格跨列。这是这类表格最常见的错法。

7.11 分页 .pager

7.12 进度、步骤条与环

7.13 三段式结论 .flow

[变更]  →  [传播]  →  [影响]
标签 10.5px 全大写 / 值 15px 600 / 说明 11.5px

7.14 时间线与路径

两个组件共享同一套”圆点 + 竖线”语法,但语义不同:

组件 圆点 语义
.timeline / .tl-* 9px,浅紫/红/绿 事件先后顺序
.path / .path-* 9px + 3px 光晕,起点红色 传播链路,逐跳因果

7.15 依赖图 .map

7.16 弹窗 .overlay / .modal

7.17 抽屉 .drawer

7.18 轻提示 .toast

7.19 空态 .empty

7.20 提示块 .callout

7.21 代码与 diff .code

7.22 证据行 .evidence / .ev-*

7.23 AI 审核卡片 .ai-*(可选功能)

AI 审核默认关闭。关闭时页面上不出现任何新元素——一个不启用它的安装不应看到它的痕迹。

这个组件在视觉上要解决一个特殊问题:让读者一眼分清”证据”与”解释”。

元素 类名 规则
分析页卡片 #an-ai-card 未配置时整卡 .hidden;有报告且已配置才出现
生成正文 .ai-text 12.5px / 1.7 行高 / --text-body / pre-wrap
溯源行 .ai-foot 11px --text-3——这是 --text-3 唯一正当的使用场景:模型名与时间是可忽略的元信息
「待验证」标注 .ai-verified-note 警告色底 + 警告色字 + 13px 图标,贴在卡片头里,紧邻标题
动作组 .ai-actions 右对齐;生成按钮 .btn-sm
设置页状态行 .ai-status-row 状态 pill + 11.5px 说明;GitLab 私有仓库卡片(7.25)复用同一配方,不另造类名
快捷填充 .ai-presets 三个 .btn-sm,刻意用普通按钮——它们是输入捷径,不是决策,不能和「保存」抢主位

三条硬规则:

  1. 「AI 生成 · 待验证」必须贴着内容,不能放脚注。 标注放在卡片头、紧邻标题, 让警告随内容一起移动。放在页面底部的免责声明没有人会读到。
  2. AI 正文用 --text-body(8.66 对比度),不用 --text-3。 它是最长的一段可读文字, 行高放宽到 1.7 也是为此——行距紧会让人读错评审要点。
  3. 快捷填充不改变 Key。 三个预设只填接口地址与模型;Key 是账号凭据,不该被模板覆盖。

失败态:AI 失败时只影响这张卡片(就地显示原因 + 重试),分析结论与证据链完全不受影响。 界面上必须让读者看到这一点——AI 是解释层,不是证据层。

.ai-verified-note 使用 --warning-fg(4.53)而非 --warning(2.79), 符合 3.4 的对比度纪律。

7.24 提交选择 .commit-list / .commit-row / .commit-actions

「新建分析」选到 COMMIT_RANGE 时,表单用它回答”这次到底分析哪些提交”。它刻意不是多选下拉: 一次要审的常是十几个提交,用户要看清标题、作者与时间才决定勾谁。

结构

状态

日期窗口(同属该模式的字段)

禁忌

7.25 GitLab 私有仓库卡片(设置页)#card-gitlab

设置页在 AI 卡片 #card-ai 与导入导出卡片 #card-transfer 之间多一张卡片,用来填写 GitLab 个人访问令牌(PAT)。它不新增组件类,全部由既有组件拼成:

元素 复用类名 / id 规则
卡片 .card #card-gitlab 与 AI 卡片同宽(grid-column: 1 / -1)
状态行 .ai-status-row / .ai-status-note #gl-status #gl-status-note 与 AI 卡片共用「pill + 11.5px 说明」配方(7.23),不另造类名
令牌输入 .field .path-field #gl-token HTML 标准密码输入框;永远不预填明文,占位符只说明「已保存 <掩码>」或「未设置」,留空 = 保持已保存的值
清除 .btn.btn-sm #gl-clear 与输入框同排,放在 .path-field 内;清除前必须确认
保存 .btn.btn-sm.btn-primary #gl-save 卡片头动作组,沿用 AI 卡片「一个主操作」的排法
说明 .callout .hint .ai-foot scope、保存位置、优先级与配置文件路径

纪律:

7.26 配置导入导出卡片(设置页)#card-config

设置页在 GitLab 卡片 #card-gitlab 与代码源导入导出卡片 #card-transfer 之间多一张卡片: 一个 JSON 带走设置项(AI 审核、GitLab 令牌)与全部代码源。两张导入导出卡片并存不是重复—— 一张搬”这台机器的全部配置”,一张只搬代码源;合成一张会逼着只想换代码源的人连带接受设置覆盖。 同样不新增组件类,全部由既有组件拼成:

元素 复用类名 / id 规则
卡片 .card #card-config 与相邻卡片同宽(grid-column: 1 / -1)
导出 .btn.btn-sm #cfg-export data-config-export 卡片头动作组左侧;window.location 直接下载,不经 fetch——导出文件可能很大,不该先读进 JS 内存
导入 .btn.btn-sm.btn-primary #cfg-import data-config-import 卡片头唯一主操作;点它只是打开隐藏的文件输入
文件输入 input[type=file] #cfg-file accept="application/json,.json" + hidden,不占位
密钥开关 .checkbox #cfg-secrets 文案「包含密钥(API Key / GitLab 令牌,明文)」;永远不预勾选
规则提示 .kv-list #cfg-transfer-hint 四行:导出内容 / 不导出 / 导入规则 / 密钥规则
结果宿主 #cfg-result 成功、部分成功、失败都渲染在这里,不另开弹窗

密钥规则(三条一起读):

结果呈现(8.1 的部分成功):

结果 呈现
设置项 .import-counts 两行:已应用的设置项(AI 审核 / GitLab 令牌)+ 密钥是否随文件导入
代码源 .import-counts 四行:共读取 / 新建 / 更新(合并标签)/ 跳过
失败项 .import-errors 逐条列出:设置项以「设置」+ 段名 + 原因,代码源以 #序号 + 名称 + 原因
文件本身非法 4xx 的中文消息进 .callout.danger + 一条错误 toast(「导入失败:…」)

导入后刷新:

禁忌:


8. 反馈与状态

8.1 五种状态,缺一不可

任何异步区域都必须实现全部五种:

状态 表现
加载中 骨架或 .spinner + 一行”正在…”;不要只放一个转圈
空 .empty 说明原因 + 下一步动作
成功 内容本身;必要时一条 toast
失败 就地显示原因(不截断、不折叠)+ 重试入口
部分成功 计数 + 逐条错误列表,不因一条失败中断整体

8.2 错误呈现分级

级别 呈现
字段级 字段下方 .hint 红色文字
区块级 该区块内 .callout.danger,其他区块照常可用
全局 顶部横幅或页面级 callout,仍保留可操作入口

错误必须说明三件事:发生了什么、为什么、下一步怎么办。 “操作失败”不是错误信息。

8.3 破坏性操作

8.4 进度可见性


9. 动效

9.1 令牌

--ease: cubic-bezier(0.32, 0.72, 0.28, 1);  /* 快出慢入,收尾柔和 */
--t-fast: 150ms;   /* 颜色、悬停、焦点 */
--t-base: 180ms;   /* 布局轨道、主题切换、页面进场 */

9.2 允许的动画

动画 时长 用途
颜色/描边/阴影过渡(32 处 transition 的主体) --t-fast 150ms 所有 hover/focus 反馈
grid-template-columns(侧栏收起) --t-base 180ms 侧栏轨道由 288px 变到 72px
页面进场 page-in --t-base opacity + translateY(4px)
遮罩进场 overlay-in --t-base opacity
弹窗进场 modal-in 220ms translateY(10px) scale(.985)
抽屉进场 drawer-in 220ms translateX(24px) + 透明度
toast 进场 toast-in 200ms translateY(8px)
进度条宽度 400ms 数值变化
环形进度 500ms stroke-dashoffset
spinner spin 800ms linear 无限 加载
不确定进度条纹 progress-slide 1.1s linear 无限 总量未知
主题按钮图标 --t-base hover 旋转 -18°
按钮按下 transform 80ms translateY(1px)
开关滑块 --t-fast translateX(11px)

入场时长的分层是有意的:页面与遮罩用 --t-base,浮层(弹窗/抽屉/toast)用 200–220ms 的裸值。浮层比基础时长略慢,让人感到”它浮起来了”。这三个裸值尚未令牌化, 新加入场动画请沿用 200–220ms 这个区间,不要另起一个 150 或 400。

9.3 禁止

9.4 降低动效

prefers-reduced-motion: reduce 时,全部动画与过渡压到 0.01ms、迭代 1 次。 这是全局规则,新增动画自动继承,大部分情况不需要单独处理。

但有一个例外必须单独处理:无限循环的动画被压到 0.01ms 后会停在周期的某个随机位置, 看起来像坏了。不确定进度条纹已经单独补了 animation: none + 静止条纹(语义不变)。

新增无限循环动画时,必须同时写一条 reduced-motion 覆盖,给出静止状态。 一次性入场动画不需要。


10. 无障碍

10.1 焦点

10.2 键盘

操作 键 行为
收起 / 展开侧栏 Ctrl + B / ⌘ + B 在输入控件内让位
聚焦全局搜索 Ctrl + K / ⌘ + K ⚠️ 当前未让位(见第 13 章 #8)
关闭弹窗 / 抽屉 Escape 关闭所有可见浮层;带草稿的表单同时重置
文件夹选择器跳转 Enter(路径框内) —

焦点管理的现状:打开浮层不自动聚焦首个控件(只有表单字段用 60ms 定时聚焦), 关闭后不归还焦点,也没有焦点陷阱。这三点是已知缺口,见第 13 章 #7。

10.3 ARIA 契约

元素 必需属性
主导航 .sidebar aria-label="主导航"
图标按钮 title + aria-label(两者都要:前者给鼠标,后者给读屏)
折叠/展开按钮 aria-expanded + aria-controls="sidenav"
主题按钮 aria-pressed 反映解析后是否为深色
弹窗 role="dialog" + aria-modal="true" + aria-labelledby
列表型浮层 role="listbox" + aria-label
进度条 / 更新条 aria-live="polite"
全局横幅 role="status"(非打断式播报)
标签筛选 pill aria-pressed 反映是否处于筛选
纯装饰图标 aria-hidden="true";雪碧图容器整块 aria-hidden

收起态必须同步两个按钮的 aria-expanded。 隐藏按钮上残留过期的展开状态, 比不写这个属性更糟。

已知缺口:当前导航项只靠 .active 类表示当前页,没有 aria-current="page"。 新增导航时请补上——视觉选中态对读屏用户不存在。

10.4 触屏

10.5 对比度

见 3.4 对比度实测。不要新增 --text-3 承载信息的用法。


11. 文案

11.1 语气

11.2 用词表

概念 界面用词
代码源 代码源(不用”项目”“仓库”混指)
其他程序 其他程序 / 对比程序(不用”peer”)
契约 契约(接口/数据库/配置/Maven/消息)
影响 影响(不用”风险”指代影响本身)
风险 / 置信度 两个独立概念,永不混称
确认 / 忽略 复核动作(不用”通过/拒绝”)

11.3 中文标签映射

风险:CRITICAL 严重 / HIGH 高 / MEDIUM 中 / LOW 低 / INFO 信息 (色调:danger / warning / warning / success / neutral)

状态:DONE 已就绪 / RUNNING 分析中 / PENDING 排队中 / FAILED 失败 (色调:success / brand / neutral / danger)

分析模式:GIT_DIFF 提交对比 / WORKTREE_DIFF 未提交改动 / PULL_REQUEST PR / MR / COMMIT_RANGE 提交范围(勾选要分析的提交)

COMMIT_RANGE 的界面动作是「勾选要分析的提交」(分支 + 日期窗口,见 7.24);按「起始..结束」 两端分析走 GIT_DIFF。命令行不受影响,仍按两端解析(起止提交都必填)。

变更类型、影响类型见 app.js 的 CHANGE_LABEL / TYPE_LABEL 映射表—— 新增枚举必须同时补映射,否则界面会显示原始常量名。

11.4 句式

按钮:动词开头,2–4 字。现网用词:新建分析 / 开始分析 / 导出 / 导入 / 立即检查 / 确认 / 忽略 / 取消。

空态:emptyBlock(title, sub) 固定两段——第一段说状态(strong),第二段说下一步。 现网样例:

场景 title sub
复核已清空 没有待处理的影响 当前分析中其他程序的所有影响项都已复核。
无分析记录 这个代码源还没有分析记录 点击「新建分析」为 <名称> 运行一次分析。
无时间线 还没有事件 运行一次分析后,这里会出现时间线。
未选中条目 尚未选择发现 左侧列表中的每一条都带有变更点、传播路径与代码位置。

第二段永远回答”那我现在能做什么”,不重复第一段。

错误:现象 + 原因 + 下一步。”目录不存在:/work/alpha(来源类型为本地目录)→ 检查路径或改选其他目录”。

计数:数字在前,单位在后,用千分位。”共 1,204 条发现”。

11.5 AI 生成内容的标注

AI 审核的输出永远带「AI 生成 · 待验证」标注,并且与静态分析结论分开存放。

要求 说明
标注措辞固定 「AI 生成 · 待验证」,不要改写成”智能建议”“AI 洞察”这类更讨喜的说法
标注位置 卡片头、紧邻标题;不放脚注
不用 AI 的口吻 模型输出原样展示(pre-wrap 保留编号行),界面不替它加”我们建议”
不隐藏失败 AI 失败时就地显示原因与重试;不静默降级成”没有建议”
不影响结论 界面文案要让读者明白:AI 是解释层,影响清单与风险由静态分析决定

一个用户如果分不清哪句话是引擎算出来的、哪句话是模型写的,这个功能就是净损失。

11.6 禁忌


12. 验收清单

改完界面后逐条勾选。

视觉

布局

状态

无障碍

一致性


13. 已知偏差:规范与现状不一致

规范描述的是应该怎样。下面这些是核对源码后确认的现状与规范不一致的地方, 逐条可复现。它们不是被认可的写法,而是待偿还的债:

# 现状 规范要求 影响
1 侧栏标签分组折叠失效:渲染写 data-navtag-group,事件委托查 data-tag-group-toggle 数据属性名必须与委托选择器一致 多标签时侧栏无法折叠分组
2 .step-body(分析进度步骤容器)无对应 CSS 每个输出到 DOM 的类名都要有样式归属 步骤内容靠 .step-dot 的 margin 撑开,布局脆弱
3 .flash(更新卡高亮)无对应 CSS 同上 点「查看」跳转后没有位置提示
4 toast 传入 'danger' 语气,但样式只有 .toast.success / .toast.error 语气枚举与样式必须一一对应 危险类 toast 与中性 toast 视觉相同
5 输入控件无 :disabled 样式(分支下拉读取中被禁用) 禁用态必须与可用态可区分 用户以为下拉坏了
6 无字段级错误态(无 .field.error / aria-invalid) 见 8.2 错误分级 表单校验失败只能靠 toast,字段定位不到
7 浮层无滚动锁定、无焦点陷阱 见 7.16 弹窗打开时背景仍可滚动,Tab 会跑到弹窗外
8 ⌘K 未做输入框让位(Ctrl/⌘+B 做了) 见 10.2 两个全局快捷键策略不一致
9 「记住上次打开的页面」勾选框未接线 见 8.1 五种状态 控件存在但无效
10 .tr-impacts / .meta-row / .flow-arrow 有样式无使用者 样式表不留死规则 阅读样式表时误导(以为有用)
11 .empty-hint 只在 .picker-row 作用域下有样式,却在别处使用 组件样式应与其使用范围一致 部分空态提示无样式
12 .grid-2-even / .grid-3 有定义无使用者 样式表不留死规则 见 5.2 的说明
13 data-theme-pref 只写不读(CSS 与 JS 都无消费方) 属性要有消费方,否则是死状态 目前靠 localStorage + data-theme 已足够;该属性是预留
14 9 处 transition 未带 --ease,回落浏览器默认缓动;同类交互配方不一致(.row-edit 带、.row-delete 不带) 见 9.1 令牌纪律 相同交互的手感略有差异
15 滚动条只有 ::-webkit-scrollbar,无标准 scrollbar-* 属性 — Firefox 显示系统默认滚动条

这份清单怎么用:改到相关组件时顺手修一条,并在同一次提交里删掉对应行。 清单长度应该单调下降;如果它在变长,说明新增代码在制造新债。


14. 相关文档

文档 内容
界面实现映射 本文规范到文件、类名、数据契约的映射
设计取舍 为什么这些边界不做得更多
核心概念 契约、传播、风险与置信度
架构设计 分层与数据流
README · 界面 面向使用者的功能说明