尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Bytebase React 覆盖层分层策略:overlay / agent / critical 三层语义化 z-index 治理与实践

Bytebase React 覆盖层分层策略:overlay / agent / critical 三层语义化 z-index 治理与实践 Bytebase React 覆盖层分层策略overlay / agent / critical 三层语义化 z-index 治理与实践【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase导读Bytebase 正在从 Vue/Naive UI 向 React 全面迁移而页面上最特殊的 React 表面是页面 Agent 窗口可拖动、可缩放、可最小化并能在用户浏览应用时持续监督 DOM 操作。本篇文章完整解析 Bytebase 于 2026-04-14 定稿的 React 覆盖层Overlay分层策略设计文档它用overlay/agent/critical三个语义化全局层族取代零散的z-index临时值同时给出指针事件、焦点与Escape的交互优先级规则。读完本文你将掌握该策略的层模型、所有权规则、Portal 架构、原语分工、迁移与测试方案并能对照仓库中的真实实现如 layer.ts理解其落地细节。该设计文档位于 2026-04-14-react-overlay-layering-policy-design.md仓库中已存在与之一一对应的实现代码与测试用例本文将以设计文档为主体、源码为佐证的方式展开。背景与动机为什么单一的z-50不够用在策略制定时Bytebase 的 React 覆盖层处于部分标准化、但尚未策略驱动的状态文档 Current State 一节共享的 React 覆盖层原语统一协调在z-50附近AgentWindow外壳当时使用更低的z-40部分 React 表面仍在使用一次性数值例如z-[60]、z-[999]。这种单一共享z-50家族的模型无法表达目标产品行为当 Agent 正在操作页面时它必须保持在普通应用对话框之上。普通对话框Dialog/Sheet是模态的若 Agent 与它们共享同一 z 层就会出现两种失败要么 Agent 被对话框盖住无法监督操作要么对话框无法在 Agent 之下正常显示。值得注意的是仓库现状已经将该设计完整落地。例如 layer.ts 中已经不存在z-50/z-40级别的全局协调取而代之的是三组常量化的语义家族根节点与数值而 sheet.test.tsx 中甚至加入了断言确保 Sheet 源码不再出现\bz-50\b这种历史遗留写法。策略决策固定的小规模语义全局层族Bytebase React 采用固定的四个语义全局层Policy Decision 一节层族用途定位base普通页面渲染不参与全局覆盖层仲裁overlay标准应用覆盖层Dialog、Sheet、Popover 等agent页面 Agent 窗口及其所有从属覆盖层critical强制会话过期 / 重新登录 UI优先级是严格的critical agent overlay base并且明确约定未经显式策略变更不得引入额外的永久全局层。这确保了层族数量保持在可维护的最小集合内避免每个功能各自协商局部z-index造成军备竞赛。当前实现中的层族常量设计落地后的 layer.ts 定义了如下常量export const LAYER_ROOT_ID { overlay: bb-react-layer-overlay, agent: bb-react-layer-agent, critical: bb-react-layer-critical, watermark: bb-react-layer-watermark, } as const; export const LAYER_Z_INDEX { overlay: 2500, agent: 2600, critical: 7000, watermark: 7100, } as const;可以看到数值已完全内部化overlay(2500) agent(2600) critical(7000) 严格递增与设计文档的优先级完全一致。仓库实现还额外引入了设计文档未强制要求的watermark7100家族专门用于企业版水印——其数值被刻意放在critical之上见 layer.test.tsx 中keeps the critical layer above the legacy Naive overlay stack与keeps the watermark above blocking app surfaces两条测试水印不拦截任何交互因此不与三层交互策略冲突。分层模型详解base普通页面渲染base包含页面内容、布局骨架layout chrome、吸顶页头页脚以及组件内部的绘制顺序。它不参与全局覆盖层仲裁——所有全局层的 z 值都远高于页面内容页面自身的内部排序由组件自己负责。overlay标准应用覆盖层overlay是绝大多数产品表面的默认家族覆盖DialogSheetAlertDialogSelectDropdownMenuPopoverTooltip这些表面相对于应用本身保持模态但不允许盖住或禁用 agent 层。也就是说一个打开着的应用对话框可以正常捕获应用内部的焦点但 Agent 窗口仍在其上方保持可见、可交互。agent全局监督者家族agent是专门为页面 Agent 设计的家族包含完整的AgentWindow最小化后的 agent 启动按钮launcher从 agent UI 内打开的对话框、菜单、Select、Tooltip 等其他覆盖层该家族保持在普通应用覆盖层之上并在应用覆盖层打开时仍然保持可交互——这是本策略唯一被刻意保留的、违背常规模态行为的例外其目的正是让用户能够监督 Agent 正在进行的 DOM 操作。critical强制认证恢复critical保留给强制认证恢复场景会话过期 UI重新登录 UI在进一步交互之前强制完成从该 UI 打开的子覆盖层critical是唯一被允许盖住并禁用 agent 的家族。仓库实现中SessionExpiredSurface.tsx 通过BaseDialog.Portal container{getLayerRoot(critical)}将重新登录面板挂入 critical 根节点并在面板内嵌SigninPageallowSignup{false}同时提供独立的 logout 入口。该表面由 SessionExpiredSurfaceGate.tsx 在应用根布局 RootLayout.tsx 中挂载与 auth 拦截中间件authInterceptorMiddleware.ts协同触发。所有权规则由 surface owner 决定而不是数值层所有权Layer Ownership由表面所有者决定而不是由某个功能想要的z-index数值决定。规则如下常规产品页面只能使用 app 覆盖层原语agent UI 只能使用 agent 专属原语认证恢复流程只能使用 critical 专属原语调用方不得通过自定义类或内联样式把某个表面提升到更高家族子覆盖层继承打开它的父表面所属家族。文档给出的三个判定示例在设置Dialog内打开的Select仍属于overlay从AgentWindow打开的确认对话框属于agent从重新登录面板打开的 tooltip 属于critical。这条规则的价值在于谁拥有表面谁决定层归属彻底切断了用数值比拼的路径让代码评审可以基于组件边界而非魔法数字进行。Portal 架构每个家族一个专属 portal root设计文档要求每个家族在document.body下拥有专属 portal rootapp overlay rootagent rootcritical root这些根节点让跨家族的优先级确定且可检查deterministic and inspectable。产品代码不应直接选择 portal root——设计系统原语会自动选择正确的根节点。仓库中的 layer.tsensureRoot实现了单例 稳定顺序const ensureRoot (family: LayerFamily) { const id LAYER_ROOT_ID[family]; const existing document.getElementById(id); if (existing) { return existing as HTMLDivElement; } const root document.createElement(div); root.id id; root.dataset.bbLayerFamily family; root.style.position relative; root.style.zIndex String(LAYER_Z_INDEX[family]); root.style.isolation isolate; // 按 ORDERED_FAMILIES 顺序插到下一个已存在根节点之前否则 append ... };每个根节点同时设置position: relative、对应家族 z-index 与isolation: isolate保证跨家族优先级稳定getLayerRoot(family)是产品代码获取根节点的唯一入口。测试 layer.test.tsx 明确验证了四个根节点的创建顺序overlay→agent→critical→watermark与document.body.children的顺序一致、各自 z-index 值以及重复调用返回同一实例单例。家族内部的排序规则是同一家族内后挂载later-mounted的 portal 渲染在先前挂载的 portal 之上策略只依赖家族内的挂载顺序不依赖跨家族顺序。为了支撑嵌套模态的挂载顺序语义实现中为同一家族内的表面与背景统一了内部层级// Backdrops and popups share one intra-family stack level so nested modal // layers can rely on portal mount order: child backdrop above parent surface, // child popup above child backdrop. export const LAYER_SURFACE_CLASS z-10; export const LAYER_BACKDROP_CLASS z-10;见 layer.ts——同一个家族内的表面和背景共享z-10子级模态依靠 DOM 挂载顺序自然叠在父级之上。原语模型app / agent / critical 三套原语设计系统应暴露独立的三套原语家族或包装器app primitives标准产品覆盖层agent primitivesagent 专属覆盖层critical primitives认证恢复覆盖层。大多数团队只应该使用 app 原语家族agent 与 critical 原语刻意保持狭窄仅供少数拥有这些表面的模块使用。数值层级numeric layer values对设计系统内部保持私有。仓库中的落地情况与之一致App 原语统一挂载到getLayerRoot(overlay)例如 dialog.tsxBaseDialog.Portal container{getLayerRoot(overlay)}并调用usePreserveHigherLayerAccess(overlay)、alert-dialog.tsx、context-menu.tsx、combobox.tsx 等Agent 原语位于 frontend/src/modules/agent/components/ui 目录下目前有AgentDialog.tsx、AgentDropdownMenu.tsx、AgentTooltip.tsx三个专用组件AgentWindow中删除聊天等确认框正是用AgentDialog实现的见 AgentWindow.tsxCritical 原语的代表是 SessionExpiredSurface.tsx直接使用getLayerRoot(critical)作为 portal container并用LAYER_BACKDROP_CLASS/LAYER_SURFACE_CLASS组织背景与面板。交互模型指针事件、焦点与Escape设计文档强调策略必须治理的不仅是绘制顺序还有指针事件、焦点和键盘关闭Escape。这是最容易看起来对、用起来错的部分。指针事件指针事件交给指针下最高的可见家族app 背景层和对话框不得拦截本应指向可见 agent UI 的点击当critical可见时它先于agent与overlay拦截交互。焦点与模态app 覆盖层在 app 家族内部保持模态agent 存在于 app 家族模态域之外critical 家族相对一切都是模态的。因此会出现一种精心设计的状态普通应用对话框仍为应用捕获焦点而用户能同时把 Agent 当作独立的更高优先级表面进行交互。Escape 处理Escape遵循活动表面 家族优先级最顶层的活动表面获得第一次处理权first refusal若更高家族处理了Escape更低家族不再响应只有更高家族拒绝处理时底下的 app 覆盖层才可能收到Escape。这避免了 agent 交互时误关底层应用对话框。实现层面usePreserveHigherLayerAccess(family) 承担了关键辅助职责当一个较低家族表面如 app Dialog打开时它会移除更高家族根节点上的aria-hidden/inert/data-base-ui-inert等可访问性遮蔽属性并在更高家族出现活动内容后通过MutationObserver持续揭示它们——这从可访问性与焦点域两个维度保证了 app 模态打开时 agent/critical 层依旧可见、可达。对应的行为测试见 dialog.test.tsx断言打开 app Dialog 后 agent 根节点与 critical 根节点的aria-hidden、inert、data-base-ui-inert均为空。允许的局部z-index局部z-index仍允许用于组件内部绘制顺序例如吸顶表格表头sticky table headers列宽调整手柄resize handles图标 / 徽标分层icon or badge layering选中高亮selection highlights。但仅当该值不试图压过页面上另一个表面时才被允许如果某个z-index的意图是赢过页面其他表面它就必须进入层系统而不是留在功能代码里。仓库中如 column-resize-handle.tsx 的z-10、InfoPanel.tsx 的吸顶z-10、MonacoOverlayWidget.tsx 的node.style.zIndex 10均属于此类组件内部排序符合策略边界。护栏GuardrailsReact 功能代码不得使用裸的全局z-*class 压过其他表面使用内联zIndex进行跨表面排序创建功能自有的顶层 portal root添加临时逃生舱口例如z-[9999]未经策略评审引入额外的全局家族。如果某个表面无法用现有策略表达说明设计系统契约不完整应在设计系统层集中修复而不是在功能里绕行。迁移策略文档推荐的六步迁移路线引入语义化层 token 与专属 portal root将现有共享 app 覆盖层原语转换到overlay家族将AgentWindow、最小化启动按钮及 agent 从属子覆盖层转换到agent家族在critical家族中构建共享的会话过期 / 重新登录表面增加针对临时 React 全局z-index的评审与 lint 护栏迁移现有 React 离群值到所属原语与家族 portal。对照仓库现状这六步已基本完成第 1 步由 layer.ts 的LAYER_ROOT_ID/LAYER_Z_INDEX/getLayerRoot落地第 2 步体现在dialog、alert-dialog、context-menu、combobox等统一挂载overlay根第 3 步体现在 AgentWindow.tsx 中最小化 launcher 与完整窗口都通过createPortal(..., getLayerRoot(agent))挂载且 agent 专属原语集中在一个ui/子目录第 4 步由 SessionExpiredSurface.tsx 完成第 5、6 步由测试与既有离群值清理体现如 sheet.test.tsx 禁止z-50的回归断言。测试策略最小行为覆盖设计文档要求的最低行为覆盖包括overlay家族内标准 app 覆盖层叠在 app 覆盖层之上agent 外壳高于 app 对话框与 Sheetagent 子对话框 / 菜单高于 app 覆盖层最小化 agent 启动按钮高于 app 覆盖层critical 会话过期 UI 高于 agentcritical 子覆盖层高于 agent指针路由指针在底层 app 背景之上时命中 agentEscape在overlay/agent/critical之间的优先级app 模态与 agent 同时存在时的焦点行为。仓库测试与之一一对应层结构与数值由 layer.test.tsx 覆盖可访问性 / 焦点域跨家族保持由 dialog.test.tsx 覆盖离群值回归由 sheet.test.tsx 覆盖。此外 AgentWindow.test.tsx 对 agent 窗口本身的交互做了行为级测试。风险与对策文档识别了四条主要风险每条都对应明确的约束家族所有权若不被原语强制功能会用局部数值绕过策略 → 对策产品代码只能走三套原语portal root 若不显式跨家族顺序会退化为偶然的挂载行为 → 对策getLayerRoot单例 稳定 DOM 顺序交互优先级若描述不足视觉顺序正确但键盘与指针行为损坏 → 对策本文档对指针、焦点、Escape三者的显式规则critical 家族若不严格收敛其他团队会开始申请高于 agent的例外 → 对策critical 是唯一例外任何新增全局家族需策略评审。结论Bytebase 采纳了严格的三家族 React 覆盖层策略专属 portal root、语义化所有权、显式交互优先级。这是与目标产品行为匹配的最小模型——标准 app 覆盖层保持自洽agent 在监督页面自动化时始终位于最上层而强制重新认证是唯一允许盖住并禁用 agent 的表面。任何比这更宽松的方案都会在 Vue-to-React 迁移过程中再次退化为一轮轮的临时z-index升级竞赛。仓库中 layer.ts 及其测试已将该策略固化为可执行的工程契约后续 React 功能开发只需遵循选对家族、用对原语、不碰全局数值三条准则即可。【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表