Bytebase 前端 UX 技术债清零迁移:一份从 814 处违规走向零容忍强制的完整执行指南
【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase
本文以 Bytebase 仓库中的前端 UX 技术债迁移计划(
docs/superpowers/plans/2026-08-24-frontend-ux-legacy-debt-migration.md)为主体,结合 UX 规范、静态扫描器与共享组件源码展开。你将看到:这 557 个违规指纹、814 处出现、横跨 218 个文件的债务如何被拆解为 12 个可独立审查的任务逐步清除;每个任务在什么约束下执行、产出什么、提交什么;以及最终的零债务状态下,扫描器如何从"基线比对"演进为"零容忍强制"。读完你可以直接复用这套"棘轮式"技术债治理方法论。
一、背景:UX 指南与"棘轮"机制
Bytebase 的 React 产品前端自 2026-03-30 引入(见docs/agents/frontend-ux.md的 Evolution References),随后通过 Base UI、shadcn 风格封装、Tailwind 语义 token 与 CVA 模式建立了统一的 UI 契约。这份契约的权威文本是docs/agents/frontend-ux.md,它声明自己是 Bytebase 产品 UI 的"canonical design and composition contract",适用于frontend/src/下所有 React 代码。
该规范最核心的设计理念是一个词:棘轮(ratchet)。规范原文指出:
The guideline is an incremental ratchet. New shared UI and every UI element modified by a change must follow it. Unrelated legacy violations may remain, but they are not examples to copy and must not increase.
也就是说:新代码和改动的 UI 必须遵守规范;历史遗留违规可以暂时存在,但只减不增。这份"允许存在"的债务被记录在检入仓库的基线文件frontend/scripts/ui-guideline-legacy-debt.json中——它是对未改动代码的"宽限额度",而非设计备选方案。
迁移计划的终极目标正是打破这个"允许存在"的状态:移除每一条当前的 UX 指南违规、删除临时的 legacy-debt 文件、让扫描器在零违规状态下强制运行。
技术栈与规范分层
计划文档明确列出了技术栈:React、TypeScript、Base UI、Tailwind CSS v4 语义工具类、CVA、StyleX、Vitest。而frontend-ux.md给出了"实现所有权"(Implementation Ownership)的分工表,这是理解所有迁移任务的前提:
| 关注点 | 归属 | 规则 |
|---|---|---|
| 行为与无障碍 | frontend/src/components/ui/下的 Base UI 封装 | 先用共享原语,再考虑原生控件或特性自有交互代码 |
| 变体 | 共享原语中的 CVA | 消费者需要相同视觉状态时添加具名变体 |
| 重复测量 | frontend/src/components/ui/styles.stylex.ts | 把稳定的控件/表单/行/布局测量放入 StyleX |
| 上下文布局 | Tailwind 工具类 | 使用语义、非任意的工具类做局部流式与响应式组合 |
| 颜色与主题 | frontend/src/assets/css/tailwind.css中的 token | 使用语义 token;禁止裸调色板颜色或手写dark:覆盖 |
| 页面组合 | 共享布局与下述配方 | 不要按特性重建页面、表单、Sheet、表格框架 |
规范还强调"不要引入第二套样式框架"——StyleX、Tailwind、CVA 是当前系统的互补部分。
二、债务全景:当前库存(Current Inventory)
计划以 2026-08-24 的frontend/scripts/ui-guideline-legacy-debt.json快照为基线,给出了完整的违规清单。这份清单不是随意枚举,而是按"规则 × 指纹 × 出现次数 × 涉及文件 × 迁移难度"五个维度建模:
| 规则 | 指纹 | 出现次数 | 文件数 | 难度 |
|---|---|---|---|---|
no-space-between | 9 | 9 | 4 | 简单,机械化替换 |
no-off-scale-gap | 27 | 30 | 27 | 简单,但需选择关系本意的间距 |
no-arbitrary-type | 15 | 22 | 15 | 简单到中等,层级决策 |
no-manual-dark | 5 | 6 | 1 | 简单,在复杂属主内做语义 token 替换 |
no-off-scale-radius | 51 | 86 | 43 | 普通框架简单,拼接控件中等 |
no-ad-hoc-sheet-width | 7 | 7 | 4 | 中等,工作流宽度决策 |
no-raw-color | 259 | 341 | 96 | 中等,视觉语义迁移 |
no-button-dimension-override | 58 | 84 | 50 | 中等;部分消费者需要共享方形按钮契约 |
no-raw-table | 5 | 6 | 5 | 中等到困难,取决于操作型还是嵌入型内容 |
no-native-control | 119 | 220 | 102 | 最难,因为原语替换后行为必须存活 |
no-literal-color | 2 | 3 | 2 | 困难,功能性颜色收尾 |
| 合计 | 557 | 814 | 218 个独立文件 |
一个关键洞察是债务的结构性分布:前 73 个文件只包含间距、排版、圆角、主题和语义颜色类债务,合计 150 个指纹、202 处出现。这些是"机械安全"的叶级样式迁移,计划要求它们在交互式迁移开始前全部完成——先把低风险的做了,把最难的no-native-control(220 处出现)和no-raw-color(341 处出现)留到后面,让每个任务都有独立的、可验证的产出。
三、可用的权威 API:Allowed APIs
迁移不是无中生有,而是"把特性代码迁到共享原语上"。计划文档给出了完整的允许 API 清单,每条都指向仓库中的真实实现:
- 基础与工作流配方:
docs/agents/frontend-ux.md中的 Foundations(间距、控件、排版、颜色、边框圆角焦点层级)与表单/表格/Sheet 工作流配方。 - 控件测量:
frontend/src/components/ui/styles.stylex.ts——只允许xs、sm、md、lg四档。从源码看,这四档的完整契约是:
| 档位 | 高度 | 内联 padding | 字号/行高 | 图标 | 内部 gap |
|---|---|---|---|---|---|
xs | 24px | 6px | 12/16px | 14px | 6px |
sm | 28px | 8px | 12/16px | 16px | 6px |
md | 36px | 12px | 14/20px | 16px | 8px |
lg | 40px | 16px | 14/20px | 20px | 8px |
- 命令按钮:
frontend/src/components/ui/button.tsx——使用Button、ButtonProps和buttonVariants,禁止原生命令按钮。源码显示其 CVA 提供了variant(default/destructive)、appearance(solid/outline/secondary/link)与size(default/xs/sm/md/lg)三个维度,default是md的别名。 - 文本输入:
frontend/src/components/ui/input.tsx、textarea.tsx、search-input.tsx、number-input.tsx。 - 选择控件:
select.tsx、combobox.tsx、checkbox.tsx、radio-group.tsx、switch.tsx、segmented-control.tsx。 - Sheet:
frontend/src/components/ui/sheet.tsx——具名宽度层级为narrow、panel、medium、standard、wide、large、xlarge、huge、workspace。从源码看,这些层级对应 384px、500px、640px、704px、832px、1024px、1120px、95vw 以及响应式上限 960px 的workspace。 - 表格:
frontend/src/components/ui/table.tsx——Table、TableHeader、TableBody、TableRow、TableHead、TableCell、TableEmptyView。源码确认TableHead内置了sortable、sortActive、sortDir、onSort排序协议和resizable拖拽调宽能力,TableEmptyView承载空态。 - 语义颜色:
frontend/src/assets/css/tailwind.css。
语义颜色 token 在tailwind.css中通过 CSS 自定义属性定义(均为 RGB 三元组形式,消费时经rgb(var(--color-x))使用),例如:
--color-accent: 79 70 229; /* #4f46e5 主操作与选中态 */ --color-main: 24 24 27; /* #18181b 主文本 */ --color-control: 82 82 91; /* #52525b 控件与次级文本 */ --color-control-light: 113 113 122; --color-control-bg: 243 244 246; --color-block-border: 229 231 235; --color-control-border: 209 213 219; --color-info: 37 99 235; /* #2563eb 状态色 */ --color-warning: 245 158 11; --color-error: 220 38 38; --color-success: 22 163 74; --color-background: 255 255 255; --color-overlay: 0 0 0;主题切换通过"主题作用域重定义这些语义 token"实现,因此规范禁止手写dark:变体——这就是no-manual-dark规则存在的根本原因。
四、全局约束:迁移的底线
计划为所有任务设定了全局约束(Global Constraints),它们是审查每个 PR 的标尺:
- 路径约定:任务作用域块中的路径相对于
frontend/,除非另有前缀。 - 行为保全清单:必须保留 API 调用、路由、权限、变更操作、加载状态、选择语义、键盘行为、拖拽/上传、焦点、编辑器命令与分页。
- 属主完整性:一个任务必须移除其直接编辑的每个文件的所有基线指纹——不能修一个 class 却留下同一属主内相邻的债务。
- 基线纪律:绝不手工增加或编辑
violations;只有在作用域报告为空后才运行node frontend/scripts/check-ui-guideline.mjs --write-baseline。 - 语义角色而非视觉猜测:主文本用
main,次级层级用control/control-light,表面用background/control-bg,边界用block-border/control-border,状态用状态 token。 - 禁止逃逸:不得直接从特性代码导入 Base UI 原语来躲避扫描器;不得用样式化的
div替换原生控件(须经共享原语保留原生语义);不得新增一次性语义 token、任意测量值、消费者自有的控件高度、手写暗色变体或内联字面颜色。 - 隐藏输入豁免:隐藏表单输入不是可见 UI,应由扫描器精确排除;文件输入仍在范围内,应使用共享包装。
- 可独立审查:每个任务独立可审查,且随该任务提交缩减后的债务文件。
这些约束回答了迁移中最常见的三个"捷径"诱惑:直接改基线、绕过共享原语、把 div 伪装成按钮——全部被明确禁止。
五、12 个任务的执行路线图
计划把整个迁移编排为 12 个任务,从"机械安全的叶级样式"逐步走向"行为敏感的共享控件、操作工作流、嵌入式渲染器与 SQL 编辑器表面"。下面逐一拆解。
Task 1:修复扫描器假阳性(Remove Scanner False Positives)
改动文件:frontend/scripts/check-ui-guideline.mjs、check-ui-guideline.test.mjs、ui-guideline-legacy-debt.json(经写入器)。
产出:原生<input type="hidden">元素被排除——因为它们不渲染任何 UI;可见输入与文件输入仍然强制。
这是一个"先写测试"的任务:先添加包含 hidden、file、text 三种输入的失败测试,断言只有 file 和 text 输入产生no-native-control;运行CI=true pnpm --dir frontend exec vitest run scripts/check-ui-guideline.test.mjs确认 hidden 输入被错误报告;然后在 JSX native-control 访问器中只跳过静态type属性恰好为hidden的 input(不跳过动态类型或type="file");重跑测试、写入缩减后的快照并运行扫描器;确认 OAuth consent 指纹减少 7 处且没有其他原生输入条目消失。提交信息为refactor(frontend): ignore nonvisual hidden inputs。
这个任务之所以排在第一位,是因为它直接决定了后续所有no-native-control迁移的噪音水平——如果 OAuth 表单的 hidden 字段一直报违规,Task 6 就永远无法"清空作用域报告"。
Task 2:迁移单出现 CSS-only 属主
文件:36 个各含 1 处出现的文件,包括src/app/layouts/SplashLayout.tsx、src/components/DashboardFrameShell.tsx、src/components/LearnMoreLink.tsx、src/components/monaco/MonacoEditor.tsx、src/components/UserAvatar.tsx、src/modules/schema-diagram/SchemaDiagram.tsx、src/modules/sql-editor/components/Welcome.tsx、src/routes/auth/PasswordForgotPage.tsx、src/routes/auth/SetupPage.tsx、src/routes/project/plan-detail/components/deploy/DeployStageCard.tsx等。
执行要点:
- 用
node frontend/scripts/check-ui-guideline.mjs --report-only捕获作用域报告,把确切的前置计数记入提交信息或 PR 说明。 - 按角色替换每个 token:间距使用最近记录的文档关系;文本使用
text-xs或text-sm及自有的行高;普通框架使用rounded-sm;裸颜色使用语义工具类。 - 对含行为逻辑的属主运行最近的既有测试;纯 token 的展示型属主以扫描器覆盖代替源码字符串测试。
- 预期减少:36 个指纹、36 处出现。提交为
refactor(frontend): remove singleton UX styling debt。
Task 3:迁移小型 CSS-only 属主
文件:28 个含 2~5 处出现的属主,例如src/components/ClassificationTree.tsx、src/components/DashboardSidebar.tsx、src/components/monaco/DiffMonaco.tsx、src/routes/auth/SigninPage.tsx、src/routes/workspace/profile/AccountSettingsPage.tsx、src/routes/workspace/two-factor/RecoveryCodesView.tsx等。
要点:编辑前运行聚焦的属主测试;使用与 Task 2 相同的角色映射迁移整个属主——拼接控件允许使用方向性rounded-l-sm/rounded-r-sm,普通卡片必须用rounded-sm;Monaco、树图标、状态图标和侧边栏的改动需目视或经组件测试检查,因为它们的颜色编码了含义。预期减少 64 指纹、82 处出现,提交为refactor(frontend): remove small UX styling debt。
Task 4:迁移集中式 CSS-only 工作流
文件:9 个(ReleaseInfoCard.tsx、IssueDetailApprovalFlow.tsx、IssueDetailCommentList.tsx、IssueDetailDatabaseCreateView.tsx、IssueDetailTaskRunTable.tsx、PlanDetailRollbackSheet.tsx、ProjectPlanDetailPage.tsx、ProjectGitOpsPage.tsx、ProjectMaskingExemptionPage.tsx)。
要点:编辑前运行 issue-detail、plan-detail、GitOps、masking 四个聚焦套件;替换作用域内所有裸颜色、任意排版、不支持圆角、超规模间距与space-*token,同时保留层级与状态含义;SQL 或任务状态颜色按状态(success/warning/error/info)映射而非旧色相;在桌面与窄宽度下验证 issue 与回滚表面布局。预期减少 50 指纹、84 处出现。Task 2-4 完成后,全部 73 个 CSS-only 属主即零债务。提交为refactor(frontend): unify concentrated workflow styling。
Task 5:补上缺失的方形按钮契约(The Missing Square Button Contract)
改动文件:frontend/src/components/ui/button.tsx、button.test.tsx、styles.stylex.test.tsx、check-ui-guideline.test.mjs。
产出:Button接受shape="square",而size继续拥有高度、宽度、内边距、排版、gap 与图标测量。
要点:
- 先添加失败测试:在
xs/sm/md/lg下渲染方形按钮,断言 24、28、36、40px 的方形契约且无消费者尺寸类。 - 扩展 Button CVA,加入
shape: { default, square }与尺寸/形状复合变体size-6、size-7、size-9、size-10,每个零内联 padding。 - 保持
shape="default"为默认,保留所有既有外观与意图。 - 新增扫描器测试:
<Button shape="square" size="sm" />通过,而<Button className="size-7" />失败。 - 明确禁止:不添加
size="auto"、appearance="unstyled"或任何无测量的逃逸口。既有h-auto、h-full、20px、22px、34px 消费者必须采用最近的文档化契约或重构布局。
提交为feat(frontend): add square button sizing。这个契约之所以必要,是因为债务清单中no-button-dimension-override有 84 处出现——大量图标按钮通过h-*/size-*覆盖共享按钮的高度,而正确做法是让共享组件直接支持方形变体。
Task 6:迁移应用与认证控件
文件:7 个(SessionExpiredSurface.tsx、EmailCodeSigninForm.tsx、PasswordSigninForm.tsx、UserPasswordFields.tsx、MultiFactorPage.tsx、OAuth2ConsentPage.tsx、PasswordResetPage.tsx)。
要点:可见的原生命令按钮换成Button,密码可见性操作换成shape="square",裸颜色/圆角换成语义角色。必须原样保留 OAuth 表单的原生 POST 动作与 hidden 字段名——Task 1 已让这些 hidden 输入对扫描器中立,无需替换;同时保留提交按钮 name/value、Enter 提交、禁用态与浏览器密码管理器行为。提交为refactor(frontend): unify authentication controls。
Task 7:迁移工作区资源与设置工作流
文件:15 个,覆盖src/routes/workspace/下的 EnvironmentsPage、GlobalMaskingPage、GroupsPage、LandingPage、MembersPage、RolesPage、ServiceAccountsPage、BrandingSection、SecuritySection、ThemePreview、UserPasswordSection、TwoFactorSetupPage、UserFormSheet 等。
要点:编辑前运行CI=true pnpm --dir frontend exec vitest run src/routes/workspace;原生选择换成Select/Combobox,可见文本/文件控件换成共享Input,命令换成带具名尺寸与形状的Button;Branding 与 Semantic Types 的文件输入accept、重置、ref 点击、同文件重选行为必须保留,替换原生节点前先添加聚焦上传测试;保留表格选择、角色/成员授权、环境重排、双因素恢复与表单脏状态。当前快照预期作用域为 63 指纹、100 处出现(Task 1 缩减前)。提交为refactor(frontend): unify workspace workflows。
Task 8:迁移项目 Sheet、表格与操作
文件:23 个,覆盖 ProjectIssueDetailPage、ProjectPlanDashboardPage、ProjectSettingsPage、ProjectSyncSchemaPage、DatabaseDetailHeader、TableDetailDialog、ImportRevisionSheet、IssueDetailActionBar、PlanDetailHeader、DeployTaskFilter、PlanStatusAction 等。
要点:把四个 ad hoc Sheet 契约换成具名宽度——Import Revision 与 Target Selection 保持wide(去掉消费者宽度类)、移动端 review sheet 用panel、pending-task 预览用narrow;两张 Table Detail 表与 Project Sync 表换成共享表格原语,保留可排序表头、列宽、行操作、滚动与空/加载态;文件输入换成共享Input(保留accept、multiple、拖拽、refs 与重置);命令按钮与尺寸覆盖换成具名Button尺寸与shape="square",保留 tab ARIA 与键盘导航;桌面与移动宽度下验证宽表与宽 Sheet。提交为refactor(frontend): unify project workflows。
Task 9:迁移共享与实例复合控件
文件:34 个,包括AccountMultiSelect.tsx、AdvancedSearch.tsx、AuditLogTable.tsx、ExprEditor.tsx、IssueTable.tsx、MarkdownEditor.tsx、Quickstart.tsx、sql-review/下四个文件、instance/下九个文件等。
要点:简单场景用共享控件;Advanced Search 与 ExprEditor 保留既有复合状态机,只把叶级 input/textarea/命令节点换成Input/Textarea/Button;Instance Assignment 换成共享表格原语且不改变水平溢出、选择与最小宽度行为;原生 select 换成Select,需搜索或自定义创建时用Combobox;不得为复合行添加通用 pressable 逃逸口——命令用Button,非命令行语义保持显式。Watermark.tsx留到 Task 12;本任务列出的每个文件离开本任务时都必须零债务。提交为refactor(frontend): unify shared composite controls。
Task 10:迁移 Schema、AI 与 Agent 嵌入式表面
文件:19 个,覆盖schema-editor/Panels/(IndexesEditor、PartitionsEditor、PreviewPane、TableColumnEditor、TableList)、schema-diagram/ER/TableNode.tsx、schema-diagram/Navigator/、modules/ai/下六个文件与modules/agent/下三个文件。
要点:编辑前运行四个模块目录的全部测试;schema-editor 的方形行操作通过共享 Button 契约统一且不改变周围网格尺寸;ER TableNode 换成共享表格原语但保留table-fixed、节点尺寸、端口、拖拽/选择行为与图专属密度;AgentChat 的 Markdown 表格渲染换成共享表格原语,用渲染器局部的紧凑 class 覆盖——不要把操作型表头高度强加给散文表格;AI/Agent 的原生控件与裸颜色在保留流式、中止、重试、历史选择、键盘提交、textarea 增长与覆盖层的前提下替换;原语替换前先为 Markdown 表格输出、ER 节点几何、提示键盘行为与历史选择添加聚焦测试。提交为refactor(frontend): unify embedded agent and schema UI。
Task 11:最后迁移 SQL 编辑器控件
文件:42 个,覆盖src/modules/sql-editor/下的 ConnectChooser、HistorySearchInput、VirtualDataTable、SharePopoverBody、SQLUploadButton、TabItem、TabList、TerminalPanel 等。快照中(排除 CSS-only 属主后)为 42 文件、58 指纹、71 处出现,多数是按钮尺寸;行为敏感尾部包括 ConnectChooser、HistorySearchInput、VirtualDataTable、SharePopoverBody、SQLUploadButton、TabItem、TabList 与 TerminalPanel。
要点:先在完整 SQL Editor 模块套件上运行并保存行为基线;按四个独立提交的子波次迁移——连接/工具栏控件、Schema/面板控件、结果视图控件、然后是标签/上传/终端;尺寸映射:20px 与 22px 控件换xs、28px 换sm、36px 换md、方形控件换shape="square"——不要为了迁就偶然的相邻测量而缩小本应共享的控件;原生搜索/文本/文件控件换成共享原语但保留 IME、Enter/Escape 处理、文件选择、焦点恢复、尺寸调整、虚拟化、剪贴板与编辑器快捷键;保留结果表单元格几何与虚拟化测量,共享 Button 样式不得改变行高或测量的画布/表格尺寸;每个子波次后运行聚焦测试,四个波次后运行完整 SQL Editor 套件。提交每个子波次。
Task 12:解决功能颜色并移除债务系统
改动文件:frontend/src/components/Watermark.tsx、frontend/src/assets/css/tailwind.css(仅当确实需要新语义角色时)、check-ui-guideline.mjs、check-ui-guideline.test.mjs;删除frontend/scripts/ui-guideline-legacy-debt.json;更新docs/agents/frontend-ux.md与frontend/AGENTS.md。
要点:
- 添加失败的 Watermark 测试:证明两个水印层仍生成带预期不透明度的非空 data URL,且主题解析不会产生无效 canvas 颜色。
- Task 9 期间把
AccountMultiSelect的字面蓝换成bg-info。 - Watermark 颜色从既有语义
error与controlCSS 通道解析,在 canvas 适配器中应用安全专属 alpha——不得把字面 RGB 字符串搬到另一个 TypeScript 文件,也不得添加扫描器忽略路径。 - 运行
node frontend/scripts/check-ui-guideline.mjs --report-only并断言输出No UI guideline violations found。 - 添加零债务状态下无债务文件运行的失败集成测试。
- 简化扫描器:普通模式对任何违规失败、对零违规成功;移除基线比对、陈旧条目处理、元数据与
--write-baseline支持。 - 删除
ui-guideline-legacy-debt.json,更新权威文档与 Agent 指南为"零容忍强制"。 - 即使移除基线比对测试,也要为每条规则保留单元覆盖。
- 提交为
refactor(frontend): enforce zero UX guideline debt。
至此,债务系统完成了它的全部生命周期:记录债务 → 缩减债务 → 归零 → 拆除债务记录机制本身。
六、扫描器源码级原理:它到底在检查什么
理解迁移计划的最好方式,是读懂执行者——frontend/scripts/check-ui-guideline.mjs。它自称"conservative static scanner, not a complete Tailwind evaluator",即一个保守的静态扫描器,不是完整的 Tailwind 求值器。
规则引擎与正则词汇
扫描器定义了legacyRules(7 条历史规则)与enforcedRules(12 条,按字母排序),并维护两组批准词汇:
- 间距:
approvedGapValues = {"0","1","1.5","2","3","4","6","8"}——对应产品间距词汇 4/6/8/12/16/24/32 中的可用档位与相邻档位。 - 圆角:
approvedRadiusValues = {"none","xs","sm","full"},CSS 侧为0、var(--radius-xs)、var(--radius-sm)、var(--radius-full)。 - 裸颜色家族:
rawColorFamilies覆盖 slate/gray/zinc/neutral/stone 到 rose/black/white 全部 Tailwind 调色板,配合rawColorProperties(accent/bg/border/caret/divide/fill/outline/placeholder/ring/shadow/stroke/text)组成rawColorPattern。 - 字面颜色:
literalColorPattern匹配十六进制(#[\da-f]{3,8})与rgb/rgba/hsl/hsla函数形式,但排除var(--...)引用——这正是语义 token 体系合法性的来源。
每个 token 依次经rulesForToken判定,可同时命中多条规则(例如space-x-2命中no-space-between,bg-gray-100命中no-raw-color,dark:bg-gray-900命中no-manual-dark,gap-[10px]命中no-arbitrary-gap,text-[13px]与leading-[18px]命中no-arbitrary-type)。
特殊访问器
除通用 token 扫描外,扫描器还有四个专用访问器:
scanSheetWidth:对SheetContent的 className 检查w-/min-w-/max-w-前缀,命中即no-ad-hoc-sheet-width——强制消费端使用width变体(如width="wide")。scanButtonDimensions:对非共享原语文件中的Button检查h-/size-覆盖,但豁免h-auto(内容派生高度)与断点前缀响应式覆盖——这正是文档中"h-auto可用于内容派生高度"与"布局属主可用标准断点前缀类应用完整响应式尺寸契约"两条规则的机器化。scanLiteralColor:只检查 CSS 颜色属性(color或以 Color 结尾的属性)的字面值。scanInlineRadius:检查内联样式对象中的borderRadius、borderTopLeftRadius、border-bottom-right-radius等,命中即no-off-scale-radius——测试用例中4px、50%、var(--card-radius)均被判违规。
基线比对与棘轮语义
核心是compareWithBaseline:把当前违规与基线按[path, rule, token]键比对,产生三类问题——new(新增或计数增长)、stale(基线里有但当前已消失,即债务已被移除却未更新基线)、shared(共享原语内的违规,永远不许进基线)。getBaselineUpdateIssues则保证--write-baseline只能"删除既有债务",若新指纹或已强制规则的计数增长会直接以退出码 1 拒绝。
check-ui-guideline.test.mjs用 18 个测试用例固化了这套语义,包括"允许语义工具类与共享组件"、"拒绝新/变更指纹"、"拒绝债务移除后的陈旧基线条目"、"拒绝共享原语豁免"、"仅当债务缩减时允许基线更新"。这些测试既保护扫描器自身,也充当了计划 Task 1 与 Task 12 的行为基线。
三个运行模式
| 模式 | 命令 | 行为 |
|---|---|---|
| 普通(棘轮) | node frontend/scripts/check-ui-guideline.mjs | 比对基线,违规或陈旧条目则以退出码 1 失败 |
| 报告 | node frontend/scripts/check-ui-guideline.mjs --report-only | 打印全部当前指纹,零违规输出No UI guideline violations found |
| 写基线 | node frontend/scripts/check-ui-guideline.mjs --write-baseline | 仅当作用域报告为空时重写快照(含版本、规则与违规) |
七、迁移的度量标准:每个任务的预期收缩
计划为几乎每个任务都标注了精确的预期减少量,构成了一张可核对的"进度表":
| 任务 | 预期指纹减少 | 预期出现减少 |
|---|---|---|
| Task 2(36 个单出现属主) | 36 | 36 |
| Task 3(28 个小属主) | 64 | 82 |
| Task 4(9 个集中工作流) | 50 | 84 |
| Task 7(15 个工作区文件) | 63(Task 1 缩减前) | 100(Task 1 缩减前) |
| Task 11(SQL 编辑器尾部) | 58(剩余) | 71(剩余) |
配合 Task 1 的 7 处 OAuth hidden 输入缩减,所有数字在最终状态下归零——而 557 指纹的完整清单以frontend/scripts/ui-guideline-legacy-debt.json为唯一事实来源,每一次--write-baseline都必须发生在对应任务作用域报告为空之后。
八、最终验证:零债务状态的验收清单
Task 12 之后,计划给出了完整的最终验证序列:
CI=true pnpm --dir frontend fix CI=true pnpm --dir frontend check CI=true pnpm --dir frontend type-check CI=true pnpm --dir frontend test node frontend/scripts/check-ui-guideline.mjs --report-only git diff --check附加的人工核对项:
- 确认
frontend/scripts/ui-guideline-legacy-debt.json不存在。 - 确认没有特性文件直接导入 Base UI 控件来躲避共享原语。
- 确认没有新语义 CSS token 与既有角色重复。
- 审查 Tasks 7-11 改动的每个 Sheet、表格、表单与编辑器工作流的桌面/移动截图。
- 已知无关的
frontend/src/react/结构失败若仍存在须单独处理——不得被本次迁移掩盖,也不得被误判为 UX 回归。
九、方法论总结:这套迁移计划可以复用到任何前端项目
回看整个计划,它其实是"规模化技术债治理"的一个可移植模板:
- 先有契约,后有扫描器:
docs/agents/frontend-ux.md定义语义(spacing 词汇、控件尺寸、语义 token、工作流配方),check-ui-guideline.mjs把可客观化的子集变成机器规则。 - 债务显式化:
ui-guideline-legacy-debt.json把"违反规范"从口头批评变成可审计、可缩减、不可增长的数据。 - 棘轮机制:基线只减不增,
--write-baseline被实现为"只能删除既有债务"的操作,杜绝"顺手洗白"。 - 按风险排序:先机械安全的 CSS-only 迁移(73 个文件),再共享原语增强(方形按钮契约),再交互工作流,最后行为最敏感的 SQL 编辑器——每个任务独立审查、独立提交、有预期数字。
- 以测试锁定行为:每个原语替换前先补聚焦测试(上传行为、Markdown 表格、ER 几何、键盘行为),确保"原语换了,行为活了"。
- 终态是系统自毁:债务归零后,删除债务文件、简化扫描器为"零违规即通过",把治理从"允许存量"推进到"零容忍"。
这套"先测量、再分层、后归零"的路径,正是 Bytebase 把 814 处 UI 违规清出代码库的完整方法论,也回答了本文开头的问题:从"可以欠债"到"不允许欠债",迁移计划本身就是一份可执行、可验证、可复用的工程路线图。
如需深入,推荐按以下顺序阅读仓库资料:计划全文(docs/superpowers/plans/2026-08-24-frontend-ux-legacy-debt-migration.md)→ 规范(docs/agents/frontend-ux.md)→ 扫描器与测试(frontend/scripts/check-ui-guideline.mjs与check-ui-guideline.test.mjs)→ 共享原语(frontend/src/components/ui/下的 button、sheet、table、styles.stylex)→ 语义 token(frontend/src/assets/css/tailwind.css)→ 债务快照(frontend/scripts/ui-guideline-legacy-debt.json)。
【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考