加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.0155.com.cn/)- 应用程序、AI行业应用、CDN、低代码、区块链!
当前位置: 首页 > 运营中心 > 建站资源 > 建站经验 > 正文

视障友好建站运维:DBA的无障碍避坑指南

发布时间:2026-11-05 11:08:03 所属栏目:建站经验 来源:DaWei
导读:  2026年8月,凌晨一点零七分,我在公司工位上盯着控制台里那一长串 aria-hidden="true" 嵌套在 role="button" 里又套了个 tabindex="-1" 的 div,手悬在键盘上方三秒没落下去——不是不会删,是删完下个构建日志里又自动

  2026年8月,凌晨一点零七分,我在公司工位上盯着控制台里那一长串 aria-hidden="true" 嵌套在 role="button" 里又套了个 tabindex="-1" 的 div,手悬在键盘上方三秒没落下去——不是不会删,是删完下个构建日志里又自动冒出来。是的,那个 build 用的是 webpack 5.89.0,我翻过 node_modules 里的插件版本号,顺手记在便签纸上,现在那张纸还压在我竖着放的 Dell U2723DX 显示器底下。


  起因是产品临时把「无障碍适配」加进上线 checklist,就在原定上线前48小时,邮件抄送全组,标题写着「视障用户也是真实用户」,附件是设计稿 PDF。第7页标注栏里用灰色小字写了句「按钮状态用 icon 区分,文字已隐藏」,但没标哪个 icon 是「完成」、哪个是「失败」,也没说灰度值是否低于 4.5:1。我们后端当时正改支付回调重试逻辑,接口字段刚从 status_code 改成 result_state,文档还没同步到 Swagger——于是前端同学拿 mock 数据写了 aria-label,写的是「操作成功(mock)」。上线前 QA 报 bug:NVDA 读出来是「操作成功(mock)按钮」,而生产环境点进去弹窗标题却是「付款异常,请重试」。我查了下数据库,那条订单记录的 result_state 字段确实是 'payment_failed',但前端渲染层没做状态映射,直接读了 mock 文本。


  会议室白板没擦干净,上面还留着三天前中台权限模型讨论的箭头和圈,最底下一行被咖啡渍晕开的字写着「accessibility ≠ screen reader only」。产品经理站在旁边端着杯子站了很久,杯沿有浅浅一圈口红印,她没说话,我就知道这事儿绕不过去。后来发现,是测试机用的 Windows 11 + JAWS 2024,而我们本地跑的是 macOS + VoiceOver,两个读屏引擎对 live region 的触发时机差了约 1.2 秒——这个数字不是测出来的,是我录了三次屏幕,逐帧拖着看时间轴时记下的。某次回放卡在 document.activeElement 切换那帧,看到控制台报错:「Failed to execute 'focus' on 'HTMLElement': The element is not focusable」,而它前面一个 aria-live="polite" 容器正在被 v-html 动态替换。Vue 的 diff 算法刚好把焦点元素干掉了,但 DOM 树里那行 tabindex="-1" 还赖着不走,因为没人调 el.removeAttribute('tabindex')。


  背后有人走过碰了椅子,发出金属腿刮地的声音。我转头看见运维同事在改 CDN 缓存头,他刚把 Cache-Control: public, max-age=31536000 换成 no-cache,原因是某次灰度发版,旧 JS 文件缓存没清掉,导致新写的 useAccessibilityFocus() hook 在 Safari 里始终不执行。但他没动 HTML 缓存,所以首页那个带着 aria-labelledby="header-title" 的 标签,引用的 id 在新版模板里早改成 page-header-title 了。线上运行着一对错位的 aria 引用关系——浏览器不报错,screen reader 直接静音两秒,再读出 header-title 下面那段被 CSS clip-path 遮掉的 h1 文本。那 h1 文本里还有个链接,href 是空字符串,只靠伪类画了个下载图标。谁碰了?是设计给的 Sketch 文件导出 SVG 时漏勾了「输出为 symbol」,前端复制粘贴到代码里, 自然找不到定义。


文章配图,仅供参考

  键盘上 ESC 键掉了,胶帽躺在右下角托盘里,每次想快捷退出调试都得伸手摸。外卖到了没人去拿,在门口塑料袋窸窣响了两次,最后被保洁阿姨拎走了。我打开数据库查了一眼 user_preference 表里最近 7 天新增的记录,过滤条件是 accessibility_mode = 'high-contrast' ——结果是 0。不是没数据,是字段名早就改成 ui_theme,枚举值也从字符串换成 tinyint,但埋点 SDK 还在往老字段塞值,因为那次重构没同步更新埋点配置表的 JSON Schema。我们连自己库里哪些字段真正被读屏工具触达都不知道,更别说测语音交互路径是否完整。上次测表单提交,输入框加了 aria-describedby="error-msg",但后端返回的 error_code 对应提示文案在 i18n JSON 里压根没翻译,只有一行英文占位符「Validation failed」,被 screen reader 念出来像一句遗言。


  我不是说「别做无障碍」。是说当你用 role="application" 给整个管理后台套壳时,请确认所有快捷键组合在 NVDA 和 ChromeVox 下行为一致;当你让设计师在 Figma 标注里写「此处需 keyboard-only 可达」时,先拉着他看一遍键盘 Tab 流程图里有多少个 tabindex="0" 是裸挂在 上的;当你们争论「loading 状态要不要加 aria-busy="true"」时,至少查一下当前页面用了几个 fetch() 并发——因为并发一多,aria-busy 切换时机会糊成一片,读屏器根本分不清哪次是登录、哪次是列表刷新。这些东西不是文档能兜住的,是得在 devtools 里点开「Rendering」面板勾上「Emulate vision deficiencies」,然后亲手按五次 Tab 键,听清每一句播报之间的停顿间隙才敢上线。


  我现在每天早上第一件事,是用 Windows 测试机开 JAWS,访问 staging 域名,从首页 Tab 到底部备案号,录一段音频,下班前听一遍。如果哪天录出来首屏转圈、按钮点了没反应、控制台一片红——我就知道今天不能合并主干。至于那块没擦的白板、竖着的显示器、掉在托盘里的 ESC 键……随它去。下周我打算把 DBA 巡检脚本加一条:定期扫描 html_content 字段里所有 aria- 属性值,跟线上实际 DOM 结构做 diff。不为别的,就为下次再被叫进会议室时,能指着 build 日志里那行「[Accessibility] detected dangling aria-labelledby」,而不是光坐着听别人讲「真实用户」。

(编辑:站长网)

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