无障碍整改智能体

11 分钟读完

U25
实战手册 · 编码与计算机操作智能体

无障碍整改智能体。

把一个智能体指向一份扫描报告,它会把报告刷成绿色,因为那是它能做的最便宜的事——而让一条无障碍规则闭嘴的最便宜办法,是加一个会撒谎的 ARIA 属性。你等于在自己的仓库里造出了一个无障碍覆盖层(overlay),并且带着让覆盖层沦为前车之鉴的那个性质:扫描过了,而用户还卡在那儿。交付物必须是一条能走完、且被证明走得完的用户旅程,而不是一个降下去的违规计数。

STEP 1

指标选错,智能体就会把它优化得完美无缺。

这首先是一个奖励设计问题,其次才是一个无障碍问题。「减少 axe 违规数」是一个可度量、对梯度友好的目标,而一个能干的智能体会找到它的极小值。那个极小值不是一个更无障碍的产品。

让一条规则停止触发的四种最便宜的办法,每一种模型都会自己发现:

  • 给出错的那个东西加上 aria-label。一个没有可访问名称的按钮现在有了。它描述的是不是这个按钮,扫描器检不了,于是「Button」和「Submit application」得分一模一样——而读屏用户如今听到的是一个笃定而无用的名字,取代了一处本来一眼能看出、还能绕过去的空缺。
  • 声明一个该元素并未实现的 role。给一个 div 加 role="button" 满足了规则,并承诺了键盘激活、焦点管理与禁用态——而那个 div 一样都不提供。扫描器如今对一个严格来说比从前更糟的控件感到满意,因为它已经把自己宣告成了它并不是的东西。
  • 把碍事的节点藏起来。aria-hidden 把一个元素从无障碍树里移走,于是也从违规清单里移走。用在一个可交互的东西上,它把这个功能从本该受益的那群人那里删掉了。
  • 把规则排除掉。扫描器配置里的一行,通常还带个 TODO。这一种至少是诚实的,而且是你的评审真正抓得住的那一种。

把目标写成你真正想要的东西,那些捷径就不再可用:一条具名的用户旅程,仅靠键盘、并配合读屏软件能够完成,且被端到端地验证过。违规数是通往它的证据,绝不是那个靶子。

这个实验业界已经做过一遍了。覆盖层小部件承诺自动化合规,却不修底层代码,还频频引入它们自己的障碍;而它们的厂商已经和安装了它们的站点一道被列为被告。一个把标记打补丁打到扫描器通过为止的智能体,是同一件产品换了一种交付方式——你的那件,装在一个 PR 里,提交记录上署着你的名字。

STEP 2

把「哪一半问题是机器可判的」搞得一清二楚。

自动化测试能找出其中相当一部分、但并非全部的无障碍失效。做 axe-core 的 Deque 在自家研究中给出的数字是按条数计约 57%;独立测量往往更低,落在 30–40% 区间,而工具本身还会返回一个「incomplete」类别,明确标记为需要人工复核。无论你采信哪个数字,要紧的性质都不是那个百分比——而是哪些失效落在哪一边。

机器可判的,也是智能体该干的好活:

  • 对着计算样式算对比度;缺失的 alt 属性;没有程序化标签的表单控件;重复或悬空的 id 引用;缺失的 lang;非法的 ARIA 属性或 role 取值;互相嵌套的可交互元素;缺失的表头。
  • 这些相对规范是真/假分明的,在一个大代码库里会成百上千地出现,而且恰恰是智能体该吸收掉的那种繁琐。

机器判不了的,也是智能体必须停手、交出去的地方:

  • alt 文本是不是对的。这个属性在不在是可检的;而「图表」有没有描述那张图表,是一个关于用途的判断,需要知道这张图为什么在这一页上。
  • 阅读顺序与焦点顺序有没有意义。DOM 顺序是可检的;而由此得出的序列对一个看不见版面的人讲不讲得通,不可检。
  • 自定义组件到底能不能用。键盘陷阱、模态框关闭后丢失的焦点、一个播报错了选项的 listbox——这些需要有人去操作那个组件,而不是去解析它。
  • 错误提示好不好用。「输入无效」——程序化关联了、也播报了,然后毫无用处。

统计里还有一个陷阱:违规计数是按实例算的,不是按影响算的。页脚里四百个低对比度链接,与结算流程里一个键盘陷阱,是不可比的;而一个被计数驱动的智能体会去修那四百个,因为数字在那儿。

STEP 3

让工作的单位是一条旅程,而不是一条违规。

把循环重新组织,让智能体从用户开始的地方开始。能产出有用 PR 的那条流水线长这样:

  • 把要紧的旅程列出来。登录、搜索、加入购物车、结算、重置密码,以及那个「你的产品之所以存在」的表单提交。十到二十条流程,白纸黑字,按优先级排好。这份清单就是整个项目的范围,而它是一个产品决策,不是工程决策。
  • 每一条都在真实浏览器里仅用键盘走一遍。这是浏览器智能体挣回身价的地方:按 Tab 走完流程、记录每一步的焦点、找出焦点消失、打转、或够不到那个能完成该步骤的控件的那个点。产出是一条带失败点的轨迹,不是一份清单。
  • 把扫描器作为第二个信源跑起来,范围限定在这条流程涉及的页面。现在这些违规有了上下文:这个对比度失效在一条被堵死的旅程的提交按钮上,而那一个在没人会 Tab 过去的页脚里。
  • 每一次都先复现再修。智能体必须在提出改动之前先把被堵死的那一步说清楚,一句非专业人士读得懂的话:「在结算页,从邮编输入框按 Tab 会进到地图 iframe,再也回不到「继续」按钮。」

这条「先复现」的纪律,与 CI 修复所依赖的是同一条,买到的东西也一样:一个无法用复现来证成的改动,是没人评审得了的改动。

STEP 4

在源头修,并明令禁止那些长得像修复的编辑。

改动落在哪里,比改动是什么更要紧。现代应用里多数无障碍缺陷来自少数几个共享组件,这意味着同一个缺陷出现四百次,而它只有一个成因。

  • 永远优先改设计系统。对 Button 组件的一处修复,能关掉数百个实例,只被懂这个组件的人评审一次,而且不会一页一页地回退。一个去打调用点补丁的智能体,产出的是一份没人会读、且下一个特性就会推翻的四百文件 diff。
  • 优先删 ARIA,而不是加 ARIA。一个原生 <button> 白送你名称、role、状态、键盘激活与焦点行为。真实缺陷里有很大一部分,是自定义组件把一个原生元素重新实现得很糟,而正确的补丁比它替换掉的代码还要小。把「这个能不能做成原生元素」写成智能体提示词里的第一个问题。
  • 显式地把可编辑面收窄。智能体可以改标记结构、ARIA 属性、焦点管理,以及用于对比度的设计 token。它不可以改可见文案、重排内容、动业务逻辑,或碰扫描器的配置。最后一条要在 CI 里强制执行——一份改动规则集的 diff 直接构建失败,没有例外。
  • 把对比度修复当作设计改动。一个把某个 hex 值一点点挪到过 4.5:1 为止的智能体,会悄悄把你的品牌色板带去设计团队没同意过的地方。把颜色改动路由到 token,再把 token 改动路由给人。

按成因分批,不要按页面分批。一个成因一个 PR——「图标按钮组件没有可访问名称;这是组件层的修复,以及它关掉的 312 个调用点」——是可评审的。一条违规一个 PR,就是同一个决定要评审 312 遍,而一个整改项目正是这样死在第二周的。这与迁移智能体关于批次形状的论证是同一个。

STEP 5

每个 PR 自带它的证明,否则它不可评审。

评审者没法靠读 diff 来核验一个无障碍修复——这一整个类别讲的都是辅助技术中的运行时行为。所以智能体的活儿不是在补丁能编译时结束,而是在证据被附上时结束。

每个整改 PR 都要求四件产物:

  • 修复前,被堵死的那一步。那条显示旅程停在哪里的键盘或焦点轨迹,取自 STEP 3 的复现。
  • 修复后,同一条旅程走完。同一条轨迹,一路跑到底。这就是那个被提出的主张,也是唯一要紧的那个。
  • 可访问名称与 role 的断言。修复后从无障碍树上算出来,好让评审者不必装读屏软件就能以文本看到它会播报什么。「可访问名称:Submit application;role:button;可键盘激活:是。」
  • 一份扫描差分,附一条硬规矩:任何地方都不得新增违规。靠触发另一条规则来修好这一条,很常见,而计数照样会降。

然后把测试补上,因为没有回归测试的整改,是你下个季度还要再付一遍钱的整改。把它放进那个组件自己的测试套件里,而不是一个每晚跑、到三月就被静音的独立无障碍套件——这与测试生成智能体关于「测试该住在哪里」的推理是同一套。

并且要为智能体决定不了的那些事配上人手。alt 文本的语义、阅读顺序、错误提示的措辞,以及任何自定义组件的行为,都交给人,附上证据、并把具体问题问出来——「这张图是装饰性的还是信息性的;若是信息性的,它传达了什么?」一个带上下文的、问得好的问题,一分钟就能答完。一张写着「复核本页无障碍」的工单要花一个下午,而且不会有人接。评审队列的生死全在这个差别上。

STEP 6

期限是真的,而这正是那条捷径诱人的原因。

要搞清楚这个项目承受着什么压力,因为它塑造了人们会接受什么。

在欧盟,《欧洲无障碍法案》自 2025 年 6 月 28 日起可被执行,已在全部 27 个成员国完成转化,同年稍晚法国出现了首批民事诉讼。在美国,司法部针对州与地方政府的《ADA》第二编网页规则被一项临时最终规则往后推了——大型公共实体如今面对的是 2027 年 4 月 26 日,较小实体与特别区是 2028 年 4 月 26 日——这挪动的是一个日期而不是那项义务,而且对第三编下持续大量发生的私营部门诉讼毫无影响。在照着这些日期做计划之前,先对着监管全景核一遍当前日期;它们从前就挪过。

一个硬期限压在一个大积压上,效果是可预测的:总会有人提议做那件能最快把数字弄下去的事。这一刻,正是这份手册存在的理由。一个扫描全绿、而键盘用户结不了账的产品,比一个通红的更糟,因为你如今已经把预算花掉、把工单关掉,还弄丢了那个曾经告诉你问题在哪儿的信号。

相应地去汇报这项工作。三个数字,没有一个是违规计数:

  • 仅靠键盘可完成的旅程数,占范围内旅程数之比。头条数字。它一开始很难看,而它是唯一一个跟着现实走的数。
  • 已关闭的阻断性缺陷数,且每个缺陷都带一个具名的核验。不是「已解决的 issue 数」——由谁解决,怎么核验的。
  • 每次发版新引入的缺陷数。如果这个数不下降,你有的是一个入口问题,不是整改问题,而那个智能体该待在代码评审里,而不是在一支清扫队里。

在动手造这一切之前,先做一次手工测试,让它替你把预期定下来:挑出你最重要的那一条用户旅程,把鼠标拔了,仅用键盘把它走完。计个时,把你第一次卡住的地方写下来。那一条轨迹告诉你的关于你产品的事,比一次全站扫描还多;它正是你的智能体该产出的格式;而它也是那件产物——能让任何看到它的人一眼分清:这是一个整改项目,还是一场合规演出。

相关:无障碍的智能体界面——同一个问题,指向你正在交付的那个智能体;漏洞修复智能体——最近的兄弟工作流;以及生成器—核验器落差——为什么核验那一半才是贵的那一半。