
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/bytebaseBytebase 是一份面向“人与 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 保留共享尺寸契约。尺寸高度内边距文字图标内部间隙xs24px6px12/16px14px6pxsm28px8px12/16px16px6pxmd36px12px14/20px16px8pxlg40px16px14/20px20px8px各尺寸的语义定位md是普通表单与命令的默认sm用于密集工具栏、表格控件与重复行操作xs只留给异常紧凑的表面不是塞下过多动作的手段lg用于突出操作或宽松的新手引导流不用于普通仪表盘页面。等宽等高 MUST 使用size-*当共享组件不拥有该测量时。对应地扫描器设有no-button-dimension-override规则专门拦截对共享Button的固定高度/尺寸覆盖。排版角色驱动禁止任意值角色字号/行高字重Caption、错误、次级行文本12/16pxRegular正文、label、控件文本14/20pxRegular 或 medium字段标题16/24pxSemibold对话框/Sheet 标题18pxSemibold页面 section 标题24/32pxBold规则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/HSLMUST 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/fullCSS 侧仅允许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字段组内字段之间 24pxsection 有 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 页面布局 FormSectionFormFieldGroup多 section 表单处于 dirty 且操作可能滚出视口时 MUST 使用StickyActionFooter——页脚在表单变脏时出现MUST NOT 在未修改的设置页占常驻空间revert/cancel 在左、唯一主 save/update 在右主操作在 invalid 或 saving 时禁用Update 在未变化时也禁用有未保存变更的导航 MUST 在丢失代价高或出乎意料时警示用户只读、自动保存或平凡单字段页面 SHOULD NOT 使用粘性页脚。Sheet 表单与编辑生命周期Sheet 的必需结构SheetContent widthstandard SheetHeader SheetTitle{...}/SheetTitle SheetDescription{...}/SheetDescription /SheetHeader SheetBody FormFieldGroup{...}/FormFieldGroup /SheetBody SheetFooter Button appearancesecondary{...}/Button Button{...}/Button /SheetFooter /SheetContentSheetHeader与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 keyref 冻结最后打开的实体让内层表单在 Sheet 关闭动画约 200ms期间视觉稳定key则在新实体打开时强制全新挂载function CreateUserSheet(props: Props) { const { open, user, onClose } props; // openfalse 期间冻结实体让内层表单在 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 widthstandard 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 滑出屏幕约 200msBase UI 的 Dialog.Portal 已经处理了动画前后的挂载/卸载生命周期。Update 按钮必须等到 dirty 才启用——在内部表单组件挂载时捕获初始值这样反映的是刚挂载的实体 prop用useMemo比较当前状态与初始值算出isDirtyUpdate 门控在isFormValid isDirtyCreate 模式始终“脏”必填字段有效即可启用。打开编辑 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 使用标准档档位宽度用途narrow384px选择器、2–3 个短字段表单、紧凑只读详情standard704px默认创建/编辑流3–6 个字段wide832px嵌套表格、表达式构建器、标签页、短 wizard专业档只给既有工作流新使用必须在变更中给出具体理由不得仅为回避响应式设计而选档档位宽度专业用途panel500px紧凑工具或诊断面板medium640px介于 narrow 与 standard 之间的既有密集表单large1024px双区域配置或 schema 导向工作流xlarge1120px密集规则或表格编辑器huge95vw保留遮罩锚点的最大化编辑表面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 是 secondarynext/finish 是 primary且在步骤间不得换位后退时保留已完成的输入前进前先校验当前步骤错误放在对应字段旁带深链、长任务或大量复核内容的 wizard SHOULD 做成页面短上下文流 MAY 用宽 sheet。表格与列表工作流结构顺序资源表格按以下顺序组合1页面或 section header2工具栏搜索、过滤、视图控件、创建动作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-colorhex/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 #197502026-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),仅供参考