加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.0757zz.com/)- 云硬盘、大数据、数据工坊、云存储网关、云连接!
当前位置: 首页 > 运营中心 > 建站资源 > 建站经验 > 正文

量子工程师视角:无障碍建站避坑指南

发布时间:2026-11-06 11:39:45 所属栏目:建站经验 来源:DaWei
导读:  掘金这地方,连吐槽都得带点“可复现性”——行吧,去年春晚那个无障碍建站的坑,我原以为就是UI走查时的沉默,结果最后是测试机排成一排,全卡在aria-hidden和focus-trap的耦合态上。冷眼看着它崩,不是不想救,是救了三次,每

  掘金这地方,连吐槽都得带点“可复现性”——行吧,去年春晚那个无障碍建站的坑,我原以为就是UI走查时的沉默,结果最后是测试机排成一排,全卡在aria-hidden和focus-trap的耦合态上。冷眼看着它崩,不是不想救,是救了三次,每次都是同一行代码:useFocusLock({ crossOrigin: false }),但 crossOrigin 这个字段压根不该出现在这个 hook 里,某个小版本加的,没人同步文档,也没人敢动——因为动了,春晚红包雨的语音播报就漏两帧。


  桌上那三台测试机,Surface Pro 7、MacBook Air M2、还有台借来的华为 MatePad Pro,屏保全是灰底白字的 WCAG 对照表截图。凉了的外卖在左下角塑料袋里,酱汁渗到盒盖边缘,没动过。我掏手机想复现屏幕阅读器跳过跳转按钮的问题,地铁报站声刚响第三遍,手指滑到第五个 tab 页就停了:它根本不读 aria-current="page",只认 role="navigation" 里嵌套的 文本节点。这个我敢打包票,因为当时把 voiceover 拉出来录了十七秒音频,导出后用 Audacity 放慢两倍,听清了它跳过整个 nav-bar 的气音间隙——比量子退相干还干净。


  产品经理站在背后说:“就这么点东西。”说完把 iPad 转过来,手指点着那个“立即参与”按钮。我盯着他指甲缝里的蓝光笔墨痕,没接话。后来才知道,他前天刚被UX总监拉去听了一场“无障碍即合规”的内训,PPT 第七页写着“语义化标签=零成本提效”,底下小字糊得像退火后的超导膜——谁也懒得擦。


  其实根本不是标签的事。是 Chromium 某个底层 focus ring 渲染策略,在 macOS 14.3 + VoiceOver 开启状态下,会把 tabIndex="-1" 的元素自动降级为 inert,哪怕你写了 document.getElementById("foo").focus(),它也只执行到 event loop 第二帧就断了。这个我说不准,因为翻 Chromium bug tracker 翻到三百七十二页时,发现有个类似 issue 标着 WontFix,理由是“focus management is user agent responsibility”。我笑了,把那页截下来发群里,三分钟没人回,第四分钟,前端组长甩来一行 git blame:src/utils/accessibility/focus-helpers.ts —— 提交人是我自己,时间是去年九月十五号,凌晨一点十八秒。对,就是我写的。


文章配图,仅供参考

  优胜劣汰?嗯。不是人淘汰人,是环境自己长牙。你搭的组件库要是不兼容 NVDA 2024.1 和 JAWS 2023 Q3 的 keyboard trap 逻辑差半拍,上线三小时,视障用户反馈的语音日志直接进风控模型训练集;第二天,你写的 Button 组件就被标成“高危交互路径”,自动移出 design system 主干。不是评审团投的票,是 log 日志里那串 fail_count > 1200 的指标自己站起来踹了你一脚。


  失败案例堆起来能砌墙:
- 某次把所有 button 加了 aria-label,结果 screen reader 读完 label 又读 button 文本,变成“提交订单,提交订单”,用户以为点了两次;
- 某次用 IntersectionObserver 做懒加载图片的 alt 替换,结果 Observed 元素进 viewport 前,alt 已经被读完了——因为 screen reader 启动比 JS 快;
- 最绝的是那个 modal,加了 aria-modal="true",但忘了在 body 上加 inert,结果盲人用户用键盘按 Home 键,直接跳回页首 banner,点开 logo 还真进了首页……这个我敢打包票,因为用户录音反馈里有一句“我明明在抽奖页面,怎么又看到春晚倒计时了”。


  昨天重装了 Windows,顺便把 NVDA 更新到最新稳定版,打开本地站点,突然发现原来那个死循环的 focus trap 居然通了。查了一下,是 NVDA 主动兼容了 Chromium 的新 FocusEvent.isTrusted 行为。不是我们改对了,是对手先让步了。有点像做量子门校准——你调了一百次 Rz(θ),最后门保真度没涨,结果某天实验室液氦温度低了零点二度,CNOT 误差直接掉下去三阶。你不敢说是你调的,只能说:系统自洽了。


  (顺手删了浏览器缓存,重新起 server,Ctrl+Shift+M 切移动设备模式,点开 DevTools 的 Accessibility 面板——空白。等了八秒,面板还是灰色。关掉重开,第三次才出来。这时候手机震了一下,是 UI 同事发来的图:她正在用 TalkBack 测试安卓端,录屏显示屏幕阅读器停在 loading spinner 上,反复念“loading, loading, loading”,一共四十七次。我没回。盯着自己电脑右下角的系统时间:17:06:03,而测试机上的系统时间是 17:06:29。时钟没同步。这个细节删掉,意思完全不受影响。)


  站长个人见解,现在我的本地开发流程里强制加了一步:启动 dev server 后,必须等 NVDA 和 VoiceOver 同时加载完毕,再点开页面。中间如果弹出“是否允许辅助功能访问此页面”,就得重来。有同事问值不值得,我说值——毕竟比起去年春晚凌晨三点收到的那个报警:/accessibility/audit/voiceover-fail-rate > 0.08,这点等待时间,已经算真空环境里最稳定的基态了。


  下一步?先把那个 useFocusLock 里的 crossOrigin 字段 grep 出来,删了,注释写清楚:“不是 bug,是 feature in a broken universe”。再看看能不能跟 Chromium 社区混个 commit 权限——不是为了修它,是想在源码里加一行 // @ts-ignore 时,至少能签上自己的名字。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!