:focus-visible 必须与:focus搭配使用且:focus在前,否则键盘用户无焦点反馈、鼠标用户无视觉提示;Safari 15.3及更早版本不支持时需依赖:focus兜底。

只写 :focus-visible 样式,键盘用户能看见焦点,鼠标用户会彻底失去反馈——这不是 bug,是设计行为。 必须搭配 :focus 一起用,且顺序不能错。
为什么 :focus-visible 单独用完全失效
浏览器只在明确的键盘导航行为(Tab、Shift+Tab、Enter、Space、方向键)下触发 :focus-visible;鼠标点击、触摸 tap、甚至 JS 调用 element.focus() 都不会匹配它。
- 只写
button:focus-visible { outline: 2px solid #007aff; }→ 鼠标点按钮后页面毫无视觉反馈,用户不知道操作是否生效 - Safari 15.3 及更早版本直接忽略整条
:focus-visible规则 → 如果没前置:focus,这部分用户连基础焦点样式都没有 - 首次页面交互是鼠标点击时,Safari 和部分 Chromium 会短暂降级为全走
:focus,后续才启用:focus-visible判断逻辑
正确写法:分层声明 + 严格顺序
:focus 是兜底层,:focus-visible 是增强层。顺序错了,降级就崩了。
- ✅ 正确:
button:focus { outline: 1px dotted #999; outline-offset: 2px; }在前,button:focus-visible { outline: 2px solid #007aff; outline-offset: 2px; }在后 - ❌ 错误:
:focus-visible写在:focus前面;或两者都加!important导致层叠混乱 - ⚠️ 危险操作:全局写
*:focus { outline: none; }—— 这会把:focus-visible的样式也一并干掉 - 别用
@supports selector(:focus-visible)包裹规则:Safari 15.3 及更早版本解析失败,整个 CSS 块被丢弃,连降级样式都拿不到
哪些元素默认不触发 :focus-visible?怎么补救
不是加了 tabindex="0" 就自动支持。浏览器根据语义和交互意图判断是否启用该伪类。
立即学习“前端免费学习笔记(深入)”;
-
<div role="button">类组件:必须监听keydown事件,显式响应Enter和Space,否则 Chromium 可能判定为“非交互意图”,拒绝激活:focus-visible - 自定义
contenteditable容器:它自身不触发:focus-visible,需手动加tabindex="0",并在focusin中通过 JS 切换.focus-visible类模拟 -
<select>用了appearance: none:部分浏览器会丢弃原生焦点样式,得手动给select:focus-visible补outline - 禁用态(
aria-disabled="true"或disabled):即使有tabindex="0",也不会匹配:focus-visible—— 这是正确行为,别强行绕过
容易被忽略的细节:对比度、偏移量、真实设备验证
写了样式不等于可访问。真正难的是让每个焦点状态都扛住 WCAG 和多端差异。
-
outline颜色与背景对比度必须 ≥ 4.5:1,可用 DevTools 的无障碍检查器验证 -
outline-offset要 ≥ 2px,否则圆角按钮边缘会粘连;别用border替代outline,它会干扰布局且不随缩放自适应 - React/Vue 封装组件里,常因 focus 透传不完整导致原生
:focus-visible失效 → 必须确认底层 DOM 元素(如<input>)是否真正接收了焦点 - Safari 对触控板点按或首次点击仍可能短暂触发
:focus-visible,不能当成“绝对不响应鼠标”的保险栓 → 建议在真实设备上测试 Tab 导航流程,别只靠桌面模拟


















