只写:focus-visible会彻底丢失鼠标用户焦点反馈,因其仅响应tab/enter等键盘行为,不触发于鼠标点击或js聚焦;且safari 15.3及更早版本直接忽略该规则,必须前置:focus兜底并确保顺序正确。

只写 :focus-visible 会彻底丢失鼠标用户的焦点反馈,必须和 :focus 配合,且 :focus 必须写在前面。
为什么单独用 :focus-visible 会让鼠标用户“失明”
浏览器只在明确的键盘导航行为(Tab、Shift+Tab、方向键、Enter、Space)下才匹配 :focus-visible;鼠标点击、触摸 tap、甚至 JS 调用 el.focus() 都不会触发它。所以如果只写 button:focus-visible { outline: 2px solid #0066cc; },鼠标点按钮后页面毫无视觉变化——不是 bug,是设计如此。
更关键的是兼容性:Safari 15.3 及更早版本会直接忽略整条 :focus-visible 规则;如果没有前置的 :focus 声明,这部分用户连基础焦点提示都没有。
:focus 和 :focus-visible 的声明顺序不能错
顺序决定降级是否可靠。浏览器不支持 :focus-visible 时,整条规则被丢弃,但前面的 :focus 仍有效——前提是它写在前面。
- ✅ 正确:
button:focus { outline: 2px solid #999; outline-offset: 2px; }→button:focus-visible { outline-color: #0066cc; } - ❌ 错误:
:focus-visible写在:focus前面 - ❌ 危险:
* { outline: none; }会干掉所有:focus和:focus-visible的outline,除非显式重置
哪些元素默认不触发 :focus-visible?怎么补救
不是加了 tabindex="0" 就自动生效。浏览器根据语义和交互意图判断是否启用该伪类,尤其对自定义组件非常敏感。
- ✅ 默认支持:
<button></button>、<input>、<textarea></textarea>、<select></select>、带tabindex="0"的<div><li>❌ 默认不触发:<code><div role="button">(缺 <code>keydown监听)、纯<div>(无 <code>role或事件)、禁用态(disabled或aria-disabled="true") - 补救方法:
<div role="button" tabindex="0"> 必须监听 <code>keydown,并在event.key === 'Enter'或event.key === ' '时触发对应操作 - 验证技巧:在 DevTools 中手动勾选元素的
:focus-visible状态,比反复切键盘更快确认样式是否生效 -
<input>和<textarea></textarea>这类表单控件不能全盘套用:focus-visible——它们需要始终有视觉反馈(比如边框高亮 + 光标),否则鼠标用户点进去却看不到光标,无法确认输入位置 -
<select></select>用了appearance: none:部分浏览器会丢弃原生焦点样式,得手动给select:focus-visible补outline - 全局写了
* { outline: none; }后,:focus-visible的样式也会被清掉,必须显式恢复
Safari 和移动端上容易被忽略的实际表现
Safari 15.4+ 支持 :focus-visible,但对触控板点按或首次点击仍可能短暂触发;Chrome/Edge/Firefox 表现稳定,但以下情况依然失效:
真正难的不是写对一行 button:focus-visible,而是确保它在所有自定义组件、所有第三方 UI 框架、每种设备和辅助技术环境下都稳定可见——样式可能被覆盖十次,但只要有一次没 fallback,键盘用户就卡在页面中间找不到路。











