Back

创建纯 CSS 工具提示

创建纯 CSS 工具提示

如果你需要一个轻量级的工具提示(tooltip),又不想为此引入一个 JavaScript 库,那么仅用 CSS 就能出色地完成这项任务。无需依赖、无需事件监听器——只需几条精心编写的 CSS 规则即可。

本文将带你了解一种实用且现代的实现方式,使用伪元素、data 属性和过渡效果,并介绍你确实需要了解的可访问性注意事项。

关键要点

  • 纯 CSS 工具提示可以通过 ::after 伪元素、content: attr(data-tooltip) 以及 :hover / :focus-visible 触发器来构建。
  • 使用 opacityvisibility(而非 display),以便工具提示能够平滑淡入淡出。
  • pointer-events: none 可防止工具提示自身接收悬停事件,从而避免闪烁。
  • CSS 工具提示存在真实的可访问性限制——它们无法可靠地被屏幕阅读器识别,并且在触屏设备上的悬停行为也不一致。
  • 对于关键 UI 提示,请使用 JavaScript、Popover API,或通过 aria-describedby 正确引用可见元素。

纯 CSS 工具提示的工作原理

核心思路很简单:在触发元素上应用 position: relative,然后将 ::after 伪元素作为工具提示气泡,相对其父元素进行绝对定位。默认情况下隐藏它,并在 :hover:focus-visible 时显示。

由于伪元素无法直接读取 DOM,因此你需要通过 data-tooltip 属性传递工具提示文本,并使用 content: attr(data-tooltip) 将其引入。这样可以保持 HTML 整洁,并避免在隐藏的 <span> 中重复书写文本。


HTML 结构

<button
  class="tooltip-trigger"
  data-tooltip="Saves your current progress"
>
  Save
</button>

使用 <button> 或锚点(<a>)作为触发元素非常重要。这些元素天生可获得焦点,这意味着键盘用户无需任何额外操作即可访问到它们。

注意:如果你想使用 aria-describedby,它必须指向 DOM 中真实存在且可见元素的 id——而非伪元素。由于本技术使用 ::after,除非你也将工具提示文本渲染在真实元素中,否则请省略 aria-describedby


CSS 实现

.tooltip-trigger {
  position: relative;
  cursor: pointer;
}

/* Tooltip bubble */
.tooltip-trigger::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);

  background-color: #1a1a1a;
  color: #fff;
  font-size: 0.8rem;
  white-space: nowrap;
  padding: 5px 10px;
  border-radius: 4px;

  /* Hidden by default */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;

  z-index: 10;
  pointer-events: none;
}

/* Show on hover and keyboard focus */
.tooltip-trigger:hover::after,
.tooltip-trigger:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

为什么使用 opacity + visibility 而不是 display

display: nonedisplay: block 之间的切换无法应用过渡效果——工具提示只会突然出现和消失。同时使用 opacityvisibility: hidden 可以让工具提示平滑淡入淡出,并在隐藏时阻止其响应指针交互。配合 visibilityopacity 还可以防止隐藏的工具提示在视觉上仍可交互。

为什么使用 pointer-events: none

如果没有这一属性,工具提示气泡本身可能会触发悬停状态,导致光标移动到其上时工具提示出现闪烁。在 ::after 元素上设置 pointer-events: none 即可完全避免该问题。

为什么使用 transform: translateX(-50%) 进行居中?

设置 left: 50% 会将工具提示的左边缘移到触发元素的中心。然后 translateX(-50%) 将其向回偏移自身宽度的一半,无论工具提示文本多宽都能实现居中——无需任何硬编码的像素计算。


需要了解的可访问性限制

纯 CSS 悬停工具提示存在以下实际限制:

  • 屏幕阅读器可能不会朗读它。 伪元素上的 content 属性并非所有屏幕阅读器都能可靠读取。对于关键信息,应将其包含在可见 UI 中,或使用 aria-describedby 指向真实元素。
  • 触屏设备上的悬停行为不一致。 不应依赖纯 CSS 工具提示来传达必要信息。
  • 无角色或活动区域(live region)。 仅靠 CSS 无法向辅助技术传达工具提示的语义。

对于交互元素上简单的补充提示,CSS 工具提示足够使用。但对于完成任务所必需的内容,则需要使用 JavaScript 来管理焦点、播报和 ARIA 状态。


展望:CSS 锚点定位与 Popover API

CSS 锚点定位(Anchor Positioning) 让工具提示的位置控制更加灵活——你可以将工具提示锚定到任何元素,而无需依赖父元素的 position: relative。根据 Can I Use 的数据,锚点定位现已在主流现代浏览器中得到广泛支持。

如需更丰富的交互行为,Popover API 是一个值得探索的原生替代方案,适用于交互式浮动 UI。


结论

使用 ::aftercontent: attr(data-tooltip) 以及 visibility/opacity 过渡构建的纯 CSS 工具提示既简洁又快速,且无任何依赖。再配合 :focus-visible 实现键盘支持,便能形成稳固的基础方案。但请务必认清 CSS 工具提示的局限性——除补充提示外,任何更复杂的场景都应使用 JavaScript 或 Popover API 等原生平台特性。

常见问题

可以。位置由伪元素上的偏移属性控制。使用 bottom 配合 calc(100% + 8px) 可放置在上方,使用 top 配合 calc(100% + 8px) 可放置在下方,或使用 right 和 left 进行水平方向放置。你也可以创建诸如 tooltip-bottom 或 tooltip-right 这样的变体类来覆盖这些属性。

触屏设备上的悬停行为不一致,因此不应依赖纯 CSS 工具提示传达必要信息。如果工具提示内容在移动端很重要,请将其渲染在由 JavaScript 控制的真实元素中,或使用 Popover API,它为跨输入类型的交互式浮动 UI 提供了更可靠的基础。

white-space: nowrap 规则让工具提示保持单行显示,但在窄屏上可能溢出。可以将其替换为 max-width 并允许换行,例如 max-width: 240px 和 white-space: normal。对于动态边缘检测,则需要 JavaScript 或 CSS 锚点定位,后者在现代浏览器中支持更灵活的原生定位行为。

对于按钮和链接等可获得焦点元素上的非必需补充提示,它是可接受的。但当工具提示承载完成任务所需的信息时,它就不够用了,因为伪元素内容无法被屏幕阅读器可靠地播报,且触屏设备上的悬停行为不一致。对于关键内容,应使用真实 DOM 元素配合 aria-describedby 和 JavaScript 管理的行为,或考虑使用 Popover API。

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay