在 2021 年实现仅键盘 :focus 样式的最佳方式是什么?

What is the optimal way of achieving keyboard only :focus styling in 2021?

长期以来,通过 CSS outline: none; 从可交互元素(例如按钮)中删除轮廓一直是常见的做法,因为大多数时候它看起来不合品牌,但它阻碍了可访问性并使浏览网站变得更加困难适用于依赖键盘导航的残障人士。

问题是我还没有找到一个简单的方法来区分mouse/keyboard和点击以及键盘tabbing元素之间的焦点事件的来源焦点状态。

我知道这个问题一直是个问题 ,但大多数来源都已有几年历史了,所以我将展示我找到的关于如何实现仅键盘焦点的主要 4 种方法,每一种各有利弊:

  1. “作弊”方法,一些网站如Target.com悬停和聚焦的样式相同,这种设计选择给了他们不需要的选择完全删除焦点轮廓。

优点:解决了问题。

缺点:设计选项有限。

button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
}

button:focus,
button:hover { 
  outline: 5px solid green; 
}
<button>Click or Tab me!</button>

  1. JS解决方案,Apple.com等网站使用ally.js to add data-focus-method attributes to their interactable elements. what-input is also quite well known and does a great job, together with various other polyfills.

优点:在所有浏览器上都能正常运行,无论设计如何都可以实现。

缺点:需要额外的 http 请求来获取 .js 文件,与其他解决方案相比,JS 对性能的要求更高。

ally.style.focusSource().current()
button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
  outline: none;
}

html[data-focus-source="key"] button:focus {
  outline: 5px solid green;
}
<button>Click or Tab me!</button>
<script src="https://cdn.jsdelivr.net/ally.js/1.4.1/ally.min.js"></script>

  1. :focus-within 优雅解决方案的圣杯。 :focus-visible 基本上是 :focus 的纯键盘版本,并已慢慢在 most modern browsers(以前的 :focus-ring)上可用。

优点: CSS 解决方案,性能好,易于实施。

缺点: 浏览器支持更好,但对于尚未支持的 Safari 仍然不理想。

button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
  outline: none;
}

button:focus-visible {
  outline: 5px solid green;
}
<button>Click or Tab me!</button>

  1. Roman Komarov tabindex="-1" hack 表明我们可以通过在 button 元素中添加 span 来区分焦点样式(技术上有效 HTML)然后给出跨度 tabindex="-1" 和不同的焦点样式。

优点:不涉及 JS,似乎适用于所有浏览器。

缺点: 需要在网站上的所有按钮和链接内使用垃圾邮件跨度,需要将填充从 buttons/links 移动到内部跨度,需要调整各种跟踪标签比如 gtm.

button {
  width: 180px;
  height: 60px;
  background: #999;
  border: none;
  outline: none;
}

button > span {
  display: flex;
  justify-content: center;
  height: 100%;
  align-items: center;
}

button:focus {
  outline: 0;
}

button:focus > span {
  outline: 5px solid green;
}

[tabindex="-1"]:focus {
  outline: none !important;
}
<button>
  <span tabindex="-1">Click or Tab me!</span>
</button>

现在的问题是,是否有更好或更简单的方法来实现此功能?是否有我缺少的行业标准?

现在的问题是,是否有更好或更简单的方法来实现此功能?是否有我缺少的行业标准?

简短回答:不,如果您的目标是“完美”(它在所有浏览器中的工作方式完全相同),那么您基本上已经在此处列出了您的选项。

然而,正如您所说,所有 4 个选项都有缺点。

就我个人而言,我会选择“最适合”的解决方案,有些用户可能最终会在点击时使用焦点指示器,但大多数新浏览器都会妥善处理:

button:focus { 
outline: 3px solid #333;
outline-offset: 3px;
 }
button:focus:not(:focus-visible) {
    outline: none;
    outline-offset: 0;
}
<button>A test</button>
<button>Another test</button>

此解决方案的缺点?

Safari 仍会在点击时显示焦点指示器......没关系!

Safari 最终会赶上并使用 :focus-visible。到那时,上面的 fiddle 是确保支持 :focus-visible 的浏览器按预期运行并且不支持它的浏览器退回到在点击时提供焦点指示器的最简单方法。

但必须完美

然后我会使用基于支持有条件加载的 polyfill。

此时您可以有条件地加载 a :focus-visible polyfill(压缩后为 3.5kb)。

我个人不喜欢这个解决方案,因为您必须更改所有 CSS...但我不像大多数人那样被点击时显示的焦点指示器所困扰,所以我可能会有偏见!

我想不起来了,但是有一种使用 JS 的方法,基本上是在鼠标单击时删除轮廓。我一年前做过,只是对细节一头雾水