无障碍设计:从第一行代码起的运维尊重
|
去年秋天那个改版上线翻车——PM 周五下午三点发钉钉:“今晚十二点切流,灰度 5%,后端已ready,前端自测过,无障碍?嗯…应该…没大问题吧。”我盯着这句话,手悬在键盘上没敲回车,光标在“应该”俩字后面闪了十七秒。这个我敢打包票,她删掉“应该”之前,连 screen reader 是读 aria-label 还是 innerText 都分不清。 抬手、按键、松手——F5 刷新监控面板。Chrome DevTools 切在 Network tab,但鼠标刚划过去就卡住半秒,像被静电吸住;我原以为是内存 leak,结果发现是隔壁测试同学把 Performance tab 开着录了四十分钟,占满渲染进程。他缩在工位角落猛敲 Ctrl+Shift+P 找“Stop recording”,汤碗搁在显示器支架右边第三格,一滴鸡汤顺着碗沿滴到无线充电板边缘,没擦,也没人管。 无障碍设计:从第一行代码起的运维尊重——这话不是口号,是筛子。不是筛用户,是筛团队。你写的第一个 没加 type=“button”,它可能活过三轮 PR review、穿得过七次 E2E,直到某天晨会前两分钟,后端突然切走了一个 header cookie,整个表单 submit 键盘回车失效——而那个按钮根本没 type,submit 事件直接冒泡到 form,form 提交刷新页面,再重定向进死循环。我抓着鼠标滚轮往下翻 console,看见十八毫秒前报的 Error:Cannot read property 'focus' of null,而那行代码在 AccessibilityHelper.ts 第四百二十三行,是 PM 上个月说“先注释掉,下个迭代加 focus 管理”的那块。 旁边同事起身倒水,保温杯盖拧开时咔哒一声,我听见自己喉结动了一下。他走后,我点了下键盘左下角 Caps Lock,键帽弹起来一半卡住,反复按三次才复位——这破键盘用了四百七十二个节点的集群日志都换不来一个新轴体。双屏壁纸是去年团建拍的机房合影,左边机柜顶灯亮着,右边灭了,没人记得关,照片里看得见但生产环境真没人在意。
文章配图,仅供参考 优胜劣汰,不是指用户被筛掉,是指谁写的代码带 aria-invalid="true" 却不联动校验逻辑,谁的 modal 透传 ESC 键却漏掉 focus trap,谁的 toast 关闭按钮没加 role="button" ——这些不是 bug,是信用破产通知书。你测一遍 NVDA + Chrome,它过了;但 WCAG 2.1 有二百六十五条准则,其中一百零九条是运维根本没法测的:比如 color contrast 在暗光办公室 LCD 反光下的感知差异,比如屏幕放大到 400% 后 viewport meta 被 js 动态重写两次的 layout shift。这些事我说不准,真不敢打包票,连我们自己的 CI pipeline 都没配 color-contrast-checker 插件,只靠 jest-axe mock 掉所有 DOM 结构……mock 得越像,漏得越狠。 上周跟产品经理吵,她把 Figma 文件发过来,里面 text layer 名字叫 “Button_States_Hover_Focus_Active_Disabled”,但实际导出的 HTML class 是 btn-primary--sm。我问:“hover 和 focus 态语义区分在哪?”她回:“视觉上不一样啊。”我截图发给她,同一行 CSS:&:focus { outline: 2px solid #007bff; },然后打字:“那 keyboard users 按 Tab 进来,看见的是你给的蓝色边框,还是你没写的 ‘登录按钮 已聚焦’ 语音提示?”她隔了十九分钟回了个“…”。后来这需求进了 backlog,状态改成“待排期(无障碍专项)”,但下周 sprint 规划会上,后端甩出个三百七十二个节点的链路追踪图,说新接口 RT 增加十二毫秒,“必须压缩”,PM 立刻把那个“无障碍专项”拖到“下下个季度”。 CSS 命名这事真操蛋。我们有个组件库叫 @xxx/ui-kit,里面 button 的 class 是 x-button x-button--primary x-button--lg x-button--loading。你知道 x-button--loading 是怎么实现的吗?用 visibility: hidden 干掉文字,再加一个 SVG spinner,但 spinner 的 role=“img” 漏写了 aria-hidden=“true”,于是 NVDA 读出来是:“spinner image,button,loading”。没人修,因为测试用例跑的是 snapshot diff,diff 里看不见 screen reader 的 utterance sequence。这个我敢打包票,我们组三个人轮番 debug 过,最后是运维值班夜里收到 P0 报警——不是错误率飙升,是某个盲人用户提交了十七次表单失败后,在客服入口连点四十二次“语音助手模式切换”,触发了 rate limit 告警。 工资条上“无障碍合规激励金”这个条目,从今年三月起就没见过实发金额。HR 解释说是“按季度审计后发放”,但审计报告我从来没在 Confluence 看到过 PDF。昨天翻 Git blame,发现 last modified 是去年十一月,作者是前端组长,修改内容是把一行 aria-live=“polite” 改成 aria-live=“assertive”,理由是“提升提示优先级”——可那天压根没发布,那行代码在线上躺了三个月,和它同文件的另一个 元素至今没有 optgroup 的 label 属性,连最基础的 VO 朗读都崩成“option item one option item two”,而这个 select 正在主站订单页用着。 所以我不信什么“上线后再补”,就像不信“等流量上来再做扩容”。你第一行 React useState 初始化的时候,有没有顺手写个 aria-describedby 对应的 id?你写 fetch.then 的时候,有没有留一毫秒等 focusable 元素 render 完再 call .focus()?没有。那就别怪某天凌晨三点告警里混着一句:“accessibility audit failure: document.title mismatch after SPA navigation”——而那句 title update,是你用 useEffect 写的,依赖项里偏偏漏了 location.pathname。 我现在把 accessibility-linter 插件设成 pre-commit hook,强制跑 axe-core CLI。卡住就卡住,总比上线后被监管函问“贵司是否符合《互联网网站适老化及无障碍改造专项行动方案》”强。至于那个卡住的 Caps Lock 键——算了,不换了。反正下周又要切一个新版,这次 PM 说“全量灰度,不回滚”。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


MySQL事务控制无障碍设计指南