模态框样式设计要点

Avatar of Chris Coyier
Chris Coyier 发表于

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

模态框。一个小弹窗,用于告知你一些重要信息。这有多难?嗯…中等难度吧。这里有很多需要考虑的地方,也有一些棘手的问题需要解决。让我们来数一数。

我通常会在结束标签 </body> 之前放置模态框的 HTML 代码。

  <div class="modal" id="modal"></div>

</body>

</html>

这主要出于样式考虑。当你处理覆盖整个页面的 body 元素时,定位模态框会更容易,而不是处理一组未知的父元素,每个父元素都可能具有其自己的定位上下文。

这对屏幕阅读器来说怎么样?我不是无障碍专家,但我听说模态框非常棘手。 Rob Dodson:

任何尝试使模态框无障碍的人都知道,尽管它们看起来很普通,但**模态框实际上是 Web 无障碍性方面的最终 Boss 战**。它们会把你嚼碎然后吐出来。例如,一个合适的模态框需要具备以下功能

  • 键盘焦点应移至模态框内部,并在模态框关闭时恢复到之前的 activeElement
  • 键盘焦点应被限制在模态框内,以防止用户意外地跳出模态框(也称为“逃离模态框”)
  • 屏幕阅读器也应被限制在模态框内,以防止意外逃离

Rob 建议查看 The Incredible Accessible Modal Window 演示。我还看到过 Noah Blon 最近的一个示例Nicolas Hoffman 的一个示例。此外,ARIA 实时区域 在这里也很重要。

如果你自己处理焦点,将模态框放在文档底部似乎是可以接受的。

居中

我们有一份关于 CSS 居中设计的完整指南。

其中有一个我最喜欢的技巧。那个无需明确知道宽度或高度就能同时垂直和水平居中的技巧

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

这非常适合模态框,因为模态框通常是完全居中的,并且可能具有不同宽度的不同版本。高度更有可能发生变化,因为高度总是与内部内容相关。

如果你完全确定模态框的高度和宽度,可以考虑其他居中方法。我之所以提到这一点,是因为使用 transform 可能会导致文本略微模糊,所以如果你遇到此问题,请查看居中指南中的其他方法(例如负边距)。

固定?

请注意我们使用了 position: fixed;。这样做是为了,即使用户向下滚动页面,触发模态框,模态框也能像他们没有滚动时一样居中并可见。

我想,总的来说,即使在“移动设备”上,我也认为固定定位现在相当安全。但如果你知道你正在处理相当一部分非常旧的手机,固定定位可能会成为问题,你可能需要考虑使用 position: absolute; 并强制滚动到页面顶部。或者其他什么方法,我不知道;进行测试。

处理宽度

在大屏幕上,典型的模态框的外观不仅居中,而且宽度有限(如上图所示)。

.modal {

  /* other stuff we already covered */

  width: 600px;
  
}

这是危险信号区域。我们在大型屏幕上获得了我们想要的效果,但我们知道还有很多屏幕的宽度甚至不到 600px。

使用 max-width 轻松修复

.modal {

  /* other stuff we already covered */

  width: 600px;
  max-width: 100%;
  
}

处理高度

设置高度更是危险信号。我们知道内容会发生变化!此外,transform 居中技术很乐意在没有滚动条的情况下截断模态框的顶部以节省空间

设置最大高度将再次拯救我们

.modal {

  /* other stuff we already covered */

  height: 400px;
  max-height: 100%;
  
}

处理溢出

既然我们开始设置高度,就需要考虑溢出。在 .modal 本身上使用 overflow 值很诱人,但这样做有两个问题

  • 我们可能希望某些元素滚动
  • 溢出会截断 box-shadow,而我们可能需要它

我建议使用内部容器

<div class="modal" id="modal">

  <!-- things that don't scroll -->

  <div class="modal-guts">

    <!-- things that scroll -->

  </div>

</div>

为了使内部内容滚动,它需要一个高度。这里有多种可能性。一种是将其定位为覆盖整个模态框,然后添加溢出

.modal-guts {

  /* other stuff we already covered */

  /* cover the modal */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

  /* spacing as needed */
  padding: 20px 50px 20px 20px;

  /* let it scroll */
  overflow: auto;
  
}

按钮

模态框的目的是在执行其他任何操作之前强制执行某个操作。如果你没有强制执行操作,请考虑使用其他UI 而不是模态框。需要有某种方法可以退出模态框。选项按钮很常见(例如“删除”/“取消”)。关闭按钮也很常见。让我们为我们的模态框添加一个关闭按钮。

始终显示关闭按钮似乎很明智,这样用户就不会处于无法直观地了解如何关闭模态框的状态。这就是我们创建不可滚动区域的原因。

良好的样式取决于你;)

处理覆盖层

模态框通常会伴随着一个覆盖整个屏幕的覆盖层。这出于多种原因很有用

  • 它可以使屏幕的其余部分变暗(或以其他方式静音),从而强化模态框的“你需要处理它才能离开”的目的。
  • 它可以用来阻止模态框外部的点击/交互。
  • 它可以作为巨大的关闭按钮。或者“取消”或任何最不显眼的操作。

典型处理方式

<div class="modal" id="modal">
  <!-- modal stuff -->
</div>
<div class="modal-overlay" id="modal-overlay">
</div>
.modal {

  /* stuff we already covered */

  z-index: 1010;

}
.modal-overlay {

  /* recommendation:
     don't focus on the number "1000" here, but rather,
     you should have a documented system for z-index and 
     follow that system. This number should be pretty
     high on the scale in that system.
  */
  z-index: 1000;

  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

}

使用类关闭(而不是使用类打开)

我发现默认情况下将 .modal 类隐藏起来非常诱人,可能使用 display: none;。然后要打开它,添加一个 open 类,例如 .modal.open { display: block; }

但请注意那里的 display: block; 吗?我认为这是一个问题。display: none; 非常有用,因为它可以从视觉上和辅助技术上隐藏模态框。**在现有 display 值之上应用它比通过猜测值进行覆盖更容易。**这意味着你的 .modal 可以使用 display: flex;display: grid; 或任何其他有用的值。模态框的不同变体可以使用任何它们想要的值,而无需担心它们会被重置为 display: block;

.modal {

  /* for example... */
  display: flex;  

}
.modal.closed {
 
  display: none;

}

切换开放性

这是我们目前为止打开和关闭的最基本的方法。

var modal = document.querySelector("#modal");
var modalOverlay = document.querySelector("#modal-overlay");
var closeButton = document.querySelector("#close-button");
var openButton = document.querySelector("#open-button");

closeButton.addEventListener("click", function() {
  modal.classList.toggle("closed");
  modalOverlay.classList.toggle("closed");
});

openButton.addEventListener("click", function() {
  modal.classList.toggle("closed");
  modalOverlay.classList.toggle("closed");
});

这还没有处理可访问性问题。请记住,这是我们上面讨论过的一个考虑因素。这是一个演示,它处理移动焦点、捕获焦点以及将焦点返回到它最初所在的位置。

样式考虑演示

查看 Chris Coyier 在 CodePen 上的 Pen 模态样式的注意事项 (@chriscoyier)。

我错过了什么?随时可以复制这个内容并告诉我。