:focus-visible不能单独使用,因为它仅在键盘导航(Tab/Enter/Space等)时触发,鼠标点击不生效;且Safari 15.3及更早版本会忽略该规则,必须前置:focus提供降级兜底,如button:focus { outline: 1px dotted #999; },再写button:focus-visible { outline: 2px solid #007aff; }。

为什么:focus-visible不能单独写
只写 button:focus-visible { outline: 2px solid #007aff; },鼠标点击后按钮完全没视觉反馈——这不是 bug,是设计行为。浏览器只在 Tab、Shift+Tab、方向键、Enter、Space 这类键盘导航时触发 :focus-visible;鼠标点、触摸 tap、甚至 JS 调用 element.focus() 都不会匹配它。更关键的是:Safari 15.3 及更早版本会直接忽略整条规则,若前面没 :focus 兜底,这部分用户就彻底看不到焦点样式。
必须和:focus共存,且:focus必须写在前面
正确顺序不是“谁更高级”,而是降级路径是否可靠:
-
button:focus { outline: 1px dotted #999; }—— 提供轻量锚点,确保所有聚焦行为都有基础反馈 -
button:focus-visible { outline: 2px solid #007aff; outline-offset: 2px; }—— 在支持的浏览器中精准接管键盘路径 - 绝对不要写
*:focus { outline: none; },它会把:focus-visible的样式也一并清掉 - 如果用了 CSS 重置库(如旧版 Bootstrap),检查它是否悄悄覆盖了
outline,需针对性恢复
哪些元素默认不触发:focus-visible?怎么补救
加了 tabindex="0" ≠ 自动支持。浏览器根据语义和交互意图判断,常见失效场景:
-
<div role="button">:必须监听keydown,显式响应 Enter 和 Space,否则 Chromium 可能判定为“非交互意图” -
<div contenteditable>:自身不触发,需手动加tabindex="0",并在focusin中用 JS 切换.focus-visible类模拟 -
<select>用了appearance: none:部分浏览器丢弃原生焦点样式,得手动补select:focus-visible { outline: ... } - 禁用态(
disabled或aria-disabled="true"):即使有tabindex="0",也不该匹配 —— 这是正确行为,别强行绕过
调试和兼容性最常被忽略的点
别靠反复切键盘验证,DevTools 里手动勾选元素的 :focus-visible 状态更快更准。另外几个硬伤:
立即学习“前端免费学习笔记(深入)”;
- 页面加载后 JS 调用
element.focus()默认不算键盘导航,:focus-visible不匹配;Chrome 111+ 支持element.focus({ focusVisible: true })强制触发 -
@supports selector(:focus-visible)在 Safari 15.3 及更早版本中解析失败,整个 CSS 块会被丢弃 —— 不要用它做条件加载 - 表单控件(
<input>、<textarea>)必须保留:focus样式,确保光标可见和输入态提示不丢失;不能一刀切全换成:focus-visible


















