- 后端
- 前端
【免费下载链接】habitica
A habit tracker app which treats your goals like a Role Playing Game.
本文以 Habitica 仓库中的 z-index-overview.md 为骨架,结合前端源码逐项验证 Habitica 客户端各类浮层组件的层级设计与覆盖规则。读者将掌握 Habitica 中进度条、创建任务菜单、加载屏、模态框、顶部工具栏、横幅、下拉菜单与通知的 z-index 取值及其背后的设计意图,并了解在 Vue + Bootstrap 4 项目中维护浮层层级的实操方法。
一、为什么需要一份 z-index 清单
在复杂单页应用中,浮层组件(弹窗、下拉菜单、通知、加载屏)往往由不同文件、不同开发者在不同时期引入。CSS 的 z-index 数值本身只决定"同一层叠上下文内"的先后顺序,一旦缺少统一规划,就容易出现"通知被弹窗遮住""进度条被导航栏盖住"之类的层叠事故。
Habitica 用一份集中式的表格文档(z-index-overview.md)记录当前所有关键浮层的 zIndex 取值与所在文件,作为开发者修改样式时的"参照系":任何新增浮层组件,都应当对照该表选择数值,避免与既有组件冲突。表格完整内容如下:
| 类型 | zIndex | 所在文件 | |:-|:-|:-| | 进度条(Progress Bar) | 1600 | app.vue | | 创建任务菜单(Create App Menu) | 999 | create-task.scss | | 加载屏(Loading Screen) | 1050 | loading-screen.scss | | 模态框(Modals) | 1350 | modal.scss | | 工具栏(Habitica 菜单) | 1080 | menu.vue | | 工具栏(弹窗打开时) | 1035 | — | | 顶部横幅(工具栏之上) | 1300 | base.vue | | 顶部横幅(弹窗打开时) | 1035 | — | | 工具栏下拉菜单 | 1000 | .dropdown-menu | | 通知 | 999 | notifications.vue |
从数值分布可以直观看出 Habitica 的层级设计分层思路:数值从高到低大致对应"全局进度条 → 模态框 → 顶部横幅 → 工具栏 → 加载屏/工具栏下拉菜单 → 创建任务菜单/通知",各层之间留有 10~250 的间隔余量,为后续新组件预留了插入空间。
二、层级最高的进度条(1600)
进度条是全局唯一使用z-index: 1600的元素,且带!important修饰:
/* 出自 website/client/src/app.vue 第 104 行 */ #nprogress .bar { z-index: 1600 !important; /* Must stay above nav bar */ }对应的实现是 nprogress 库(Habitica 通过axios-progress-bar加载其样式,见 user-main.vue 中@import '~/axios-progress-bar/dist/nprogress.css',并在 app.vue 与 user-main.vue 中同步覆写)。nprogress 自身的 CSS 已定义了基础样式,Habitica 又在 progress-bar.scss 中把进度条颜色定制为品牌紫色($purple-400)并固定高度 4px。
之所以把它抬到 1600,是"确保在模态框(1350)与导航栏(1080)之上"——网络请求期间顶部进度条必须始终可见,即使此时弹窗或菜单正处于打开状态。这也是 z-index-overview 表中唯一一处注释写明"Must stay above nav bar"的数值,属于全局最高优先级浮层。
三、模态框与工具栏的"弹窗联动降级"机制
Habitica 层级设计中最精妙的一点是:弹窗打开时,工具栏(1080→1035)与顶部横幅(1300→1035)会主动降级,让出空间给模态框。
3.1 模态框 1350
模态框的 z-index 定义在 modal.scss:
.modal { z-index: 1350; padding-left: 0px !important; }同时,app.vue 与 user-main.vue 都对.modal-backdrop(Bootstrap 模态框的半透明遮罩)做了定制,将不透明度提高到0.9并改用品牌紫背景色$purple-100,保证遮罩之下的页面内容被明显压暗。
3.2 工具栏 1080,弹窗打开时降为 1035
工具栏即顶部导航条,模板中通过id="habitica-menu"标记(menu.vue),其.topbar类在 menu.vue 中设置:
.topbar { z-index: 1080; background: $purple-100 url(@/assets/svg/for-css/bits.svg) right top no-repeat; min-height: 56px; box-shadow: 0 1px 2px 0 rgba($black, 0.24); }弹窗打开时(Bootstrap 会在<body>上添加.modal-open类),通过非 scoped 样式降级:
/* menu.vue 第 450-452 行 */ body.modal-open #habitica-menu { z-index: 1035; }3.3 顶部横幅 1300,弹窗打开时同样降为 1035
顶部横幅(促销、礼包、隐私提醒等公告条)由 base.vue 统一定义:
/* 非 scoped:弹窗打开时 */ body.modal-open .habitica-top-banner { z-index: 1035; } /* scoped:常规状态 */ .habitica-top-banner { width: 100%; padding-left: 56px; z-index: 1300; }横幅默认 1300 高于工具栏的 1080,因此正常状态下横幅覆盖在工具栏之上;而弹窗打开时两者都降到 1035,低于模态框的 1350——这保证模态框、遮罩层与页面其他内容之间的层叠关系始终正确,同时横幅与工具栏在弹窗场景下也不会盖住弹窗内容。
实际使用中,这些横幅组件(如 damagePaused.vue、gemsPromo.vue、giftPromo.vue、privacy.vue)通过插槽机制复用 base.vue 的布局与样式,因此降级规则只需在 base.vue 中维护一次,所有横幅统一生效。
四、加载屏与下拉菜单(1050 / 1000)
加载屏定义在 loading-screen.scss:
#loading-screen, #loading-screen-inapp { z-index: 1050; position: absolute; top: 0; bottom: 0; right: 0; left: 0; display: flex; justify-content: center; align-items: center; background-color: $purple-100; color: $white; font-size: 16px; line-height: 1.5; }它同时覆盖全屏与"应用内"两种加载场景,居中展示加载动画(#melior配合fadeColor关键帧做 2.4s 循环的呼吸闪烁效果)。
工具栏下拉菜单(Habitica 菜单展开后的子菜单)使用 1000。该值来自 Bootstrap 4 的默认样式:Habitica 前端引入的是 Bootstrap 4.6(见 bootstrap.scss 中@import '~/bootstrap/scss/bootstrap',依赖声明见 package.json 中"bootstrap": "^4.6.0"),其.dropdown-menu默认z-index: 1000。注意:由于下拉菜单的 1000 高于工具栏的 1080 降级值 1035,弹窗打开时下拉菜单仍可能浮现在降级后的工具栏之上,这是表格中值得留意的一个细节。
加载屏 1050 与下拉菜单 1000 之间、以及它们与工具栏(1080)之间数值非常接近,说明这两个组件在层级设计上处于"工具栏附近"的中间地带。
五、创建任务菜单与通知(999)
两个组件都使用 999,处于表格中的最低档:
- 创建任务菜单:定义于 create-task.scss。
.create-task-area采用position: absolute定位(right: 12px; top: -24px),z-index 为 998——注意源码实际值是 998,而 z-index-overview 表中记录为 999,表格与代码存在 1 的偏差,以当前源码为准。该菜单负责新建习惯、每日任务、待办与奖励四种任务的入口(对应.task-icon中icon-habit、icon-daily、icon-todo、icon-reward四个图标)。 - 通知:定义于 notifications.vue。
.notifications容器position: fixed; right: 10px; width: 350px,源码注释明确写着z-index: 999; // to keep it above modal overlays。
值得留意的是,通知的注释语义是"保持在模态框遮罩之上",但其 999 实际上低于模态框 1350。结合 Bootstrap 4 的层叠规则可以推断:这里"above modal overlays"更准确的含义是指**高于弹窗打开时降级后的工具栏与横幅(1035 之下接近的位置)**或指与其他 999 级组件的共存关系,从数值上看它并不能真正盖住 1350 的模态框本身。这是从代码注释与数值对比中可以观察到的文档与实现之间的张力点,修改该组件层级前需先确认实际覆盖需求。
六、全局视角:Habitica 的 z-index 分布与维护建议
综合 z-index-overview.md 表格与上述源码验证,可将 Habitica 当前浮层层级整理为一张纵向分布图:
1600 全局进度条(nprogress,#nprogress .bar) 1350 模态框(.modal) 1300 顶部横幅(.habitica-top-banner,常规状态) 1080 工具栏(.topbar,常规状态) 1050 加载屏(#loading-screen / #loading-screen-inapp) 1000 工具栏下拉菜单(Bootstrap .dropdown-menu 默认值) 999 通知(.notifications) 998 创建任务菜单(.create-task-area,源码实际值) 1035 弹窗打开时工具栏与顶部横幅的降级值此外,源码中还散落着少量不在表格内的局部 z-index(如 tasks/user.vue 中的 9999、itemPopover.vue 与 spells.vue 中的 1080),它们在各自组件内部形成独立的层叠上下文,不影响全局分层。
对于在 Habitica 上继续开发的工程师,维护建议如下:
- 新增浮层组件前,先查阅 z-index-overview.md,在已有数值的间隙中选值,避免与既有浮层撞车;
- 遵循"弹窗打开时降级"模式:工具栏与横幅均通过
body.modal-open选择器把 z-index 降到 1035,新增的页头级浮层应复用这一约定; - 注意表格与代码可能的漂移:本文验证发现创建任务菜单的源码值是 998 而非表格记录的 999,说明文档需要随代码同步维护,引用时以源码为准;
- 不要轻易改动 1600 进度条:它是全局最高层,任何新的"必现浮层"如果要超过它,需要同时评估是否真的必须盖住加载进度条。
七、结语
Habitica 用一份 10 行表格管理了客户端最核心的浮层层级,其设计核心是"模态框优先 + 弹窗联动降级":进度条 1600 永远可见,模态框 1350 居中,工具栏与横幅在弹窗打开时主动让位到 1035,通知与创建菜单处于 999 档。本文逐项核对源码后确认,该表格与实现整体一致,仅在创建任务菜单(998 vs 999)处存在 1 的偏差。理解这张表,就能在 Vue + Bootstrap 4 的浮层世界里精准定位每一层的位置,避免层叠事故。
- 后端
- 前端
【免费下载链接】habitica
A habit tracker app which treats your goals like a Role Playing Game.
相关推荐
突破WPF弹窗层级困境:FlyoutsControl的z-index管理完全指南
突破WPF弹窗层级困境:FlyoutsControl的z index管理完全指南 你是否曾在WPF开发中遭遇弹窗被遮挡、层级混乱的尴尬?当多个悬浮面板(Flyo
桌面应用UI组件gsd-2 前端 UI 指南:伪元素 z-index 分层详解与负层级防盖字实践
gsd 2 前端 UI 指南:伪元素 z index 分层详解与负层级防盖字实践 本文基于 gsd 2 仓库内置的 userinterface wiki 技能规
人工智能AI Agent代码智能体Agent 编排CLIAI 应用vux 中的 v-transfer-dom 指令:把弹层自动挂载到 body,根治移动端弹窗 z-index 与遮罩层级问题
vux 中的 v transfer dom 指令:把弹层自动挂载到 body,根治移动端弹窗 z index 与遮罩层级问题 导读 v transfer dom
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考