Bytebase 前端 UX 规范解析:React 产品界面的设计契约、尺寸体系与自动化 Ratchet 机制
【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase
Bytebase 是一份面向“人与 Agent 共同操作的数据库治理工具”的产品,其界面必须为高频重复操作、扫描比对和安全操作而优化,而不是营销式排版。本文以仓库中的正式契约 docs/agents/frontend-ux.md 为主体,结合 check-ui-guideline.mjs 扫描器、sheet.tsx 等共享原语源码,完整还原这份前端 UX 指南的设计决策、尺寸/间距/颜色体系,以及“棘轮式(ratchet)”自动化强制机制——读完后你能掌握 Bytebase 产品 UI 的完整构图契约,并理解它是如何用静态扫描器把规范钉死在 CI 级别的。
这份契约的定位与“棘轮”原则
该指南自称是 Bytebase 产品 UI 的canonical design and composition contract(正式设计与构图契约),适用于frontend/src/下的 React 代码,并与 AGENTS.md 和 frontend/AGENTS.md 中的代码风格与所有权规则互补。
它采用incremental ratchet(增量棘轮)模型,这是理解整份文档的钥匙:
- MUST / SHOULD / MAY 要求语言:MUST 对新 UI 与被直接修改的 UI 强制生效;SHOULD 是默认项,偏离必须在变更说明中给出具体理由;MAY 只表示“受支持的选项”,不是“推荐的默认值”。
- 新增共享 UI 与任何被改动的 UI 元素都必须遵循本指南;无关的历史违规可以残留,但不得增加,也不是可以效仿的例子。
- 一份签入仓库的扫描器基线记录了既有债务——它只是对“未触碰代码”的容忍,不是设计替代方案。
产品性格:操作型工具的 UI 取向
Bytebase 被明确定义为“operational database-development tool”。产品 UI 必须为重复劳动、扫描、比对与安全操作优化,具体要求:
- 信息密度高但组织有序(dense but organized);
- 偏好可预测的导航与持久化上下文;
- 主操作与破坏性操作必须无歧义;
- 避免装饰性卡片、超大页面标题、不传达状态或层级的视觉效果;
- 禁止卡片套卡片:页面 section 是无边框的布局区域,卡片只用于重复条目或真正需要框体的工具。
实现所有权:StyleX、Tailwind、CVA 各管一摊
文档用一张职责表划清了三层样式系统的边界——这是它防止“第二套样式框架”泛滥的核心手段:
| 关注点 | 责任人 | 规则 |
|---|---|---|
| 行为与可访问性 | frontend/src/components/ui/中的 Base UI 包装 | 使用原生控件或功能自有的交互代码之前,先使用共享原语 |
| 变体(Variants) | 共享原语中的 CVA | 调用方需要相同视觉状态时,添加命名 variant |
| 重复测量 | styles.stylex.ts | 稳定的控件、表单、行与布局测量值放入 StyleX |
| 上下文布局 | Tailwind 工具类 | 局部流式与响应式组合用语义化、非任意值工具类 |
| 颜色与主题 | tailwind.css 中的 token | 使用语义 token;禁止原始色板色与手动dark:覆盖 |
| 页面构图 | 共享布局与下文 recipe | 每个功能不得自建 page、form、sheet 或 table 框架 |
从源码结构看,这套分工是真实落地的:例如 ProjectPageLayout.tsx 中的页面框架测量值(16px 内容节奏、8px 工具栏间隙)全部通过stylex.create表达,而局部流式布局交给 Tailwind 类名——正是“重复测量进 StyleX、上下文布局进 Tailwind”的范例。文档同时强调:不要引入第二套样式框架,StyleX、Tailwind 与 CVA 是同一系统的互补部分。
基础令牌体系(Foundations)
间距:7 级标尺 + gap-only 规则
产品间距词表为4 / 6 / 8 / 12 / 16 / 24 / 32px,对应的关系-间隙契约:
| 关系 | 间隙 |
|---|---|
| 图标与相邻标签 | 4–6px,随控件尺寸 |
| 相邻控件与按钮 | 8px |
| 表单 label/title 与其控件 | 6px |
| 同一字段内的控件 | 8px |
| 一个 section 内的相关内容 | 16px |
| 一个表单组内的字段 | 24px |
| 页面主要区域 | 24–32px |
强制规则:
- MUST 使用
gap-*/gap-x-*/gap-y-*表达兄弟间距; - MUST NOT 使用
space-x-*/space-y-*; - MUST NOT 引入
gap-[10px]这类任意 gap 值; - 页面布局遵循 16px 内容节奏,使用
WorkspacePageLayout与ProjectPageLayout提供的 padding 变体,不得另加竞争性的外层包裹。
扫描器侧,check-ui-guideline.mjs 中维护了一个白名单approvedGapValues = {0, 1, 1.5, 2, 3, 4, 6, 8}(即 Tailwind 空格单位对应的 4/6/8/12/16/24/32px),任何 off-scale 的 gap 都会触发no-off-scale-gap规则。
控件尺寸:sizeprop 是完整契约
共享控件上的sizeprop 选定的是完整的基础契约——调用方 MUST NOT 覆盖基础尺寸,也不得独立缩放受管图标;布局所有者 MAY 用标准断点前缀类应用完整的响应式尺寸契约,但不得只覆盖高度。内容自定尺寸的复合控件 MAY 使用h-auto;纯图标控件 MUST 保留共享尺寸契约。
| 尺寸 | 高度 | 内边距 | 文字 | 图标 | 内部间隙 |
|---|---|---|---|---|---|
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 |
各尺寸的语义定位:md是普通表单与命令的默认;sm用于密集工具栏、表格控件与重复行操作;xs只留给异常紧凑的表面,不是塞下过多动作的手段;lg用于突出操作或宽松的新手引导流,不用于普通仪表盘页面。等宽等高 MUST 使用size-*(当共享组件不拥有该测量时)。对应地,扫描器设有no-button-dimension-override规则,专门拦截对共享Button的固定高度/尺寸覆盖。
排版:角色驱动,禁止任意值
| 角色 | 字号/行高 | 字重 |
|---|---|---|
| Caption、错误、次级行文本 | 12/16px | Regular |
| 正文、label、控件文本 | 14/20px | Regular 或 medium |
| 字段标题 | 16/24px | Semibold |
| 对话框/Sheet 标题 | 18px | Semibold |
| 页面 section 标题 | 24/32px | Bold |
规则:MUST 使用与信息层级匹配的角色;MUST NOT 引入text-[...]、leading-[...]任意值;MUST NOT 让字号随视口宽度缩放或使用负字距。文本必须通过换行、截断或有意约束来控制,不能与相邻控件重叠,也不能把固定格式表面撑变形。
颜色:只用语义 token,禁止裸色板
颜色层全部走 CSS 自定义属性支撑的语义工具类:
| 语义 | 示例 token |
|---|---|
| 主操作与选中态 | accent、accent-hover、accent-text |
| 主文本 | main、main-hover、main-text |
| 控件与次级文本 | control、control-light、control-placeholder |
| 表面 | background、control-bg、control-bg-hover |
| 边框 | block-border、control-border |
| 状态 | info、warning、error、success及其 hover token |
| 遮罩 | overlay+ 透明度修饰 |
硬性禁令:
- MUST NOT 在新 UI 或改动过的 UI 中使用
text-gray-600、bg-blue-50、text-white这类裸色板工具类; - MUST NOT 在语义 token 可表达角色时使用字面 hex/RGB/HSL;
- MUST NOT 添加手动
dark:变体——主题作用域会重新定义语义 token,暗色适配是 token 机制的“免费副产品”; - 真正新的语义角色应进入 tailwind.css;一次性的色板选择不应进入。
扫描器侧对应三条规则:no-raw-color(内置了 slate/gray/red/blue/black/white 等 24 个原始色族 + 11 个颜色属性前缀的正则匹配)、no-literal-color(匹配#rgb、rgb()/rgba()/hsl()/hsla()字面量)、no-manual-dark。
边框、圆角、焦点与层级
rounded-xs用于紧凑控件,rounded-sm用于普通带框表面,rounded-full只用于圆形控件、头像与胶囊;- 控件用
border-control-border,区域边界用border-block-border; - 交互元素 MUST 有可见的键盘焦点态,优先使用共享原语拥有的焦点行为;
- 层级使用 frontend/AGENTS.md 中描述的
overlay、agent、critical三个语义层族;功能代码 MUST NOT 用裸 z-index 建立全局堆叠,也不得直接 portal 到document.body。圆角同样受扫描约束:Tailwind 侧仅允许none/xs/sm/full,CSS 侧仅允许0与var(--radius-xs/sm/full),否则触发no-off-scale-radius。
工作流表面选择:先选 Surface,再写组件
| 需求 | 表面 |
|---|---|
| 保留列表上下文的多字段资源创建/编辑 | Sheet |
| 多 section、需要持久路由的设置或编辑 | 全页面表单 |
| 确认、破坏性确认、单字段输入、只读结果 | Dialog或AlertDialog |
| 长的、值得独立路由的多步工作流 | 全页面 wizard |
| 受益于父级上下文的短多步工作流 | 宽 sheet wizard |
核心纪律:不要仅仅因为 Dialog 实现更小就选 Dialog——表面选择必须来自任务复杂度、用户上下文与预期导航。
Dialog 默认值契约:DialogContent与AlertDialogContent默认带p-6内边距,不要再加内层 padding 包裹,需要时直接在 content 元素上用p-*覆盖;DialogContent默认是宽内容尺寸(max-w-[max(48rem,55vw)]),更小的对话框传max-w-*(必要时加w-*)。组件默认值里不要放2xl:max-w-*这类响应式变体——tailwind-merge 无法用调用方的无修饰工具类替换它们,结果是在宽屏上静默胜出。
表单工作流
共享表单解剖
表单 MUST 在共享表单原语能支撑所需行为时使用它们:
<FormSection title={...}> <FormFieldGroup> <FormField title={...} description={...}> <Input ... /> </FormField> </FormFieldGroup> </FormSection>节奏契约:字段 label/title 区与控件之间 6px;同一字段的控件之间 8px;字段组内字段之间 24px;section 有 24px 垂直内边距与 16px 内部节奏。大屏上 section header 占 section 宽度的 25%、内容占剩余部分;小屏上两者堆叠、16px 间距。FormLabelMUST 用htmlFor关联其控件;校验 MUST 紧邻受影响的字段,不能只靠 toast 或禁用按钮解释非法输入;必填、禁用、pending、服务端错误 MUST 在不依赖颜色的情况下依然可理解。
密集水平表单(多选项连接表单)
面向数据库连接这类多选项场景,MAY 通过共享表单原语使用水平字段,section 标题保持在字段上方(而不是新增第三列放标题):
- 标签列一致、控件列弹性;普通控件保持
md,靠布局与渐进披露省空间; - 标签是否堆叠依据表单宽度决定,独立于导航侧栏断点;复合控件 MAY 在字段本身堆叠之前先在控件列内换行;
- 描述与校验放在它们解释的控件旁边;两种布局下 label 与 radio 组都要有可访问名称;
- 决定后续字段的选项放最前;认证、密码来源、同步等选择放在
SegmentedControl中保持可见;较长的 provider 标签 MAY 在控件内换行,前提是每段依然清晰可用; - 密码来源选择器 MAY 与直接密码输入同行;外部来源在下方展开配置;切换来源时保留各自的草稿,提交只提交当前来源;TLS、SSH、IAM 等依赖配置在控制项下方用嵌套流展开,而不是“框中框”;
- 安全模式保持可见,选中时展开其依赖字段;开关与分段控件对齐到控件列起点;普通连接行保持 16px 节奏;“同步全部/所选数据库”这类模式用显式选择;
- 空的可选集合 MAY 以“添加”动作起步,但已有条目与校验错误 MUST 保持可发现;
- 创建连接的页脚 MAY 把 Test Connection 放在 Create 旁边、Cancel 放左侧;测试反馈 MUST 保持可见且可达。
页面表单(Page Form)
当设置多 section、需要稳定 URL、或属于更大详情页的一部分时使用页面表单:
<ProjectPageLayout> <ProjectPageContent> <FormSection ... /> <FormSection ... /> </ProjectPageContent> {isDirty && ( <StickyActionFooter left={...} right={...} /> )} </ProjectPageLayout>规则要点:MUST 组合对应的 workspace/project 页面布局 +FormSection+FormFieldGroup;多 section 表单处于 dirty 且操作可能滚出视口时 MUST 使用StickyActionFooter——页脚在表单变脏时出现,MUST NOT 在未修改的设置页占常驻空间;revert/cancel 在左、唯一主 save/update 在右;主操作在 invalid 或 saving 时禁用,Update 在未变化时也禁用;有未保存变更的导航 MUST 在丢失代价高或出乎意料时警示用户;只读、自动保存或平凡单字段页面 SHOULD NOT 使用粘性页脚。
Sheet 表单与编辑生命周期
Sheet 的必需结构:
<SheetContent width="standard"> <SheetHeader> <SheetTitle>{...}</SheetTitle> <SheetDescription>{...}</SheetDescription> </SheetHeader> <SheetBody> <FormFieldGroup>{...}</FormFieldGroup> </SheetBody> <SheetFooter> <Button appearance="secondary">{...}</Button> <Button>{...}</Button> </SheetFooter> </SheetContent>SheetHeader与SheetFooter保持可见,只有SheetBody滚动;三部分使用 24px 水平内边距与 16px 垂直内边距,页脚按钮 8px 间隙;每个 sheet 必须有可访问的SheetTitle(仅当已有可见标题传达同样信息时才用视觉隐藏标题);Create 在必填字段有效时启用,Update 额外要求 dirty 状态;嵌套 select/menu/popover MUST 用它们的 portal 选项或其他共享 overlay 原语,不得用临时 z-index 抬升。
编辑 Sheet 的生命周期(always-mounted 模式的正确写法)
这是指南中最具实战价值的一段——它解释了为什么“标准写法”会出 bug:当 Sheet 通过<Sheet open={open}>常驻挂载时,useState初始值只在首次挂载执行,切换到另一行实体(点击另一行的 Edit)不会重新填充字段。正确模式是外层包装 + 内层表单 + 稳定实体 ref + key:ref 冻结最后打开的实体,让内层表单在 Sheet 关闭动画(约 200ms)期间视觉稳定,key则在新实体打开时强制全新挂载:
function CreateUserSheet(props: Props) { const { open, user, onClose } = props; // open=false 期间冻结实体,让内层表单在 Sheet 关闭动画中视觉稳定。 // Base UI 的 Dialog.Portal 在动画结束后卸载,表单随之卸载。 const openEntityRef = useRef(user); if (open) { openEntityRef.current = user; } const stableUser = openEntityRef.current; return ( <Sheet open={open} onOpenChange={(next) => !next && onClose()}> <SheetContent width="standard"> <UserForm key={stableUser?.name ?? "new"} user={stableUser} onClose={props.onClose} onCreated={props.onCreated} onUpdated={props.onUpdated} /> </SheetContent> </Sheet> ); } function UserForm({ user, ... }: InnerProps) { // useState 初始值直接读 `user`——内层组件每次打开都全新挂载,永远是新鲜的 const [title, setTitle] = useState(user?.title ?? ""); // ... }三个必须遵守的推论:
- 不要用
{open && ...}守卫内层表单——那会在关闭动画一开始就卸载它,留下一个空白 sheet 滑出屏幕约 200ms;Base UI 的 Dialog.Portal 已经处理了动画前后的挂载/卸载生命周期。 - Update 按钮必须等到 dirty 才启用——在内部表单组件挂载时捕获初始值(这样反映的是刚挂载的实体 prop),用
useMemo比较当前状态与初始值算出isDirty,Update 门控在isFormValid && isDirty;Create 模式始终“脏”,必填字段有效即可启用。 - 打开编辑 sheet 前先取全量实体——列表 API 常返回部分对象,同步缓存查找(如
store.getX(id))可能只返回带 name/email/title 的 stub,嵌套字段(如workloadIdentityConfig.subjectPattern)为 undefined;行点击处理器应使用异步getOrFetchX形式,保证 Sheet 拿到完全水合的实体。
从源码结构看,这一模式已在仓库中落地:openEntityRef模式出现在 EditUserSheet.tsx、GroupsPage.tsx、ServiceAccountsPage.tsx 与 CreateWorkloadIdentitySheet.tsx 中,说明它不是纸面示例而是既有实现惯例。
Sheet 宽度:9 档固定档位,禁止临时宽度
SheetContent 的widthvariant 在源码中是明确的像素契约(narrow: "w-[24rem]"、standard: "w-[44rem]"等)。新的普通产品流程 MUST 使用标准档:
| 档位 | 宽度 | 用途 |
|---|---|---|
narrow | 384px | 选择器、2–3 个短字段表单、紧凑只读详情 |
standard | 704px | 默认创建/编辑流,3–6 个字段 |
wide | 832px | 嵌套表格、表达式构建器、标签页、短 wizard |
专业档只给既有工作流,新使用必须在变更中给出具体理由,不得仅为回避响应式设计而选档:
| 档位 | 宽度 | 专业用途 |
|---|---|---|
panel | 500px | 紧凑工具或诊断面板 |
medium | 640px | 介于 narrow 与 standard 之间的既有密集表单 |
large | 1024px | 双区域配置或 schema 导向工作流 |
xlarge | 1120px | 密集规则或表格编辑器 |
huge | 95vw | 保留遮罩锚点的最大化编辑表面 |
workspace | 响应式,上限 960px | 带手机/平板行为的 workspace 式编辑器 |
调用方 MUST 使用widthvariant,不得对SheetContent施加w-*、min-w-*、max-w-*;只有当共享契约需要新的可复用尺寸时才新增/修改档位。扫描器规则no-ad-hoc-sheet-width会拦截一切临时宽度。
对话框与破坏性操作
Dialog用于短阻塞操作,不用于多 section 资源表单;AlertDialog用于需要显式确认的破坏性确认;- Dialog 内容默认 24px 内边距,不要再加内层 padding shell;
- 主操作在阅读顺序中最后;破坏性操作用 destructive variant 并精确命名动作;
- Dialog 与 alert-dialog 内容 MUST 有可访问标题,后果不明显时还要有简洁描述。
Wizard
- 使用共享 step indicator 与稳定页脚操作;
- Back 是 secondary,next/finish 是 primary,且在步骤间不得换位;
- 后退时保留已完成的输入;
- 前进前先校验当前步骤,错误放在对应字段旁;
- 带深链、长任务或大量复核内容的 wizard SHOULD 做成页面;短上下文流 MAY 用宽 sheet。
表格与列表工作流
结构顺序
资源表格按以下顺序组合:1)页面或 section header;2)工具栏(搜索、过滤、视图控件、创建动作);3)需要时的状态区(错误或非阻塞通知);4)表格/列表内容;5)可选分页页脚;6)选中行后的选择操作条。工具栏 MUST 用sm/md尺寸共享控件 + 8px 动作间隙;搜索与过滤保持视觉成组,创建动作留在工具栏末端;运营型表格 SHOULD 使用可用页面宽度,不得居中塞进营销式窄列;边框 +rounded-sm容器 MAY 框住真正受包含的表格,但不得把表格包进嵌套卡片、也不得让外层 section 变成浮动卡片。
表格测量
- 表头行高 40px;
- 默认单元格 16px 水平、12px 垂直内边距;
- 交互菜单/列表行 32px(紧凑)或 36px(默认)最小高度,14/20px 主文本,8px 内部间隙;
- 数值右对齐;选择列与纯图标列保持窄;
- 名称与主标识左对齐并占据剩余宽度;
- 长单行标识用截断 + tooltip(或其他查看完整值的方式);
- 多行内容是有意的换行,在需要稳定行高时不得不可预测地改变列几何。
加载、空态与错误态
- 初始加载使用稳定的表格骨架或加载区域,MUST NOT 短暂闪出空态;
- 加载更多保留既有行,只禁用续载动作;
- 无过滤的空资源列表解释缺失原因,并在用户有权限时给出对应的创建动作;
- 过滤结果为空时保留过滤条件并提供清除过滤动作,不以创建资源为首要补救;
- 拉取错误保留有用上下文,可恢复时提供重试;
- 空、错误、权限态使用共享状态组件,而不是临时居中卡片。
分页契约
无分页时:只渲染工具栏与表格,不渲染页脚;不渲染禁用的分页控件或无法改变结果的每页行数选择器;客户端排序/过滤仅在完整有界结果集已加载、且不会给用户“服务端全局搜索”错觉时才可接受。
有分页/加载更多时:使用usePagedData与PagedTableFooter作为标准契约;搜索、过滤、排序、页大小 MUST 是后端请求输入;其中任何一个变化都会先重置行、续载状态与缓存查询身份再加载第一页;加载更多 MUST 复用同一组查询输入,只追加结果,失败时不清除已显示行;PagedTableFooter放在页面布局页脚或表格的无框页脚区,不得复制它的每页行数/加载更多控件;无剩余页的表格 MAY 保留每页行数控件,但不再显示禁用的加载更多按钮。
选择与行操作
- 行与全选使用共享
Checkbox; - 全选语义 MUST 显式:当前可见页、已加载行、还是整个过滤结果——不得暗示超出 API 能操作范围的选中;
- 批量操作用共享选择操作条;
- 空间允许时让最高频的安全行操作保持可见;次级与破坏性动作放进共享下拉菜单;
- 破坏性批量动作需要确认并说明受影响数量;
- 窄屏下动作 MAY 移入溢出菜单,但选择状态与主操作必须保持可发现。
响应式表格
保留“识别行 + 执行主任务”所需的列;在把主标识缩到失去意义之前,先隐藏或移走次级元数据;真正的表格化对比用横向滚动,不得仅因移动端就把运营数据表拆成互不相关的卡片;粘性表头/列 MAY 在显著改善长宽对比时使用,其层级保持表格局部,且低于语义 overlay。
响应式与状态检查清单
在认为一个 UI 工作流完成之前,指南要求逐项验证:
- 最长本地化标签能容纳或换行,不与相邻 UI 重叠;
- 按钮组以水平、垂直 8px 间隙换行;
- 固定格式控件、看板、工具栏与行具有稳定尺寸;
- Sheet 在手机上仍可用,且从不超出视口宽度;
- 内容滚动时,页面与 sheet 的操作保持可达;
- 工作流可能进入的 loading、empty、error、disabled、dirty、saving、success 状态都已定义;
- 键盘焦点顺序遵循视觉任务顺序;
- 权限禁用的动作按产品授权模式一致地隐藏或解释。
自动化强制:UX Ratchet 的扫描器与债务基线
规范如何不被腐化,是这份指南最有工程特色的部分。完整前端检查与 UX ratchet 的运行方式:
pnpm --dir frontend testnode frontend/scripts/check-ui-guideline.mjs扫描器(见 check-ui-guideline.mjs 文件头注释:“Enforces the objective subset of docs/agents/frontend-ux.md”)当前强制执行 13 条规则,分为两类:
- legacy 规则(对应历史债务基线):
no-ad-hoc-sheet-width、no-arbitrary-gap、no-arbitrary-type、no-manual-dark、no-native-control(禁止绕过共享原语直接用<button>/<input>/<select>/<textarea>,共享原语目录豁免)、no-raw-color、no-space-between; - 全量强制规则(在 legacy 之上追加):
no-button-dimension-override(拦截对共享 Button 的固定尺寸覆盖)、no-literal-color(hex/rgb/hsl 字面量)、no-off-scale-gap、no-off-scale-radius、no-raw-table(禁止裸<table>,表格必须走共享表格原语)。
技术实现上,扫描器用 TypeScript Compiler API 静态解析frontend/src下的 TS/TSX(提取 className 与模板字面量中的 token,识别断点前缀与!important 修饰),用 PostCSS 解析 CSS 的圆角声明与@apply。签入仓库的 ui-guideline-legacy-debt.json 按文件 + 规则 + token + 出现次数记录每条临时例外指纹。
从compareWithBaseline与getBaselineUpdateIssues的源码可以确认棘轮的三个硬性语义:
- 共享原语零豁免:任何落在
src/components/ui/前缀下的违规直接判定为shared类问题,永远不允许进基线——“共享原语必须被修好”,债务机制只保护功能代码; - 只能减不能增:普通检查模式把当前违规与基线逐指纹比对,新出现的(
new)或数量变化的指纹即失败; - 写基线同样受审:
--write-baseline模式在写入前调用getBaselineUpdateIssues,拒绝记录新指纹或已强制规则的计数增长——命令自己会退出并列出“请先修复”的违规。
node frontend/scripts/check-ui-guideline.mjs --write-baseline基线文件自身携带三条自我说明(description、updateCommand、removalCondition),其中移除条件明确写着:“所有违规修复后,删除本文件及基线处理逻辑”。指南最后一条纪律是:永远不要通过编辑基线来授权新债务;要么修 UI,要么当规则本身错误时,把正式指南、扫描器与测试一并修改并给出显式设计理由。
同时指南诚实标注了扫描器的边界:它无法判断某个 sheet 是否正确的表面、某张表的列优先级是否合理、粘性页脚是否必要——自动化检查通过时,评审人与 Agent 依然 MUST 应用工作流 recipe。
演进脉络与当前权威
从文档的演进参考(Evolution References)可以看到这份契约是“React 产品前端统一工作的累计结果,而不只是最近一个月的产物”:PR #19750(2026-03-30)引入 React 产品前端;PR #19758 建立 Base UI、shadcn 风格包装、Tailwind 语义 token 与 CVA 模式;PR #20012 及后续 overlay 工作确立共享交互与层级行为;PR #20500 及后续完成 React-only 产品方向;PR #20743 引入 StyleX 做类型化共享测量;PR #20750 继续 UI 一致性工作;PR #20942 确立当前路由所有权与前端护栏;PR #21056 统一 step 与粘性页脚行为。
文档最后强调:历史计划文档保留迁移上下文,但 docs/agents/frontend-ux.md 与 frontend/AGENTS.md 才是当前的实现权威。
小结
这份指南的价值不在单条规则,而在三层结构:设计判断(产品性格、表面选择、recipe 构图)、可枚举契约(7 级间距、4 档控件、9 档 sheet 宽度、语义 token 表,均可静态判定)、棘轮式强制(扫描器 13 条规则 + 只减不增的债务基线 + 共享原语零豁免)。对维护者而言,它的用法是:写 UI 前先按“工作流表面选择表”定表面,按 recipe 用共享原语组合,最后跑node frontend/scripts/check-ui-guideline.mjs让机器守住客观下限——而主观质量(表面是否合适、列优先级、粘性页脚是否必要)留给评审。
【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考