只写 button:focus-visible 会出问题,因为浏览器不支持该伪类时整条规则被忽略,且无 :focus 兜底导致鼠标点击无任何焦点反馈;必须共存 :focus(基础锚点)与 :focus-visible(键盘强化),顺序为 :focus 在前、:focus-visible 在后,并确保 :focus 提供轻量视觉提示。

为什么只写 :focus-visible 会出问题
单独用 button:focus-visible { outline: 2px solid #007aff; },鼠标点按钮后完全没焦点反馈——这不是样式没生效,而是浏览器根本没匹配到伪类,同时你又没给 :focus 留兜底。键盘用户按 Tab 进入时样式是对的,但鼠标用户点击后无法确认是否点中,尤其在工具栏密集区域,极易误操作。
必须共存、且顺序不能错::focus 在前提供基础视觉锚点(哪怕只是淡色 outline 或 box-shadow),:focus-visible 在后专门强化键盘路径。否则旧浏览器(如 Safari 15.3)会直接忽略整条规则,新浏览器也会因层叠失效而“静默”。
button:focus 和 button:focus-visible 的书写顺序与取值逻辑
关键不是“哪个更高级”,而是降级路径是否可靠。浏览器不支持 :focus-visible 时,它整条 CSS 规则被跳过;此时必须靠 :focus 接住所有聚焦行为。
-
button:focus应设为轻量反馈,例如outline: 1px dotted #999;或box-shadow: 0 0 0 2px rgba(0,118,255,0.2);,避免干扰鼠标用户 -
button:focus-visible必须显式覆盖 outline,且带outline-offset: 2px——编辑器工具栏图标间距常小于 4px,不加 offset 容易和相邻元素粘连 - 绝对不要写
*:focus { outline: none; },这会把:focus-visible的样式也干掉;若需重置,只针对具体选择器,如button:focus { outline: none; }
哪些元素默认不触发 :focus-visible,怎么补救
不是加了 tabindex="0" 就万事大吉。浏览器对语义缺失的元素会拒绝激活该伪类,常见于:
立即学习“前端免费学习笔记(深入)”;
-
<div role="button">类组件:必须监听keydown事件,响应Enter和Space,否则部分 Chromium 版本判定为“非交互意图” -
<span tabindex="0">:无明确 role,Safari 可能直接跳过;应改用<button type="button">或至少加role="button" -
contenteditable容器:它自身不触发:focus-visible,需手动加tabindex="0"并在focusin中 JS 切换.focus-visible类来模拟 - 禁用态按钮(
aria-disabled="true"):即使有tabindex="0",也不该让它可聚焦——键盘用户会卡在不可用项上
Safari 和移动端的兼容性陷阱
Safari 15.4+ 原生支持,但 iOS 上仍有两个硬伤:
- 软键盘弹出后,
<textarea>获得焦点时常不触发:focus-visible,建议对这类元素额外加textarea:focus { box-shadow: 0 0 0 2px rgba(33,150,243,0.3); } - 老版本 Safari(15.3 及更早)可能把悬停(
hover)误判为键盘事件,导致鼠标用户看到轮廓;此时需用@supports not selector(:focus-visible)块降级,并在里面写input:focus, select:focus { outline: 2px solid #0066cc; } - polyfill(如 focus-visible.js)引入后,必须同步在
@supports not selector(:focus-visible)中声明.focus-visible类样式,否则老浏览器里 JS 加了类,CSS 却不认
最常被忽略的一点:outline-offset 不是装饰,是可读性底线。工具栏按钮横向排布时,缺了那 2px 呼吸感,焦点环就和图标边框融成一块,键盘用户根本分不清光标停在哪一个上。



















