SANSUI的博客

系统外观
© Sansui 2026
All rights reserved
Sansui的博客
人活着就是为了卡卡西

z-index 设计与海浪模型

2026年1月7日
阅读评论

摘要:本文针对前端中多个元素层叠时 z-index 管理混乱的问题,提出一个名为海浪模型的设计模型,以构建复杂 z-index 管理的最佳实践。

……好像 AI。但我是人,我纯手打的。

基础知识 - 代码的画布

层叠上下文是代码逻辑上的画布。一旦一个元素产生了层叠上下文,它内部的所有子元素就被包裹在这个范围里。

层叠上下文的产生

  • 根元素:<html>
  • 定位 + 层级:positionrelative / absolutez-index 不是 auto
  • 固定定位:position: fixedsticky
  • CSS3 属性:
    • opacity 小于 1。
    • transform 不为 none(比如旋转、缩放)。
    • filter(滤镜)、flex/grid 子元素的 z-index 等。

❗注意:层叠上下文的产生,和层内元素关系,是两套规则,两者之间没有关系,没有关系,没有关系。因为部分规则相似,很容易给记混了。

层叠内元素关系

为了好解释,我使用 context-level 说明绝对的上下覆盖关系。

  • CL-1: 背景和边框(最底层)
  • CL-2: 定位元素(如absolute)且负 z-index
  • CL-3: 块级元素(static 定位的普通 div 等)
  • CL-4: 浮动元素(float)
  • CL-5: 内联/行内块元素(文字、图片)
  • CL-6: 定位元素(如absolute)且 z-index 没有设定为负,还有 opacity,transform 等。
  • CL-7: 定位元素(如absolute)正 z-index(最顶层)

z-index 在其中的作用相当于是一个算层叠关系的插件。必须和定位配套使用,否则不生效。

从功能上说,层叠上下文才是真正面向用户的Z轴,z-index属性只是这个 z轴的一个插件。

视觉画布

可以发现,被提升到 CL-6 的层,其规则和产生一个层叠上下文高度重合,就像产生了一个新的画布。这也是为什么说,容易把 CL-6 层等同于产生层叠上下文的层,但是不是的,CL-6 是描述自己作为子元素的层级,而“产生层叠上下文”描述的自己作为父元素的行为。

  • opacity 且 z-index: 0。位于 CL-3,产生了层叠上下层文,但是其实层级很低,z-index 是无效的。
  • CL6: relative 且 z-index: auto。位于 CL-6,产生不了层叠上下文,其子元素如果有更低的层级,可能会被盖掉。子元素有更高的层级,会盖掉别人。
  • CL6: relative 且 z-index: 0。位于 CL-6,产生不层叠上下层文,其子元素层级和当前元素严格一致。

理解了这一点,可以记住。在做目录头时,并不是设置了 relative 就万事大吉了,如果你的后面的元素也是 relative,很容易被覆盖。

z-index 划分

考虑到我们不可能把所有画布内元素全挤在 CL-6,全靠 DOM 关系划分层级,因此需要设计 CL-7 的层级,也就是正 index 区域。

正 z-index 的画布内元素应处于同一个层叠上下文内。如果不在同一个,则按产生了新的层叠上下文设计。

基础逻辑

对于一个画布(层叠上下文)内,我设计新产生的画布设计上分100层。对应 z-index 0(CL-6)到 z-index 100。每 20 层为一个全屏大画布,比如 Modal。也就是

  • 0-20:画布内组件层级
  • 20、40、60、80、100:新画布层级

那 21-39 有什么用?没有用,不要用。只是为了提醒你,这个东西是个大画布。如果把画布内组件设定到了 20 以上,你可能就不知道什么东西溢出到了 Modal。

此外,如果真的想做一些在不同层级上换来换去的小组件,也可以根据模型的定义很快决定放到哪个层级,非常方便。

海浪模型

想象一个海星被 5 层海浪(大画布)冲到沙滩上,每一层海浪上有自己的生物群。

大部分生物都挤在海底(0层以下)层,形成了基石。到第 0-19 层有一堆小东西争先恐后地彰显存在感,但是你又觉得不那么重要。之后的20层、40层、60层通常而言清澈透明,但一旦染色后,就会将下面的生物变得不见天日。

此时你觉得这个海星,你应该看到它吗?应该放在哪一层?

通常而言,层级设计遵循以下基础逻辑。

  • 大尺寸元素的层级低小,小尺寸元素的层级高。
  • 重要性高的层级高。

第一条是一个视觉规律,第二条则是功能需要。

0-19 层

根据基础的逻辑,0-20 层我会如下划分。

  • 移动端菜单栏 -> 中偏小,重要,9
  • 浮动按钮 -> 中偏小,但不重要,5
  • 侧边栏 -> 大,但重要。你很难说和浮动按钮哪个在上面。这两个要权衡一下,给到 4。
  • Hover tips -> 小,重要,19
    如果 Hover tip 也可能本身是一个交互丰富的大画布,这时要看重要性。如果重要性与 Modal 相当,设到 20(新一层海浪)。如果只是随便看看,共享当前层的主要内容,只是覆盖一下正文,设到 0 或 1。大画布谨慎设置中间值,容易造成混乱。
  • 目录头:暂时一下冒头,给到 0(注意是 z-index:0,不能是 z-index:auto)
  • 拖拽:暂时冒一下头,给到0。

在海浪模型里,带 9 的数字被表示为重要,而整十数表示为新的起点

20、40、60层

几乎为 Modal 层。通常页面上只会出现一个Modal,20 层足够使用,或者你要设置为40,60,都可以。如果你有很多层 Modal 要排序(窗口管理)。要么从 z-index 入手,要么从 DOM 结构入手。显然,别去动 DOM 结构树,靠 z-index 足够完成窗口层级管理。

最重要的海星

这个海星如果你认为是稀世珍宝,任何时候都需要被看见,请直接设置到 999。但一旦,你认为它有可能会被覆盖,请思考它被覆盖时,是不是还是这么重要。

如果不确定,参考基础逻辑:面积越小通常越重要。这不是一个我定义的规则,而是客观的视觉逻辑。面积越小,通常代表了信息量越密集。一个很常见的对比是:

  • Hover tips(小面积):在一个层非常非常重要。但需要被新画布覆盖
  • 消息通知(中小面积):重要吗?超重要的,但是消息通知上是不是还是应该可以 Hover 一些提示?
  • Hover reference(大面积):比如参考。重要吗?很重要。但是你觉得需要被消息通知、进度条覆盖吗?需要。

天空层(1000+)

总有些东西,你希望他们不要在海浪里浮动,而浮动在天上,不被任何东西覆盖。比如全局进度条、全局 Hover、全局消息。

放在天空的这点问题本身不大。最大的问题是,你觉得这个海星,应该放在海浪还是天空?不要让本该在海里的海星升天。一旦你觉得这个海星会被新逻辑的 Modal 覆盖,请让海星立刻返回 0-19 层。

为什么要思考海浪模型

  • 易于复用:对于可复用的浮动组件,不管其父元素的 CL 层级,总能被安插到正确的位置。如果发现异常,那就是忘了使父元素产生层叠上下文来包裹他
  • 避免混乱,减少设计的心智负担
更新于 2026-01-06
Waline