:focus响应所有聚焦方式,:focus-visible才专用于键盘聚焦;必须先写:focus提供基础反馈,再用:focus-visible强化键盘焦点样式。

直接说结论:不是 :focus 不消失,而是它本就不该“只在键盘时出现”——它响应所有聚焦方式,包括鼠标点击后自动获得的焦点。想让点击无轮廓、仅键盘有反馈,必须用 :focus-visible,且不能丢掉 :focus。
为什么鼠标点完按钮还留着蓝边?
这是 :focus 的正常行为:只要元素获得焦点(不管怎么来的),样式就生效。浏览器默认给 button、input 等加 outline,你写了 button:focus { outline: 2px solid blue; },鼠标点一下,它立刻聚焦、样式就上;松开手,焦点没移走,蓝边当然不消失。
- 这不是 bug,是设计使然——键盘用户靠这个定位当前操作位置
- Safari 中有时蓝边“卡住不退”,常因 focus 状态未被正确释放(比如事件冒泡干扰或 JS 阻止了 blur)
- 单纯加
outline: none会彻底砍掉可访问性,键盘用户将失去焦点路径
如何让鼠标点击不显示焦点轮廓?
靠 :focus-visible,但它不能单独存在。必须和 :focus 配合,顺序不能错:
-
:focus先写,提供兜底视觉反馈(哪怕只是微弱 outline 或 shadow) -
:focus-visible后写,专门强化键盘用户的焦点环(颜色更醒目、带 offset) - 若把
:focus-visible写在前面,会被后面的:focus覆盖,等于白写
正确写法示例:
立即学习“前端免费学习笔记(深入)”;
button:focus {
outline: 2px solid #999;
}
button:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
哪些情况 :focus-visible 根本不触发?
它依赖浏览器 UA 启发式判断,不是稳定开关。常见失效场景:
- 页面首次交互是鼠标(如加载后点按钮),后续一段时间内浏览器可能“降级”为全用
:focus - 用了
autofocus—— 自动聚焦不算“键盘导航”,:focus-visible不匹配 - JS 调用
el.focus()但没传{ focusVisible: true }(仅 Chromium 支持,且需配合HTMLElement.focusOptions) - 旧版 Safari(15.3 及以下)、Firefox(99 以下)原生不支持,需 polyfill 或降级方案
移动端和框架里特别容易踩的坑
真机测试比 DevTools 模拟更重要:
- iOS Safari 对
input[type="number"]的焦点行为延迟明显,建议统一用type="text"+ 输入限制 - React/Vue 封装的按钮组件,若内部调用
el.focus()却没透传 focus 选项,:focus-visible很可能静默失效 - Shadow DOM 中,
:focus-visible作用域受限,需在组件内部显式定义,不能靠外层样式穿透 - 用了
contenteditable的区域,Safari 直到 iOS 15.4 才稳定支持:focus-visible
最常被忽略的一点:即使你写了完美的 :focus-visible,如果父容器或祖先元素用了 pointer-events: none 或 visibility: hidden,焦点根本进不去,伪类自然不会激活。


















