:focus-visible 能精准区分键盘与鼠标聚焦,但需正确声明顺序、避免全局 outline: none、确保元素可聚焦且焦点来源为键盘;否则不触发。

直接结论:用 :focus-visible 替代 :focus 做焦点样式,就能让键盘用户看到清晰轮廓,鼠标用户完全不受干扰——但必须配合正确的声明顺序和 outline 恢复逻辑,否则它根本不会显示。
为什么 :focus-visible 写了却没反应
最常见原因不是语法错,而是浏览器压根没“认为这次聚焦该被看见”:
- 鼠标点击后按空格/回车,
:focus-visible不触发(焦点来源仍是鼠标) -
tabindex="-1"的元素首次被 Tab 进入时,部分浏览器(尤其 Safari)不激活它 - 页面加载后 JS 调用
element.focus(),默认不算键盘导航,:focus-visible不匹配 - 全局写了
* { outline: none },把:focus-visible的基础样式也干掉了
调试时直接在控制台跑 document.activeElement,确认焦点是否真落在目标上,再检查它的 tabIndex 是否 ≥ 0 且未被 disabled。
:focus 和 :focus-visible 的声明顺序不能乱
必须先重置所有 :focus 的 outline,再用 :focus-visible 单独恢复——靠层叠顺序自然降级,而不是靠 !important 强顶:
button:focus { outline: none; }
button:focus-visible { outline: 2px solid #007aff; outline-offset: 2px; }
这样写的效果是:
- 旧浏览器(如 Safari 15.3 及更早)只认第一行,所有焦点框消失
- 新浏览器执行两行:鼠标点按钮 →
:focus生效但:focus-visible不匹配 → 无 outline;键盘 Tab 进入 →:focus-visible匹配 → 显示轮廓 - 如果写成
button:focus-visible, button:focus并列,旧浏览器会直接忽略整条规则(CSS 解析失败)
哪些元素默认不触发 :focus-visible
不是加了 tabindex="0" 就一定能用,浏览器有内置判断逻辑:
- ✅ 默认触发:
<input>、<textarea></textarea>、<button></button>、<select></select>、带tabindex="0"的<div> <li>❌ 默认不触发:<code><a></a>(无href)、<span></span>(即使有tabindex="0")、<label></label>(除非显式设tabindex) - ⚠️ 自定义组件(比如用
<div role="button"> 实现的开关)需确保有 <code>role+ 键盘事件监听,否则部分浏览器拒绝激活:focus-visible如果不确定,DevTools 里手动勾选元素的
:focus-visible状态,比反复切键盘更快验证样式是否生效。兼容性和可访问性细节不能漏
:focus-visible在 Chrome 86+、Firefox 89+、Safari 15.4+ 原生支持;IE 完全不识别,旧版 Safari 行为不稳定。更关键的是:- 别依赖
@supports (focus-visible: auto)做兜底——它在 Safari 15.4 之前返回 false,但实际部分行为已可用 -
outline颜色必须满足 WCAG 对比度要求(≥ 3:1),深色背景建议用#007aff,浅色背景可用#5b3bff - 第三方 UI 库(如旧版 Bootstrap 或某些 CSS-in-JS 方案)可能悄悄覆盖了
:focus-visible的样式,务必在 DevTools 的 Computed 面板里查最终生效的outline
真正容易被忽略的点是:autofocus 元素或 JS
.focus()默认不触发:focus-visible,如果业务强依赖这个场景(比如表单自动聚焦后需引导键盘操作),得用element.focus({ focusVisible: true })(Chrome 111+ 支持)或手动加类兜底。 - 别依赖











