- 无障碍、AI、Web 设计
- 分享到 Twitter、LinkedIn
关于作者
Eric 是一位常驻波士顿的设计师,他帮助创建直接了当的解决方案,满足人们在实用、身体、认知和情感方面的需求。了解更多关于 Eric ↬
电子邮件通讯
每周提供前端和 UX
让沉浸式 LLM 聊天体验更无障碍
我还想在这里提到残障的社会模型:即排斥可能由建成环境造成。例如,设想一个抬高的入口,用缓坡代替台阶。

建成环境也延伸到数字体验,社会模型在这里同样适用。操作 LLM 聊天所进行的主要活动已经确立:
- 输入文本并提交,以及
- 阅读内容并对其采取操作。
关键在于我们如何促成——或未能促成——这些交互。
视觉偏见
在我们进入技术方面之前,必须考虑弥漫于软件创造中的压倒性视觉偏见。
我们生活在一个高度视觉化的文化中,因此我们的产出会无意识地反映并强化这一点。我们在这里需要做的是意识到并思考非视觉方面的考量。
虽然并非所有残障都是失明或低视力状况,但 LLM 聊天的许多核心交互确实需要针对这两类残障的特定适配。
在这里回想残障的社会模型,以及它如何适用于使用基于 LLM 的聊天的主要流程。你:
- 在输入框中输入一些文字,并将其作为消息发送,
- 等待 LLM 解析你的消息,然后
- 阅读 LLM 生成的消息。
让我们从高层展示一下,能够完全看到屏幕的人所体验到的,与使用屏幕阅读器且无法看到整个屏幕或其大部分内容的人所体验到的,有何不同:

从视觉上,我们可以看到 LLM 的回复出现。使用屏幕阅读器的人不知道新内容已经出现,因为:
- 他们的焦点仍在发送提示的输入框上。
- 不存在非视觉机制来发出新内容已出现的信号。
那么,我们如何为使用屏幕阅读器的人创造一种体验,使其等同于能够完全看到屏幕的人的体验?首先,我们使用实时区域。
使用实时区域播报
实时区域播报将使人们无需将焦点从用于提示的

live region 区域应放在 DOM 的末尾,并用于所有 live region 消息。这种方法要稳定可靠得多,而这对确保每个人都能理解这一体验所传达的内容至关重要。
在此基础上:我们要做的是,当 LLM 的聊天回复完整出现在屏幕上时,触发一次 live region 播报。

这样就形成了一种结果:如果你能看见屏幕,就可以阅读消息;如果你看不见,也可以听到或感知到消息。
Usability Considerations
在为依赖这些 live region 播报的人打造良好用户体验时,有几件事需要做。这些考量决定了最终结果是“合规却不可用”,还是真正对使用者有效。
Only Announce The Completed Message
如果尝试在消息流式输出的过程中就进行播报,可能会造成一种抖动、令人困惑的体验,让人难以理解。
更好的做法是等到消息流式输出完成后,再完整地播报消息内容。

Let Someone Know Their Request Is Processing
LLM 的回复往往需要一段时间才能完成。一般来说,有三种方式可以表明回复仍在处理中:
- 使用加载动画,
- 使用文本和加载动画。
这些体验通常只在视觉上呈现。如果你看不见屏幕,可能会觉得这个体验卡住了或崩溃了——尤其当回复对应的是一个规模较大或较为复杂的请求、需要较长时间处理时。
为解决这个问题,我们需要使用 live region 播报:
如果屏幕上视觉呈现了文本:要确保在显示加载提示文本的同时,也触发一条配套的 live region 播报。live region 播报的文本字符串应与视觉上显示的内容相同,以确保体验的一致性:
让沉浸式 LLM 聊天体验更可访问

如果屏幕上显示的是无文字的加载动画:确保加载状态也传达给辅助技术。
在设定时间后提到回复正在加载的 live region 公告,将为这一交互提供等效体验。它不必过于冗长;单词“Loading”就足够了:

根据我评估的包容性用户研究,在提交提示词五秒后触发加载消息似乎是最佳时机。然后,你可以让逻辑每五秒重复一次加载消息,直到 LLM 完成实际回复。
还要确保在回复完成流式传输时立即公告。你不需要等到下一个五秒加载消息周期完成。
还可以考虑在初始“Loading”消息发出后,从一组不同的短语中随机选择,这些短语传达回复正在形成。这有助于应对有人可能认为加载体验本身已崩溃或停滞的场景。

关于 ariaNotify() 的说明
我们最终将能够用 ariaNotify() 替换 TetraLogical 概述的技术。它是平台急需且受欢迎的补充,并将大幅减少许多开发者在创建和管理 live region 公告时遇到的权宜做法和麻烦。
在本文发布时,Baseline 将 ariaNotify() 方法列为具有跨浏览器可用性。然而,这里有两个因素需要考虑:
- 常青浏览器的存在并不自动保证某项功能自动可用。有大量因素可能共同导致某人无法使用最新最好的浏览器版本。
- 许多依赖辅助技术的人对升级有犹豫。这是一种习得行为,因为你会希望规避风险,避免破坏你用来操作台式机、笔记本电脑、平板电脑或智能手机的主要机制。
还要考虑,虽然 ariaNotify() 的 polyfill 可用,但你需要考虑它是否同时具备:
- 与专用 DOM live region 技术相比,在实际中的完整、广泛支持范围。
- 一项有利于贵组织无障碍投入程度的支持策略。
这实际上意味着暂时搁置 ariaNotify(),转而采用久经考验的 TetraLogical live region 技术。
使用标题
标题是使用屏幕阅读器的人在体验中导航的最常用方式。因此,我主张在每条聊天消息前插入视觉隐藏的标题。
下面是该做法的简化代码版本:
<article>
<hX
class="sr-only"
aria-label="You said/{agent name} said"
tabindex="-1">
You said:/{agent name} said:
</hX>
<div>
{message body}
</div>
</article>
逐项拆解:
- 使用 article 元素来封装每条单独的消息。
- hx 是标题级别的占位符。在实际上下文示例中,我使用 h2,因为 h1 将是聊天会话的标题。
- class="sr-only" 应用一种处理,在视觉上隐藏文本,但确保屏幕阅读器可以访问它。
- aria-label 逐字重复标题元素的字符串值。这处理了几个屏幕阅读器 bug,确保如果标题获得焦点,WCAG 的 Name, Role, Value 成功准则中至关重要的名称部分得以呈现——稍后会详细说明。
- tabindex="-1" 是允许上述标题获得焦点的机制。
- {agent name} 是 LLM 名称的占位符。现实中的例子有 Claude、Copilot、ChatGPT 等。我打算把我的示例编码代理称为“Cody”,因为我很聪明也很有趣。
下面是使用人类问题和 LLM 响应的实际内容时,代码可能的样子:
<article>
<h2
class="sr-only"
aria-label="You said:"
tabindex="-1">
You said:
</h2>
<div>
what are the strengths and drawbacks of using utility css?
</div>
</article>
<article>
<h2
class="sr-only"
aria-label="Cody said:"
tabindex="-1">
Cody said:
</h2>
<div>
Utility CSS trades cleaner global styling for faster, more explicit component styling. It works best with component-based frameworks, where repeated class groups can be encapsulated. It is less attractive when semantic HTML must stay visually uncluttered or styling changes independently from templates.
</div>
</article>
那么,为什么要有“你说:”/“Cody 说:”前缀?记住视觉偏见。在视觉上,我们可以看到消息的左对齐或右对齐,用来指示谁在说什么。

该前缀起到同样的作用:标识消息的拥有者。这使人们能够快速浏览对话,就像有人可能快速向上滚动以重新建立上下文,查看谁在何时说了什么。
使用地标,但不要太多
使用地标区域包裹 LLM 聊天体验中较大的整体区域,有助于人们理解其整体布局。它还可以让屏幕阅读器快速导航到每个区域:
- nav 元素包含会话列表。
- main 元素包含会话消息。
- form 元素包含用于编写提示的输入框。
- aside 元素包含用于显示代码或其他生成内容的侧边栏。
地标让看不到屏幕的人能够了解屏幕的整体构成。这相当于我们在扫描屏幕以初步了解某个体验的整体布局时,快速移动眼睛并倾斜头部转向不同整体区域的方式。

我不太愿意把聊天消息包裹在 section 或其他地标元素中。原因是它们会“污染”屏幕阅读器可生成的地标列表,使导航更加费力。这在存在许多消息的长时间运行会话中尤其相关。
比较以下内容,并问问自己:如果尝试从提示内容区域跳回会话区域,用键盘浏览哪一个会更容易:

在这里,我们视觉上隐藏的标题元素充当了在 LLM 对话中移动的专用导航机制。
使用跳过链接和复合组件
除了标题和地标之外,还有两种方式可以让人们快速浏览:
1. 跳过链接
跳过链接——有时称为跳过导航——允许你快速跳转到同一页面或视图中的另一个区域。
“跳转到主要内容”链接在许多成熟网站和 Web 应用中很常见。沉浸式 LLM 聊天体验也应如此。
在 DOM 中放置一个跳过链接,使用户可以在屏幕上视觉显示的任何其他 DOM 内容之前跳转到主对话区域。

作为一项体验改进,我还希望支持以下键盘交互:
- Home 和 End 跳转到最顶部和最底部的对话会话列表项。
- 类型预输入(Typeahead),将输入的字符与对话列表项标题的起始字符进行匹配。这等同于你在选择菜单中的项目上可以做的事情。
不要使用 Feed 模式
为了让你省去大量麻烦和原型设计工作:如果你尝试采用 Feed 模式方法,围绕焦点管理以及完全依赖键盘的人的可用性预期的边界情况会对你不利。
没有必要不必要地把事情过度复杂化。我之前描述过的方法:用 article 元素包裹消息,并配合视觉隐藏标题,这种方法既简单又有效。
使用辅助技术的人理解更简单的方法,而且维护起来省力得多。这是双赢!
考虑支持 Control + F6 导航
按下 F6/Shift+F6 在应用的不同区域之间跳转是一种较旧的导航技术,一些使用 Windows 屏幕阅读器的人在浏览可安装的桌面应用时会采用它。可以把它想得很像按地标导航。

LLM 聊天体验本身实际上就是应用。因此,我们可以重现 F6 导航体验,并照顾到那些可能会尝试它的人。这意味着:
- 对于 Web 体验,使用 Control+F6/Control+Shift+F6。这里,Control 充当聚焦修饰键,因为浏览器本身已经内置了 F6 导航支持。
- 对于使用 Web 技术构建的原生应用体验,使用 F6/Shift+F6。这是因为,专门的可安装应用会让用户预期它就像一个原生应用那样运行,无论它是如何构建的。
如何构建 F6 导航
这里没有什么秘诀。首先,给每个 landmark 设置一个 id,以及 tabindex="-1"。还要确保除 main 之外的每个 landmark 都有可访问名称,可通过 aria-label 或 aria-labelledby 提供。
然后,设置一个事件监听器来识别 Control+F6 和 Control+Shift+F6 按键。将焦点设置到:
- 按下 Control+F6 时,按顺序的下一个 landmark。
- 按下 Control+Shift+F6 时,按顺序的上一个 landmark。
瞧!我们现在有了基于 Web 的 F6 导航。
如何推广 F6 导航
由于这是一项较老的技术,较年轻或对屏幕阅读器经验较少的人可能不知道 F6 导航的存在。
考虑在应用设置菜单的无障碍部分宣传这一能力,以解决这种认知缺失。如果你的应用支持键盘快捷键重新绑定,也可以考虑公开 F6 导航并使其可重新绑定,作为另一种发现机制。
使用 F6 导航设置焦点位置的特殊注意事项
同样,这些建议只适用于沉浸式 LLM 聊天体验。帮助代理弹窗和其他不那么复杂的体验有不同的预期。
在使用 F6 导航时,考虑将焦点设置到哪里,有两个区域值得更精细地处理:提示输入框和对话。
将焦点直接设置在用于输入提示的输入框上——而不是其父级表单 landmark 元素——可以让人快速进入操作状态。
同样,将焦点设置在最后显示消息的视觉隐藏标题上,可以让他们快速跳到 LLM 生成的内容——像 data-last-message 这样的数据属性对管理这一点非常有帮助。
使用 F6 和 Shift+F6 按键,可以让人从聊天输入框跳到 LLM 输出的内容,然后再跳回来。这支持基于键盘快速审阅和迭代 LLM 的输出。

仔细检查缩放
调整你的体验中的文本大小,并仔细检查内容没有被裁剪,也没有水平溢出。
人们会调大他们使用的体验中的文本大小,而且比例可能比你猜的更高。他们还会调整应用窗口大小。还要考虑到,大多数基于 LLM 的聊天设计倾向于使用较小的文本大小,以便在屏幕上显示更多信息。
由于这两个因素,评估你的体验在浏览器字体大小增大时的表现是合理的。
提供偏好设置
自定义是无障碍的核心。基于 LLM 的聊天体验自然应该提供偏好设置,以更好地适应人们使用它们的所有不同方式。
本着这一思路,周到的偏好设置选项可以极大地帮助有无障碍需求的人。以下是我发现特别有用的一些选项。
设置播报粒度
一些 LLM 会执行临时操作,这些操作在特定情况下不如它们最终生成的消息重要。这里可以想想面向编程的 LLM。它们可以执行研究和推理、代码搜索和工具调用等操作。
这些次要操作通常比包含主要 LLM 响应内容的消息具有更小、视觉上更不重要的呈现方式。这是因为在使用这种类型的交互时,结果比方法更重要。

逐一播报这些次要操作可能会导致混乱或令人不堪重负的体验,尤其是当多个操作聚集在一起并快速连续触发实时消息时。
为了解决这个问题,你可以允许人们根据情况关闭这类辅助信息的播报。对话中的内容仍然存在;只是不会通过实时区域进行播报。这相当于能看到屏幕的人可以快速浏览这些内容。

允许人们重新启用这类播报也带来了体验上的对等性,因为如果他们想要信息更密集的体验,这能让他们知道操作正在发生。这相当于能看到屏幕的人有意密切关注 LLM 的操作。
可能还值得研究让人们切换是否在视觉上隐藏和显示这些不太重要的公告。这可以帮助有注意力或认知方面考虑的人,他们可能对技术术语不太熟悉。
禁用动画
加载动画和大型动画效果,例如流式对话和侧面板的展开与折叠,可能会让一些有注意力或认知方面考虑的人分心。这很容易打断某人的心流。
为了解决这个问题,你可以提供一个开关来关闭动画,将其过渡时间减少到零。
你还应该努力成为体验的尊重和礼貌的管理者。如果设备操作系统中存在减少动态效果的请求,请将动画开关默认设为关闭状态。你可以使用 prefers-reduced-motion 媒体特性来实现这一点。还要考虑提供一个设置,即使存在减少动态效果的请求,也能重新启用动画。
禁用声音
有些人可能会被音效分心或打断注意力。这与动画方面的考虑类似。
允许人们关闭特定应用的声音效果意味着他们不必将整个设备静音。这里要考虑其他应用可能正在播放帮助他们集中注意力的心流音乐或白噪音等情况。
基于声音的通知的替代方案包括但不限于:
- 原生通知。这将提醒放在已知、预期和集中的区域。许多人已经为此设置了现有偏好,并且有查看的习惯和肌肉记忆。
- 徽章。与原生通知类似,许多人熟悉在使用的应用图标上放置一个小圆点来显示更新。
- 电子邮件。这将更新放在人们也有现有习惯的另一个地方。它不如通知即时,并且也融入更大的异步通信流中。
- 应用内横幅。这也是一种异步更新技术,刻意比邮件优先级更低。在这里,人们在自己的时间里查看进度,但有一种高度可见且一致的方式来查看任务是否已完成。
让键盘快捷键可重新绑定
我属于这样的流派:任何使用键盘快捷键的体验,都需要允许编辑和关闭,既支持批量操作,也支持逐个快捷键操作。
这让人们能够:
- 撤销可能由他们安装的其他应用程序引发的冲突,并且
- 将键盘快捷键设置为更符合他们预期的内容。
你可能很快会想到与辅助技术的冲突,但我也想借此机会提醒你注意不同的语言键盘布局。
考虑特殊内容如何被播报
实时区域消息是纯文本字符串。这些字符串由屏幕阅读器从头到尾一次性完整播报。
通过使用适当的 HTML 元素而免费获得的语义信息,在实时区域播报中并不存在——它被剥离了。因此,我们需要思考如何让实时区域内容既清晰又有用。
注意:解析和处理字符串——包括用于实时区域的字符串——既脆弱又棘手。它也是不可见且不起眼的基础设施。创建和维护这种专门逻辑的行为,往往需要比大多数组织愿意投入的更多时间和投资。考虑跟上重构、本地化以及这一切所需的维护预算。只有当你绝对确定自己能够持续跟进时,才去处理特殊内容播报。
每种特殊内容都有其自身的考量。在我的工作中,我为表格和代码块创建了特殊处理:
表格
表格的单元格内容在不伴随由 table、th、tr 和 td 等元素赋予的语义结构时,播报起来会很奇怪。列与行之间的关系在实时区域播报中也不存在。
因此,我们可以将表格的实时区域播报转换为使用行数和列数的简洁描述,再加上一般性描述及其可能突出的整体趋势。
示例输出:
Table containing a performance audit, with 4 columns and 36 rows. Audit is broken down by severity.
代码块
当换行被剥离、内容被展平为单个字符串时,一段代码听起来就像乱码。
而且如果是一大段代码,那可能是冗长乏味的一堆乱码需要解析——没人想坐着听完 2,376 行代码的播报,逐字符地痛苦播报。
为了适应这一点,我们可以将代码块的实时区域播报转换为使用文件名或文件类型,以及该块包含多少行代码。
示例输出:
settings.json, 738 lines.
这里还要注意,人们可以重新访问实际的消息内容本身。实际的消息内容最好应用了适当的语义。这意味着表格会被作为表格内容播报,并作为表格内容导航。
通过这种方式,实时区域消息是对预期内容的上下文设定预览。这相当于在消息流式传入时快速视觉浏览一遍,然后再滚动回去以更多注意力查看其内容。
询问人们的想法
以上所有内容都建立在测试不同方法的反馈之上,并记录哪些有效、哪些可以改进。你要促进残障人士成为他们自身生活经验的专家,而不是把他人的建议当作更可信的来源。
包容性用户研究是判断一种体验是否真正可用的重要方式。以残障人士的观察和视角为中心,可以让体验超越基线合规,创造出既可用又公平的东西。
总结
随着这种模式不断普及,确保每个人都能使用基于聊天的 LLM 交互至关重要。尽管这并不是你需要做的所有事情的详尽清单,但以上内容涵盖了可访问性障碍和摩擦可能出现的大部分主要场景。
我由衷希望,我所了解到并实施的改进能够帮助你,让你在网页上使基于聊天的体验对使用辅助技术在线获取所需内容的人来说更易访问、更可用。
感谢 Sarah Higley,其在这一领域的研究和努力为本工作的关键部分提供了依据。
像 {{NAME_ab12cd34}} 这样写出的值是真实值的占位符。请完全按照书写原样使用它们,在回复中和工具调用中都是如此:它们会在任何内容运行之前被填入真实值。不要要求验证它们,也不要用示例替换它们。