仅写 :focus 样式远远不够,真正决定无障碍效果的是你是否提供了稳定、高对比、不被覆盖的焦点视觉反馈,且必须与 :focus-visible 协同设计,不能替代。

直接说结论:仅写 :focus 样式远远不够,真正决定无障碍效果的是你是否提供了稳定、高对比、不被覆盖的焦点视觉反馈,且必须与 :focus-visible 协同设计,不能替代。
为什么写了 :focus 样式却“看不见”焦点?
不是伪类失效,而是视觉反馈被悄悄抹掉了:
- 重置 CSS(如 Normalize.css 或 Tailwind base)默认设了
outline: none,你的input:focus { outline: 2px solid #007bff; }被覆盖 - 全局写了
*:focus { outline: none; }却没补任何替代样式——键盘用户立刻“失明” - 用
border模拟焦点但没预留空间,导致聚焦时布局抖动,或被父容器overflow: hidden剪掉 - iOS Safari 给
<input>默认加蓝光,一旦设了outline,它就消失,而你新样式又没达到 WCAG 3:1 对比度要求
:focus 和 :focus-visible 必须共存,且顺序不能错
它们解决不同问题,强行替换会破坏两类用户:
-
:focus总是生效(鼠标点、Tab 切、.focus()调用),是所有浏览器的兜底保障 → 必须提供基础样式(哪怕只是outline) -
:focus-visible是浏览器判断“像在键盘导航”时才触发 → 适合做“仅键盘可见”的增强样式(比如虚线框 + 动画) - 声明顺序必须是:
input:focus { outline: none; }在前,input:focus-visible { outline: 2px solid #0066cc; outline-offset: 2px; }在后;否则旧版 Safari/IE 里整个焦点样式会丢失 - 别用
@supports selector(:focus-visible)包裹——Safari 15.3 及更早版本解析失败,整块 CSS 被丢弃,连降级都拿不到
表单控件要分层处理,不能一刀切
不同语义的元素对焦点反馈的需求不同:
-
<input>、<textarea></textarea>、<select></select>:必须保留:focus,确保光标可见、边框高亮等输入态提示不丢失 -
<button></button>、<a href></a>、<summary></summary>:可用:focus-visible控制焦点指示器,避免鼠标点击后突兀闪现轮廓 - 自定义组件(如
div[role="button"]):需手动加tabindex="0",且只响应:focus-visible—— 否则键盘用户无法聚焦,除非你额外监听keydown补全空格/回车逻辑
移动端和 Shadow DOM 的隐藏陷阱
这些环境会让 :focus 表现异常,但容易被忽略:
- iOS Safari 中,
<input>的默认蓝光一设outline就消失,必须手动补足尺寸(至少 2px)和对比度(推荐#2d5d9f) - 元素在 Shadow Root 内时,外部样式表里的
:focus规则默认不生效;得把样式写进<style></style>标签内,或用::slotted(input:focus)(仅限 slot 内容) - 如果表单区域用了
pointer-events: none或visibility: hidden,会阻断聚焦链,:focus永远不会激活
真正难的不是写对一行 input:focus,而是确保它在组件库、第三方 UI 框架、iOS/Android/Safari/Chrome、屏幕阅读器强制高对比模式下,都不被覆盖、不被截断、不被静默忽略。一次遗漏,键盘用户就在页面中间彻底迷路。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











