移动端触摸反馈与可访问性是一体两面:未满足可访问性条件(如无事件监听、缺cursor:pointer、pointer-events:none、漏touch-action)会导致iOS :active失效;触控目标须≥44×44px且用padding扩展;禁用outline:none,改用:focus-visible+明确视觉反馈;必须使用原生语义化标签而非div+role="button"。

移动端触摸反馈和可访问性不是两个问题,而是一体两面:没做对可访问性,触摸反馈就注定失效;只加视觉动效,反而会掩盖真实交互状态。
为什么 :active 在 iOS 上经常不触发
根本不是 CSS 写错了,而是 Safari 把 :active 当作“可交互元素”的信号灯——它只对满足条件的元素亮起。常见断点包括:
- 元素没绑定任何事件监听器(哪怕空
touchstart也不行) - CSS 没写
cursor: pointer(对触摸无效,但它是 Safari 启用:active的开关) - 父级设置了
pointer-events: none,子元素的:active全部静默 - 漏加
touch-action: manipulation,导致 iOS 延迟 300ms 才响应,:active瞬间闪一下就没了
实操建议:给所有按钮类元素统一加这段 CSS:
button, [role="button"], .btn { cursor: pointer; touch-action: manipulation; }再补一个空 touchstart 监听器(仅需一次):document.querySelectorAll('button, [role="button"]').forEach(el => { el.addEventListener('touchstart', () => {}); });
触摸目标尺寸必须硬达标 44×44px
WCAG 2.2 明确要求最小触控区域为 44px × 44px,这不是建议值,是可访问性合规底线。靠 font-size 或 line-height “撑大”点击区是典型误区——实际可点区域仍只有文字本身。
立即学习“前端免费学习笔记(深入)”;
- 用
min-width: 44px; min-height: 44px;强制约束容器尺寸 - 优先用
padding扩展热区,而非width/height—— 它不影响布局流,且兼容 label 包裹 input 的语义热区放大 -
label必须直接包裹input(不能有换行或空格),否则浏览器不嫁接点击事件 - 禁用
user-select: none在按钮或链接上——它会干扰 TalkBack / VoiceOver 的文本选择和操作识别
焦点样式不能只靠颜色区分
:focus 不是给鼠标用户看的,是键盘 Tab 导航和屏幕阅读器的唯一路径。删掉默认 outline 可以,但必须用显式视觉反馈替代:
- 用
:focus-visible替代:focus,避免鼠标点击时出现多余轮廓线 - 必须保留清晰的视觉反馈,例如
outline: 2px solid #0066cc;或box-shadow: 0 0 0 2px #0066cc; - 禁用
outline: none全局设置——这是无障碍红线 - 如果用了
tabindex="-1"做程序化聚焦(如模态框打开后聚焦按钮),记得提前在 HTML 中写好该属性,否则.focus()静默失败
别用 div + role="button" 伪装可交互元素
自定义元素在 VoiceOver/TalkBack 下大概率被跳过,且不响应 :active 或键盘触发。实测中常见现象是:真机双指滑动切换焦点时,“按钮”直接被跳过;或点击后无任何反馈,但手动触摸却能触发 JS 逻辑——这说明 DOM 存在,但语义缺失、事件链断裂。
- 必须用
<button>、<a href>、<input type="checkbox">这类原生标签承载点击行为 - 若需样式隔离,用
button { all: unset; }重置后再写样式,别用div+ JS 模拟 -
role="button"不自动获得 keyboard focus,也不触发:active,在部分 WebView(如微信 X5)中完全无效 - DOM 深度不宜超 6 层,否则触控延迟显著增加;Chrome DevTools 的 “Show DOM properties” 可查 depth 值
真正卡住可访问性的,从来不是某个 ARIA 属性漏写了,而是整个结构默认就假设用户“只用手指点”,忽略了语音控制、开关设备、眼动仪这些真实存在且合法合规的使用方式。



















