模态框用原生 dialog:白拿的东西比想象中多

自己搭模态要手写焦点陷阱、ESC、滚动锁与返回焦点。原生 dialog 配 showModal 全部内置,还能用 ::backdrop,代价只有一条样式继承规则。

要一个模态框时,我的默认做法已经换了:用原生 <dialog> 加 showModal(),而不是一个 <div class="overlay">。

showModal 免费给你的四件事

showModal() 与 open 属性不是一回事,前者把元素放进顶层(top layer),于是四件平时要手写的事变成内置行为:

  • 焦点陷阱:Tab 不会跑到背后的页面上,背景内容对辅助技术是 inert 的
  • ESC 关闭:不用自己监听 keydown,关闭事件是 close
  • 滚动锁:背后的文档不再滚动,也不用手动给 body 加 overflow: hidden
  • 焦点归还:关闭后焦点回到打开它的那个元素(浏览器记着)

顶层渲染还顺手解决了 z-index:模态永远在页面之上,不会被某个 overflow: hidden 的祖先裁掉,也不用给页头加一个更大的 z-index。

::backdrop 是唯一的样式坑

遮罩不是 dialog 的背景,而是同名的伪元素 ::backdrop:

dialog::backdrop {
  background: color-mix(in srgb, var(--c-ink) 60%, transparent);
}

它不继承 dialog 上的自定义属性 —— 因为 ::backdrop 是顶层里的独立盒子。想让它读到站点令牌,就在 :root 上定义,而不是在 dialog 上。这一条是它最容易让人困惑的地方。

什么时候还是自己搭

本站的工具页里有一层纯 CSS 的 ui-overlay(结果面板、加载提示),那是展示层,不是模态:没有焦点要陷阱、没有交互、随时可以滚走。真正的模态(确认删除、清空数据)走 dialog。

能上原生元素就不要自己搭;自定义的理由应该只剩视觉。

← 返回文章列表

评论

…