Install
openclaw skills install a11y-label-audit扫描 React/TSX 组件中缺失的无障碍标签并自动修复。 检测纯图标按钮缺少 aria-label、div/span 模拟按钮缺少 role 和键盘交互、 图片缺少有意义的 alt 文本、表单元素缺少 label 关联、 弹窗缺少 aria-modal 和焦点管理、动态内容缺少 aria-live 等问题。 同时考虑跨平台兼容性问题:Tauri 桌面端(WKWebView/WebView2)、 微信/QQ 内置浏览器、Chrome 扩展、受限 WebView 等不同引擎对 ARIA 属性和焦点管理的支持差异。 当用户提到无障碍审查、a11y 检查、WCAG 合规、添加 aria 标签、 屏幕阅读器支持、无障碍改进、兼容性问题时使用此技能。
openclaw skills install a11y-label-audit扫描当前目录或指定文件中 React/TSX 组件缺失的无障碍(a11y)标注,并自动修复。
检测:可点击元素中仅包含图标组件(如 <YbIcon>、<Icon>、<SvgIcon>、<img>),没有可见文本,也没有 aria-label / aria-labelledby。
修复:根据图标的 type / name 属性或上下文推断标签内容,添加 aria-label。如果项目使用 i18n 函数(t()),用其包裹标签文本。
检测:<div> 或 <span> 带有 onClick 处理器但缺少:
role="button"tabIndex={0}onKeyDown / onKeyPress 键盘事件处理(Enter/Space 触发点击)修复:添加 role="button"、tabIndex={0},以及一个在 Enter/Space 时触发点击的 onKeyDown 处理器。
检测:<img> 元素中:
alt 属性完全缺失alt 为无意义值,如 alt="image"、alt="photo"、alt="picture"、alt="icon"装饰性图片使用 alt="" 是合法的,不应标记。
修复:根据上下文(文件名、周围文本、父组件语义)添加描述性 alt。如确为装饰性图片,添加 alt="" 和 role="presentation"。
检测:<input>、<textarea>、<select> 没有关联的 <label>(通过 htmlFor / 包裹方式)或 aria-label / aria-labelledby。
修复:根据 placeholder、附近标题或父级上下文推断文本,添加 aria-label。可行时优先使用显式的 <label> 包裹。
检测:弹窗/对话框组件(如 Dialog、Modal、YbcDialog、Drawer)缺少:
aria-modal="true"(如果底层库未设置)aria-labelledby 或 aria-label 指向对话框标题修复:添加指向标题元素 id 的 aria-labelledby,或在无标题元素时添加 aria-label。焦点管理问题以警告形式标注。
检测:动态内容区域(Toast 通知、加载状态、错误提示、聊天消息)缺少 aria-live、aria-atomic 或 role="status" / role="alert"。
修复:添加合适的 aria-live="polite"(默认)或 aria-live="assertive"(错误/警告),以及需要整体重新播报时添加 aria-atomic="true"。
任务进度:
- [ ] 步骤 1:确定扫描范围(文件/目录)
- [ ] 步骤 2:按 7 条规则搜索违规项(含兼容性)
- [ ] 步骤 3:汇总审查报告
- [ ] 步骤 4:应用自动修复
- [ ] 步骤 5:验证修复未引入新问题
使用 Glob 查找目标目录下所有 .tsx 文件:
**/*.tsx
排除 node_modules、dist、build、.next、__tests__、*.test.tsx、*.spec.tsx。
针对每条规则,使用 Grep 查找潜在违规项:
纯图标按钮:
onClick.*<(YbIcon|Icon|SvgIcon)
然后验证父级可点击元素是否存在 aria-label。
div 模拟按钮:
<(div|span)[\s\S]*?onClick
然后验证是否存在 role="button" 和 tabIndex。
缺失 alt:
<img[\s\S]*?(?!alt=)
未标注的表单元素:
<(input|textarea|select)[\s\S]*?(?!aria-label)
未标注的弹窗:
<(Dialog|Modal|YbcDialog|Drawer)
然后验证是否存在 aria-labelledby 或 aria-label。
缺少实时区域标注的动态内容:
(toast|Toast|notification|Notification|loading|Loading|errorMsg|errorMessage)
然后验证附近是否存在 aria-live 或 role="alert" / role="status"。
兼容性问题:
inert[^a-zA-Z]
检查是否有 inert 回退。
:focus-visible
检查是否有 :focus 基础样式回退。
suppressHydrationWarning
检查同一元素是否有正确的 alt / aria-label。
role="(status|alert|feed)"
在微信/QQ 环境中这些 role 不可靠,检查是否有语义化元素替代或多通道回退。
按以下格式输出审查结果:
## 无障碍审查报告
### 文件:src/components/Example.tsx
| 行号 | 规则 | 问题 | 建议修复 |
|------|------|------|----------|
| 42 | icon-button-a11y | onClick div 中的 `<YbIcon>` 缺少 aria-label | 添加 `aria-label={t('关闭')}` |
| 78 | div-button-a11y | `<div onClick>` 缺少 role 和键盘交互 | 添加 `role="button"` tabIndex={0} onKeyDown |
使用 StrReplace 逐项应用修复。保留:
t() / useTranslation,用其包裹标签文本修复后,对修改过的文件运行 ReadLints,检查是否引入了新错误。
TDesign 的 Button、Dialog、Input 等通常内部处理了基本无障碍。重点关注:
content 或 children 等提供隐式标签的 propsYBButton、YbcDialog 是薄层封装。检查 a11y props 是否被正确透传。如果封装层吞掉了 aria-* props,需要标记。
<Image>Next.js 的 Image 组件默认要求 alt 并会发出警告。仍需验证 alt 文本是否有实际意义。
本项目运行在多种平台上,不同引擎对无障碍 API 的支持存在差异,修复时必须考虑兼容性。
| 平台 | 引擎 | 无障碍支持等级 |
|---|---|---|
| 桌面端(Tauri macOS) | WKWebView | 较好,VoiceOver 支持完整 |
| 桌面端(Tauri Windows) | WebView2 (Chromium) | 好,NVDA/JAWS 支持完整 |
| 桌面端(Tauri Linux) | WebKitGTK | 一般,Orca 支持有限 |
| Chrome/Edge 扩展 | Chromium | 好 |
| 微信内置浏览器 | 自研 X5 / 系统 WebView | 较差,部分 ARIA 属性被忽略 |
| QQ 内置浏览器 | MQQBrowser 内核 | 较差 |
| PC 微信 | Chromium 内嵌 | 一般 |
| 企业微信 | 系统 WebView | 一般 |
| 小程序 WebView | 受限沙箱 | 差,焦点管理受限 |
7a. inert 属性兼容(compat-inert)
inert 属性在旧版 WebView 中不被支持。如果使用了 inert,需要同时提供 polyfill 或回退方案(aria-hidden="true" + tabIndex={-1})。
7b. <dialog> 元素兼容(compat-dialog)
原生 <dialog> 元素在 X5 内核、旧版 WKWebView、小程序 WebView 中可能不受支持。优先使用库组件(TDesign Dialog / YbcDialog)而非原生 <dialog>。
7c. focus-visible 伪类兼容(compat-focus-visible)
:focus-visible 在微信 X5 内核中可能不生效。需要同时保留 :focus 样式作为回退:
.btn:focus { outline: 2px solid var(--focus-color); }
.btn:focus:not(:focus-visible) { outline: none; }
.btn:focus-visible { outline: 2px solid var(--focus-color); }
7d. aria-live 在受限 WebView 中的兼容(compat-live-region)
微信/QQ 内置浏览器中 aria-live 可能不触发屏幕阅读器播报。对于关键通知,除了 ARIA 标注外,建议同时提供视觉反馈(如 Toast 动画)和触觉反馈(如震动 API)作为多通道回退。
7e. 焦点管理在小程序 WebView 中的限制(compat-focus-trap)
小程序 WebView 沙箱中 document.activeElement 和程序化 focus() 行为不一致。弹窗焦点捕获应通过库组件实现,避免直接操作 DOM focus。检查是否存在 isMpWebview 条件分支来处理此类差异。
7f. role 属性在 X5 内核中的支持(compat-role)
微信 X5 内核对某些 ARIA role(如 role="status"、role="alert"、role="feed")支持不完整。在这些环境中,优先使用语义化 HTML 元素(<button> 替代 role="button"、<nav> 替代 role="navigation")。
7g. suppressHydrationWarning 与无障碍的交叉(compat-hydration)
项目中大量使用 suppressHydrationWarning。检查设置了此属性的 <img> 元素是否同时有正确的 alt,防止 SSR/CSR 不一致导致 alt 文本丢失。
<button>、<a>、<input> 在所有引擎中的无障碍支持都优于 ARIA 标注的 <div>isDesktop、isMpWebview、isWeChat 等环境检测,为受限平台提供替代方案if ('inert' in HTMLElement.prototype))而非 UA 嗅探当存在大量违规时,按以下顺序修复(影响从高到低):