内容
触觉
<p align="center">
<img src="https://raw.githubusercontent.com/tactual-dev/tactual/main/assets/logo.png" alt="Tactual logo" width="160">
</p>
屏幕阅读器导航成本分析器。衡量屏幕阅读器用户发现、到达和操作网页上每个交互目标所需的按键次数 — 在特定的辅助技术配置文件(NVDA、JAWS、VoiceOver)下。
## 功能
现有的无障碍工具检查**一致性** — ARIA 是否正确?对比度是否足够?
Tactual 衡量**导航成本** — 屏幕阅读器用户到达“结账”按钮需要多少操作?如果他们越过目标怎么办?他们能否发现目标存在?菜单是否在按下 Enter 键时打开,还是仅在单击时打开?焦点是否落在第一个菜单项上还是停留在触发器上?
工作原理:
- Playwright 无障碍快 屏幕阅读器- 可选地(菜单、对话框、选项卡、披露)并键盘事件探测,包括 APG 样式的小部件表单错误流程
-具有入口点(地标、标题、线性 Tab)的并对每个目标进行 可选地使用pup/virtual-screen- 验证预测路径以
Tactual 是用于分析您自己的网站和暂存环境的开发人员工具。可以在本地、CI 中运行,或中的 MCP 服务器运行扫描服务。
## 适用场景
Tactual 补充了诸如 axe-core 和 Pa11y 等一致性扫描器。这些工具的 WCAG 和 AR规则覆盖的正确第一步。Tactual 旨在解决下一个问题:在页面具有后,AT 用户发现、到达和操作重要目标有多高?
在进行屏幕阅读器导航成本分类、路径跟踪、测量的键盘证据、前后 优先级和 MCP 工作流程时使用 Tactual,其中代理需要带有源选择器候选的紧凑型发现结果。在关键旅程的最终验证、时序敏感流程、浏览器/AT 设置和故意与通用 APG 示例不同的实现模式时,使用真实的屏幕阅读器和手动测试。
**代理快速路径:** 本 README 是一个产品概述和参考。代理应首先使用 `docs/AGENT-RECIPES.md` 中的任务模式和 `docs/MCP.md` 中的完整 MCP 架构,然后回到此处仅用于产品上下文、安装说明和发布表面示例。
需要 Node 或更高版本
npm install tTactual 将 Playwright 作为运行时依赖安装,因此px tactual@latest ...` 命令无需将 Play独安装到 npx 缓存中。MCP作为运行时依赖提供,因此-mcp`安装的 `t 包中运行,无安装 SDK。
## 快速入门
### 命令行界面
# 分析默认配置文件:generic-mobile-web-sr-v0)
npx tactual analyze-url https
# 使用特定的分析
nactual analyze-url https://profile voiceover-ios# 探索隐藏菜单、选项卡、披露)
nactual analyze-url https:// --explore
# 为您的使用评分预px tactual analyze://shop.com --presetout
npx t-url https://docspreset docs-site 输出为 JSON、Markdown
npx analyze-url https://example json --output reportpx tactual analyze-url https://example.com --format sarif --output report.s# 比较两次分析npx tactual baseline.json candidate.json
tactual diff-results baseline.json --format json 打印 NVDA 在按 Tab 键
npx t://example.com tactual transcript https.com --at voiceover 列出可用的文件和评分预npx tactual profiles tactual presets 运行基准测试套件 tactual benchmarkpx tactual benchmark --suite all
基准测试固 npm 包一起提供,因此在全新安装,无需将存储件克隆到当前目录。
# 验证预测路径屏幕阅读器(步骤计数) 需要可选依赖情况下与 tactual 一npx t https://example.com-targets 10 semantic
# 初始化 t 配置文件
npx init
# 使用 + 真实 Chrome机器人保护的网站
tactual analyze-url https://www.npmjs.com/ --stealth --channel chrome
# 深入的键盘探测,包括显示的小部件和表单错误流程
npx tactual analyze-url https://docs.example.com --probe --explore --probe-mode deep
# 焦点探测在一个打开的分支上,例如对话框触发器
npx tactual analyze-url https://.com/settings \
--probe \
-selector "[ariaprofile-dialog']" \
-strategy modal
# 分析 +虚拟 SR 验证(预测步骤)
npx analyze-url https://examplevalidate --validate-max10
```
控制台输出包括每个发现的紧凑路径行,显示屏幕用户如何到达它:
```
██████ link:reference structural
D:4771 O:100 Rec:100
getBy', { nameReference' })
Tab ×2 "v" → K" → Tab "Reference → 目标无法或地标导航```
其中 `Tab `nextItem`` = ``,`;` = `nextLandmark`,` = `nextLinkB` =`,`Enter 激活。连续操作步骤折叠(`Tab ×2`### 从审计到修复应用或预览障碍工作,T提供了小规模、可的证据:
发现的 `selectorpenalties`、`Fixes` 和
- 分组的 ` 和汇总输出候选
-_pages.site.repeatedNavigation跨路由的重复- `diff / `diff_results`
首先进行,然后在更改深一个路由bash
# 站。重定向便工具使用。
nactual analyze-pages \
https://app.example.com https://app \
https://.com/settings \
nvda-desktop-v0 --format json > tactual-site.json
#一个路由并生成 Markdown 报告 tactual analyzeapp.example.com/docsprofile nvda \
--probe --probe-mode --format markdown ---report.md
# 当一个分支是目标打开它并在那里花费探测预算。
npx analyze-url https://app/docs \
nvda-desktop-v0 \
--probe \
--entry-selector "[aria-controls='search-panel']" \
--probe-selector "#search-panel" \
--probe-strategy composite-widget \
--format markdown --output tactual-search-panel.md
# 在编辑之前保存基线,然后验证补丁。
npx tactual analyze-url https://app.example.com/docs --explore --probe json --output baseline# 在本地存储一个根本原因构建/重启预览:
npx tactual analyze-url https://app.example.com/docs --explore --probe json --output
npx tactual.json candidate.json将候选部分组件、导航模式或小部件合约等重复的起点。在项目期望或 PR ,确认源组件路由、命令、、用户影响、和验证。分数变动证据,但更改的无障碍行为为准。
MCP 相同的紧凑审查循环保持、选择器、更改和前后验证的范围内。
### 库 API
```typescript
import { analyze, getProfile } fromactual";
import { } from "twright";
import { chromium } from "playwright = await chromium.launch page = await browser.newPage();
await page.gotoexample.com");
const state = await captureStateawait browser.close();
const profile = getProfile("-web-sr-v0");
const result = analyze], profile);
for (const finding of result.findings console.log(fId, finding.sc, finding.severity);
}
```
**屏幕阅读模拟器** — NVDA、JA VoiceOver 将为每个目标宣布的内容,包含选中、展开、模态、值、必填、无效等):typescript
importScreenReader,
Announcement,
buildAnnouncement,
build} from "tactual";
const report = await simulateScreenReader(page,);
for (const a of report.formFields console.log(a.announcement);
//Subscribe, check box, checked"
// → "Country, combo box, collapsed"
// → "Email, invalid entry, required, you must use a work address"
}
// 比较阅读器
const tx.targets[5Announcement(tx, " // → " box, collapsed(tx, "voiceover"); // → "Country, popup button"
// 同时显示这三个
buildMultiATAnnouncement(tx);
// → { nvda: "...", jaws: "...", voiceover: "..." }
// 线性导航记录 — SR 用户在 Tab 键遍历页面时听到的内容
const transcript = buildTranscript(state.targets, "nvda");
// → [{ step1, kind: "landmark", announcementMain, main landmark ...]
// 多目标导航模式(线性、按标题地标、按表单控件)
import { } from "tactual/playwright";
// 导航(NVDA:H 键)
const headings = buildNavigationTranscript(state.targets, { mode: "by-heading" });
//元素导航到另一个元素
const path = buildNavigationTranscript(state.targets, {
: "link:before-main",
to: "heading:welcome",
linear",
});
//级的地标(在 DOM 中但被 HTML夺,例如 <header> 在 <section>for (const d of report.demotedLandmarks) {
console.warnReason);
}
```
**验证和校准 API 将模型 SR 验证运行或人为观察数据集进行比较:
```typescript
import { validateFindingsInJsdom } from "tactual/validation";
import { runCalibration, formatCalibrationReport }tactual/calibration定一个 JSDOM 实例、PageState、校准数据集 validation = awaitingsInJsdom(dom, result.find {
maxTargets10,
strategy:",
});
constCalibration(dataset, new Map([[state.url, result]]));
console.log(CalibrationReport(calibration));
```
或者从 CLI```bash
npx tactual transcript https://example.com --at voiceover
npx tactual calibration-report my-calibration.json ---nvda.json
```
模拟器是启发式预测,而不是真正的输出。模拟器本身速度很快(纯捕获的目标上旦目标在内存中,耗时不到一秒),但完整的 `analyze-url包括浏览器启动 + + 评分,耗时几秒钟,小页面耗时较短,启用(~30和 `--explore1-5 分钟复杂 SPA 上)长。分析在无头浏览器中默认运行时不会弹出任何窗口。(使用 `--no` 或 `--channel chrome --stealth` 访问可见/受机器人保护的网站。)
**数据质量。** 与 [W3C ARIA-AT 项目](https://aria-at.w3.org) 中的令牌级断言进行校准:**77/77 角色/名称/状态令牌断言(JAWSOver)上全部通过,准确100,涵盖 36 个单一目标模式(按钮、切换所有菜单按钮变披露、折叠面板、复选框/三态、开关、滑块、对话框、警报、链接、选项卡、单选旋转按钮、菜单栏)的角色/名称/状态措辞,以及 目标地标构建后运行 `` 以验证最新的上游断言。这是对模拟器的校准,而不是对浏览模式、详细设置、时序或每个有效小部件变体的屏幕阅读器保真度的完整证明。校准集之外的 AT 特定覆盖为高/中/信度。
### MCP 服务器
T用于 AI 代理消耗的 MCP 服务器:
# 启动(stdio 默认)
npx tactual-mcp
# 传输启动(适用于托管平台、远程客户端px tactual-mcp --http # http://127..1:/mcp
tactual-mcp --http --port=3000 # 自定义端口(或设置 PORT 环境变量)
npx tactual-mcp --port 3000 # 也支持的格式
actual-mcp --host=0.0.0 绑定到默认:127.0.1)
```
对于面向网络的部署,请将 HTTP 传输置于身份验证的 TLS 代理后面范围限定为受客户端。有关清单和威胁模型 [SECURITY.md](SECURITY.md)。
** MCP 工具 工具 || --- | --- |
| `analyze_url`页面的 SR 导航成本(默认 SARIF可选探索、键盘/小部件/、作用域/目标导向探测、隐通道用于机器人保护,以及过滤。 |
| `trace_path` | 目标的逐步导航路径,带有模拟的 SR 公告。 |
| `validate_url` | 验证预测路径是否与 `@guidepup/virtual-screen-reader` 一致。返回每个策略的可达性和平均准确性。关闭预测和验证循环。 |
| `calibration_report` | 运行观察到的校准保存的完整分析 JSON,并返回用于调优工作流的结构信号。 |
| `list_profiles` | 列出可用的 AT。 |
| `` | 比较两次分析结果 — 改进、严重性变化| `suggestations` |排名的修复建议| `save_auth` | 认证并保护内容的会话状态| `analyze | 多页面站点分类,汇总统计数据面的重复导航。 |
完整的:[docs/MOLS.md](docs/MCP-TOOLS.md)
#### 由 AI 工具在项目中包:
npm install tactual
```
**Cl — 添加到项目根目录下的 `.m` 中:json
{
cpServers": {
"tactual":type": " "command":",
": ["tactual"]
}
}
```
**GitHub** — 添加copilot/mcp.json` 或 `~/./mcp`:
``` "m": {
"tactual": {
"type": "stdio",
command": "npx",
"argsactual-mcp }
}
}
```
**Cursor / Wind Cline**编辑器的 MCP 配置格式:
{
"": {
actual": {
": "n "argstactual-mcp"]
}
}
}
直接(全局安装)** — 如果您使用 npx:
```bash
g tactualactual-mcp 在 stdio 上启动 MCP 服务器
```
### GitHub Actions
使用 GitHub Actions 市场中的复合操作:
```yaml
jobs:
a11y:
runs-on: ubuntu-latest
permissions:
security-events: write # 用于 SARIF 上传
pull-requests: write # 用于拉取请求评论
steps:
- name: 分析可访问性
uses: tactual-dev/tactual@v0.5.0
with:
url: https://your-app.com
profile: nvda-desktop-v0
explore: "true"
probe: "true"
probe-mode: standard
fail-below: "70"
comment-on-pr: "true"
```
该操作安装 Tactual 和 Chromium 浏览器二进制文件,运行分析,将 SARIF 上传到 GitHub 代码扫描,并在平均分数低于阈值时使构建失败。设置 `comment-on-pr: "true"` 以在拉取请求上发布摘要评论(在重新运行更新)。输出 `average-score` 和 `result-file` 以供下游步骤使用。操作版本跟踪 Tactual 版本 - 升级 `uses:` 行以获取补丁。
默认设置是保守的:`probe` 除非启用,否则关闭,因为它会发送真正的键盘事件,而强制颜色图标检查仅针对声明 `visualModes` 的配置文件(如 `nvda-desktop-v0` 和 `jaws-desktop-v0`)运行。
或者直接使用 CLI 以获得更多控制:
```yaml
- name: 安装 Tactual
run: npm install tactual
- name: 安装浏览器
run: npx playwright install chromium --with-deps
- name: 运行可访问性分析
run: npx tactual analyze-url https://your-app.com --format sarif --output results.sarif --threshold 70
- name: 上传 SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
```
#### 回归门(CI 失败比基准线更差)
将 `--baseline` 与 `--fail-on-regression` 结合使用,将 Tactual 变成一个严格的 CI 门控:保存一个已知良好的构建的基准线,然后在更改使 N+ 个发现比基准线更差时使拉取请求检查失败。`diff-results` 命令也可以单独运行,以提供人类可读的 before/after 报告。
```yaml
# 一次性:快照 main 作为基准线
- name: 快照基准线
if: github.ref == 'refs/heads/main'
run: |
npx tactual analyze-url https://preview.your-app.com \
--format json --output tactual-baseline.json
- name: 上传基准线
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: tactual-baseline
path: tactual-baseline.json
# 在拉取请求上:与基准线比较,回归失败
- name: 获取基准线
uses: actions/download-artifact@v4
with:
name: tactual-baseline
- name: 分析 + 门控回归
run: |
npx tactual analyze-url https://pr-preview-${{ github.event.number }}.your-app.com \
--format sarif --output results.sarif \
--baseline tactual-baseline.json \
--fail-on-regression 3 # 如果 3+ 个发现回归,则失败
```
或者通过操作:
```yaml
- uses: tactual-dev/tactual@v0.5.0
with:
url: https://pr-preview.your-app.com
baseline: tactual-baseline.json
fail-on-regression: "3"
```
该操作镜像 `analyze-url` CLI 界面用于分析输入,以及 CI 到 CLI 的契约测试,以保持这些字段一致。一些工作流控件是操作编排,而不是直接的 CLI 标志:`fail-below` 包装 CLI `--threshold`,`comment-on-pr` 控制拉取请求评论步骤,SARIF 上传由工作流处理。您将设置的最常见的输入包括 `profile`、`explore`、`explore-depth`、`explore-budget`、`explore-timeout`、`probe`、`probe-mode`、`probe-strategy`、`scope-selector`、`probe-selector`、`entry-selector`、`goal-target`、`goal-pattern`、`stealth`、`channel`、`wait-for-selector`、`exclude`、`exclude-selector`、`focus`、`min-severity`、`max-findings`、`baseline`、`fail-on-regression`、`fail-below`、`validate`、`storage-state`、`summary-only`。当您想要使用与操作固定的 Tactual 版本不同的版本时,上述直接 CLI 调用模式仍然是推荐路径。
| 界面 | 命名约定 | 示例 |
| ------- | ----------------- | ------- |
| CLI | kebab-case 标志 | `--probe-strategy modal-return-focus` |
| MCP 和库选项 | camelCase 字段 | `probeStrategy: "modal-return-focus"` |
| GitHub 操作 | kebab-case 输入 | `probe-strategy: modal-return-focus` |
## 配置
### CLI 标志
```
选项:
-p, --profile <id> AT 配置文件(默认:generic-mobile-web-sr-v0)
-f, --format <format> json | markdown | console | sarif(默认:console)
-o, --output <path> 写入文件而不是 stdout
-d, --device <name> Playwright 设备仿真
-e, --explore 探索隐藏分支
--explore-depth <n> 最大探索深度(默认:3)
--explore-budget <n> 最大探索操作(默认:50)
--explore-timeout <ms> 总探索超时;包括探测时间(默认:60000)
--explore-max-targets <n> 停止前最大累积目标(默认:2000)
--allow-action <patterns...> 允许探索匹配这些模式的控件(覆盖安全)
--exclude <patterns...> 按名称/角色 glob 排除目标
--exclude-selector <css...> 按 CSS 选择器排除元素
--scope-selector <css...> 仅捕获、评分和探测这些子树
--focus <landmarks...> 仅在这些地标内分析
--suppress <codes...> 抑制诊断代码
--top <n> 仅显示最差的 N 个发现
--min-severity <level> 要报告的最低严重程度
--threshold <n> 如果平均分数 < N,则退出非零
--preset <name> 评分预设(ecommerce-checkout,docs-site,dashboard,form-heavy)
--config <path> tactual.json 的路径
--no-headless 有头浏览器(适用于 bot 阻止的网站)
--channel <name> 浏览器通道:chrome,chrome-beta,msedge(使用已安装的浏览器;绕过大多数 bot 检测)
--stealth 反检测默认值:现实的 UA,覆盖 navigator.webdriver,伪造插件/语言
--user-agent <ua> 覆盖 User-Agent 字符串
--timeout <ms> 页面加载超时(默认:30000)
--probe 选择加入运行时键盘探测以交互目标
(焦点,激活,Escape,Tab)。
还探测菜单,对话框,选项卡,披露,组合框/列表框,
和表单错误模式。
当与 --explore 结合使用时,还会探测显示状态的目标
(菜单项,对话框主体,扩展的小部件)。
--probe-budget <n> 覆盖通用探测预算(默认:每个 --probe-mode)
--probe-mode <mode> fast | standard(默认)| deep。
fast=5 个通用/5 个菜单/3 个模态/5 个小部件;
standard=20/20/10/20;deep=50/40/20/40。
预算在初始 + 所有显示状态之间共享。
--probe-selector <css...> 仅探测这些子树而不更改捕获/评分
--entry-selector <css> 在捕获/探测之前激活此触发器
--goal-target <target> 确切的目标 id/名称/角色/种类/选择器提示
--goal-pattern <pattern> glob 目标 id/名称/角色/种类/选择器提示
--probe-strategy <strategy> all | overlay | composite-widget | form |
navigation | modal-return-focus | menu-pattern
--validate 在捕获的 DOM 上运行虚拟屏幕阅读器,并包含
一个预测与验证的步骤比较在输出中。
需要可选依赖:jsdom + @guidepup/virtual-screen-reader。
除非省略可选依赖,否则默认安装。
--validate-max-targets <n> 最大验证发现(默认:10)
--validate-strategy <mode> 虚拟 SR 导航策略:linear | semantic(默认:semantic)
--check-visibility 强制每个图标的对比度检查,跨配置文件视觉模式
--no-check-visibility 即使配置文件声明模式,也禁用每个图标的对比度检查
--detect-routes 记录 SPA 路由更改期间的分析
--descend-frames 包括 iframe 无障碍目标;Chromium 可以通过 CDP 恢复许多跨源 OOPIFs
--auto-scroll 在捕获前滚动以显示延迟/无限滚动内容
--dismiss-banners 尽力清除安全的 cookie/同意横幅
--probe-hover 悬停可能触发器以显示悬停内容
--walk-tab-order 记录 Tab 遍历以检测焦点顺序/焦点陷阱问题
--diff-viewports 比较桌面和移动捕获以获取隐藏内容
--wait-for-selector <css> 在捕获前等待选择器(适用于 SPA)
--wait-time <ms> 页面加载后额外等待
--storage-state <path> 用于身份验证页面的 Playwright storageState JSON
--also-json <path> 还写入此路径的 JSON(单个分析运行适用于 CI)
--summary-only 仅返回摘要统计信息,无需个别发现
-q, --quiet 抑制信息诊断
```
### tactual.json
使用 `tactual init` 或手动创建:
```json
{
"preset": "ecommerce-checkout",
"profile": "voiceover-ios-v0",
"exclude": ["easter*", "admin*", "debug*"],
"excludeSelectors": ["#easter-egg", ".admin-only", ".third-party-widget"],
"scopeSelectors": ["main"],
"probeSelectors": [".checkout-dialog"],
"probeStrategy": "modal-return-focus",
"focus": ["main"],
"suppress": ["possible-cookie-wall"],
"threshold": 70,
"priority": {
"checkout*": "critical",
"footer*": "low",
"analytics*": "ignore"
}
}
```
配置文件从工作目录自动检测(`tactual.json` 或 `.tactualrc.json`)。CLI 标志与配置文件设置合并并覆盖。
## AT 配置文件
| 配置文件 | 平台 | 描述 |
| -------------------------- | -------- | ----------------------------------------------------- |
| `generic-mobile-web-sr-v0` | 移动设备 | 标准化移动 SR 原语(默认) |
| `voiceover-ios-v0` | 移动设备 | iOS Safari 上的 VoiceOver — 基于转子的导航 |
| `talkback-android-v0` | 移动设备 | Android Chrome 上的 TalkBack — 读取控件 |
| `nvda-desktop-v0` | 桌面设备 | Windows 上的 NVDA — 浏览模式快速键 |
| `jaws-desktop-v0` | 桌面设备 | Windows 上的 JAWS — 虚拟光标和自动表单模式 |
配置文件定义每个导航操作的成本、评分维度权重、`costSensitivity`(缩放可达性衰减曲线)和上下文相关修饰符。有关实现细节,请参见 `src/profiles/`。
**移动配置文件限制。** `voiceover-ios-v0` 和 `talkback-android-v0` 配置文件准确模拟操作成本和 SR 公告措辞,但 Tactual 的键盘探测( `--probe`)仅测试桌面交互(Tab、Enter、Escape)。它们 **不** 模拟触摸手势(单击、双击、右滑、三指滑动、转子旋转等)。对于移动配置文件,评分维度反映来自配置文件模型的预测成本 — 而不是测量的行为。仍然需要真实设备测试来验证移动无障碍。
**视觉模式。** `nvda-desktop-v0` 和 `jaws-desktop-v0` 配置文件声明一个 `visualModes` 矩阵(浅色/深色 × 强制颜色开启/关闭),因此分析器在每个组合后重新模拟捕获并采样每个图标的计算样式。发现生成器比较每个图标的计算 `fill` 与最近的非透明祖先 `background-color`,并在对比度低于 WCAG 1.4.11 非文本阈值(3:1)时发出惩罚。
四种惩罚措辞,三种评分等级:
| 惩罚措施 | 触发因素 | 运维影响 |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| `图标在 <mode>` 中不可见` | 对比度 < 1.5:1,无相邻文本标签 | 运维能力上限 60 |
| `装饰图标在 <mode>` 中不可见` | 对比度 < 1.5:1,控件有可见文本标签 | 运维 -5 |
| `图标在 <mode>` 中对比度低` | 对比度 1.5-3.0:1,无相邻文本标签 | 运维 -5 |
| `作者设置的 SVG 填充颜色在 <mode>` | 对比度 OK 在 Playwright(≥3:1)但模式为 `forced-colors: active` 并且填充颜色是作者的 CSS 字面量(非系统,非 `currentColor`) | 运维 -2 |
检查跳过已经是 HCM 安全的图标:`fill="currentColor"`、`fill: ButtonText`(或任何系统颜色),`forced-color-adjust: none`(作者退出),或计算 `fill === color`(CSS 应用的 currentColor)。与可见文本标签相邻的低对比度图标完全被抑制 — 标签标识控件,图标是强化。
**为什么存在替换风险层级。** 不同的用户 HCM 主题有不同的 Canvas/ButtonText/系统颜色值。作者的 literal 填充颜色(例如 `svg { fill: #e4e6e6 }`)可能与 Chromium 的默认 HCM 调色板对比良好,但与特定的用户主题对比度差。Playwright 在 Playwright、Chrome 和 Edge 中对 literal 本身的渲染在 `forced-color-adjust: preserve-parent-color`(SVG 路径的默认设置)下是一致的 — 具体的关注点是主题变异性,而不是隐藏的操作系统绘制替代。Tactual 标记模式,以便您知道在具有代表性的 HCM 主题的真实 Edge 中进行验证,而不是因为 Playwright 的对比度测量是误导的。
通过 `--no-check-visibility`、`checkVisibility: false` 在 `tactual.json` 中或 MCP `analyze_url` 工具上的 `checkVisibility: false` 显式禁用。强制启用 `--check-visibility`,即使配置文件不声明模式(无操作,无模式)。
检查增加了大约 +50-200ms 每个声明的模式每个页面 — 重新模拟媒体是便宜的;每个模式都没有新的浏览器上下文。
## 评分预设
预设捆绑了用于常见用例的焦点过滤器和优先级映射。它们位于配置文件和 CLI 标志之下(预设 → tactual.json → CLI 标志)。
| 预设 | 用例 | 焦点 | 关键目标 |
| -------------------- | -------------- | ---------------- | ------------------------------------ |
| `ecommerce-checkout` | 购物流程 | main | 结账,购物车,支付,购买 |
| `docs-site` | 文档 | main, navigation | 搜索,导航 |
| `dashboard` | 网页应用 | main, navigation | 保存,提交,创建,删除,搜索 |
| `form-heavy` | 表单页面 | main | 提交,保存,下一页,继续,错误 |
```bash
npx tactual analyze-url https://shop.com --preset ecommerce-checkout
npx tactual presets # 列出所有预设及其详细信息
```
预设默认抑制 cookie 横幅和 analytics 目标。要覆盖,请使用 `--exclude` 或在 `tactual.json` 中设置 `priority`。预设不组合 — 只能激活一个 `--preset`。
## 评分
每个目标都会收到一个**5维评分向量**:
| 维度 | 测量内容 |
| -------------- | -------------------------------------------- |
| 可发现性 | 用户能否察觉到目标的存在? |
| 可达性 | 到达目标的导航成本是多少? |
| 可操作性 | 控件的行为是否可预测? |
| 恢复能力 | 从过度偏移中恢复有多困难? |
| 互操作风险 | AT/浏览器支持差异的可能性(惩罚) |
不同配置文件中,维度权重有所不同:
| 配置文件 | 可发现性 | 可达性 | 可操作性 | 恢复能力 | 成本敏感度 |
| ------------------------ | -------- | -------- | -------- | -------- | ----------- |
| generic-mobile-web-sr-v0 | 0.30 | 0.40 | 0.20 | 0.10 | 1.0 |
| voiceover-ios-v0 | 0.30 | 0.35 | 0.20 | 0.15 | 1.1 |
| talkback-android-v0 | 0.25 | 0.45 | 0.20 | 0.10 | 1.3 |
| nvda-desktop-v0 | 0.35 | 0.25 | 0.30 | 0.10 | 0.7 |
| jaws-desktop-v0 | 0.30 | 0.25 | 0.35 | 0.10 | 0.6 |
**综合评分:** 加权几何平均数:`overall = exp(sum(w_i * ln(score_i)) / sum(w_i)) - interopRisk`。每个维度在取对数前均设有一个最低值,以避免对数运算中的零值。当某维度为零时,该维度对几何平均数的贡献将被显著降低。
**严重性等级:**
| 评分 | 等级 | 含义 |
| ------ | ---------- | -------------------------- |
| 90-100 | 优秀 | 低关注度 |
| 75-89 | 可接受 | 需要改进 |
| 60-74 | 中等 | 需要审查 |
| 40-59 | 高 | 可能造成明显阻碍 |
| 0-39 | 严重 | 可能造成阻塞 |
## 诊断
Tactual 会发出有关捕获可靠性、页面结构、视觉可访问性、运行时证据、ARIA 有效性以及重复成本模式的诊断信息。警告是审查提示,而不是自动合规失败。许多视觉/内容检查是启发式的,应在提交缺陷之前在上下文中确认。
| 代码 | 级别 | 含义 |
| --- | --- | --- |
| `blocked-by-bot-protection` | 错误 | 检测到机器人防护页面;捕获的内容不是预期页面。 |
| `empty-page` | 错误 | 完全没有找到目标。 |
| `ok` | 信息 | 捕获产生了一组目标,没有可靠性警告。 |
| `possibly-degraded-content` | 警告 | HTTP 页面的目标数量可疑地少。 |
| `sparse-content` | 警告 | 只找到了 1-4 个目标。 |
| `possible-login-wall` | 警告 | 怀疑存在身份验证门控内容或登录重定向。 |
| `possible-cookie-wall` | 信息 | 可能存在 Cookie 同意墙,遮挡了内容。 |
| `redirect-detected` | 信息/警告 | 捕获结果跳转到了不同的 URL 或域名。 |
| `timeout-during-render` | 警告 | 请求的渲染等待超时。 |
| `framework-detected` | 信息 | 在捕获过程中检测到了前端框架信号。 |
| `spa-route-changes` | 信息 | 在分析过程中发生了 SPA 路由变化。 |
| `exploration-no-new-states` | 警告 | `--explore` 运行了,但没有发现额外状态。 |
| `frames-descended` | 信息 | 捕获或跳过了 iframe 子帧。 |
| `auto-scrolled` | 信息 | 自动滚动在捕获前运行,并报告了它所发现的内容。 |
| `banners-dismissed` | 信息 | 尝试关闭 Cookie/同意横幅。 |
| `tab-order-walked` | 信息/警告 | 标签顺序遍历记录了焦点停止;对正 `tabindex` 发出警告。 |
| `viewport-divergence` | 警告 | 桌面/移动视口差异导致目标、标志或标题缺失。 |
| `no-headings` | 警告 | 没有找到标题元素。 |
| `heading-skip` | 警告 | 标题层次结构跳过了一级,例如 `h1 -> h3`。 |
| `empty-heading` | 警告 | 标题存在但没有文本。 |
| `numeric-heading` | 警告 | 标题文本仅为数字、标点符号或微不足道的单个字符内容。 |
| `h1-count` | 信息/警告 | 页面没有有用的单个 H1,或有多个 H1 需要审查。 |
| `no-landmarks` | 警告 | 没有找到标志区域。 |
| `no-main-landmark` | 警告 | 缺少 `<main>` 标志。 |
| `no-banner-landmark` | 信息 | 缺少 `<header>` / 横幅标志。 |
| `no-contentinfo-landmark` | 信息 | 缺少 `<footer>` / 内容信息标志。 |
| `no-nav-landmark` | 信息 | 缺少 `<nav>` / 导航标志。 |
| `landmark-demoted` | 警告 | HTML 标志存在,但因嵌套上下文而被降级。 |
| `structural-summary` | 信息 | 单行结构概述。 |
| `no-skip-link` | 警告 | 页面有 5+ 个目标,但没有 skip-to-content 链接。 |
| `broken-skip-link` | 警告 | Skip 样式链接指向不存在的片段目标。 |
| `skip-link-not-first` | 警告 | 跳过链接存在,但不是在前两个 Tab 停止中可达。 |
| `visual-order-divergence` | 警告 | 视觉顺序似乎与 DOM/SR 导航顺序有差异。 |
| `shared-structural-issue` | 警告 | 影响超过 50% 目标的惩罚被提升到页面级别。 |
| `redundant-tab-stops` | 警告 | 多个链接目标创建了重复的 Tab 停止,指向同一目的地。 |
| `data-flow-dependencies` | 信息 | 探索的状态揭示了仅在先前操作后才启用的控件。 |
| `form-summary` | 信息/警告 | 总结表单,并在表单似乎缺少提交控件时发出警告。 |
| `missing-autocomplete` | 警告 | 标准表单字段缺少有用 `autocomplete` 标记或禁用它们。 |
| `empty-interactive` | 警告 | 交互目标没有可访问的名称。 |
| `fake-interactive-elements` | 警告 | 可点击的非语义元素不可用键盘/SR 访问。 |
| `cdp-click-listeners` | 警告 | CDP 在非交互元素上发现了类似点击的监听器。 |
| `ambiguous-link-names` | 警告 | 具有相同可访问名称的链接指向不同的目的地。 |
| `media-without-controls` | 警告 | 音频/视频缺少控件且未隐藏。 |
| `duplicate-id` | 警告 | 重复的 `id` 值会破坏标签和 ARIA 引用。 |
| `nested-interactive` | 警告 | 交互控件嵌套在其他交互控件内。 |
| `meta-refresh` | 警告 | 页面通过 meta refresh 自动刷新或重定向。 |
| `missing-image-alt` | 警告 | 图片缺少 `alt` 属性。 |
| `suspicious-image-alt` | 警告 | 图片替代文本看起来像占位符或文件名占位符。 |
| `missing-iframe-title` | 警告 | iframe 缺少 `title` 或可访问标签。 |
| `missing-html-lang` | 警告 | `<html lang>` 缺失或看起来不像 BCP 47 语言标记。 |
| `poor-document-title` | 警告 | 文档标题缺失、为空、太短或通用。 |
| `viewport-blocks-zoom` | 警告 | 视口元设置限制用户缩放。 |
| `low-contrast-text` | 警告 | 交互文本或标题未达到 WCAG 样式文本对比度阈值。 |
| `color-only-conveyance` | 警告 | 文本似乎仅通过颜色传达意义。 |
| `color-blindness-contrast-fail` | 警告 | 在模拟色觉障碍下,文本对比度降低。 |
| `lang-switch-without-marker` | 警告 | 文本语言似乎在没有 `lang` 标记的情况下更改。 |
| `invalid-aria-role` | 警告 | 存在非标准 ARIA 角色。 |
| `unknown-aria-attr` | 警告 | 存在未知 `aria-*` 属性。 |
| `invalid-aria-attr-value` | 警告 | ARIA 属性值在允许的值集之外。 |
| `missing-required-aria-attr` | 警告 | ARIA 角色缺少必需的状态或属性。 |
| `aria-naming-prohibited` | 警告 | 名称应用于禁止命名的角色。 |
| `unsupported-aria-attr-for-role` | 警告 | ARIA 属性在元素的角色上不受支持。 |
## 探索
`--explore` 标志激活有界分支探索:
- 打开菜单、选项卡、披露、折叠和对话框
- 从隐藏的 UI 捕获新的可访问状态
- 将发现的目标标记为 `requiresBranchOpen`
- 尊重深度、操作次数、目标数量和新颖性预算
- 安全操作策略阻止破坏性交互
探索对于具有显著隐藏 UI 的页面很有用(例如,下拉菜单、选项卡式接口、模态对话框)。
探索候选项在迭代之前按稳定键(角色 + 名称)排序,因此相同的页面内容在多次运行中产生相同的探索顺序。
## 探针
`--probe` 标志测量重要的交互模式在出现在可访问树后是否有效。探针是可选的,因为它们会发送真正的键盘事件并增加运行时。自 0.4.0 以来,这包括通用焦点/激活检查、菜单合约、模态对话框合约、触发器到对话框流程、选项卡、披露、组合框、列表框和必填字段错误流程。探针发现包括证据摘要,以便报告将测量的故障与建模或启发式评分区分开来。
目标导向的控件在复杂的 SPA 中保持深层探针有用:
| 需求 | CLI | MCP/Action 字段 | 效果 |
| ---------------------- | ------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| 分析一个子树 | `--scope-selector "#drawer"` | `scopeSelector` / `scope-selector` | 仅捕获、评分和探测选定的子树(s)。 |
| 探测一个子树 | `--probe-selector "#drawer"` | `probeSelector` / `probe-selector` | 保持页面范围的评分,但仅在选定的子树(s)内花费探测预算。 |
| 首先打开一个分支 | `--entry-selector "[aria-controls='menu']"` | `entrySelector` / `entry-selector` | 在捕获/探测之前激活触发器,并优先考虑新揭示的目标。 |
| 针对已知目标 | `--goal-target "checkout"` | `goalTarget` / `goal-target` | 将探测范围缩小到匹配的目标 ID、名称、角色、类型或选择器。 |
| 按全局匹配 | `--goal-pattern "*dialog*"` | `goalPattern` / `goal-pattern` | 与目标一致,但使用全局匹配。 |
| 按意图花费预算 | `--probe-strategy modal-return-focus` | `probeStrategy` / `probe-strategy` | 运行与 `all`、`overlay`、`composite-widget`、`form`、`navigation`、`modal-return-focus` 或 `menu-pattern` 相关的探测家族。 |
例如,要评估一个模态分支而不爬行无关的菜单:
```bash
npx tactual analyze-url https://app.example.com/settings \
--profile nvda-desktop-v0 \
--probe \
--entry-selector "[aria-controls='profile-dialog']" \
--probe-strategy modal-return-focus \
--format markdown
```
### 探索预算
| 预算 | CLI 标志 | 默认值 | 目的 |
| ------- | ----------------------- | -------- | --------------------------------------------------------------------------------- |
| 深度 | `--explore-depth` | 3 | 最大递归深度 |
| 操作 | `--explore-budget` | 50 | 所有分支的总点击预算 |
| 目标 | `--explore-max-targets` | 2000 | 如果累积的目标超过此值,则停止 |
| 时间 | `--explore-timeout` | 60000 ms | 绑定总探索时间,包括初始探测、分支捕获和揭示状态探测 |
**尺寸指导:
| 页面类型 | 建议设置 | 为什么 |
| ------------------------------------------------------------ | ------------------------------------------------------------------- | ---------------------------------- |
| 营销站点、文档页面、博客 | 默认设置 | 小表面,默认很少达到 |
| 带有侧边栏/菜单的仪表盘 | `--explore-depth 3 --explore-budget 50` (默认值) | 捕获一层菜单打开 |
| 复杂应用(Figma、Notion等) | `--explore-depth 4 --explore-budget 100 --explore-max-targets 5000` | 更深的菜单,更多的状态 |
| 具有非常大的隐藏UI的页面(表情选择器、颜色网格) | `--explore-max-targets 10000` 加上 `--exclude "emoji-*"` | 限制或过滤掉洪流 |
| 未知页面的快速排查 | `--explore-depth 1 --explore-budget 10` | 只打开明显的分支,速度快 |
如果在打开有用的分支之前探索超时,请慢慢提高 `--explore-timeout` 和 `--explore-budget`,或使用 `--entry-selector`、`--probe-selector` 和 `--probe-strategy` 将预算花费在您关心的分支上。如果输出有重复的目标,请降低 `--explore-depth`(深度递归可以通过不同路径重新发现相同的元素)。
### SPA 框架检测
Tactual 检测到 SPA 内容在捕获可访问树之前已渲染。检测到的框架:React、Next.js、Vue、Nuxt、Angular、Svelte 和 SvelteKit。还检查了通用 HTML5 内容信号(地标、标题、导航、链接)。对于未被自动检测覆盖的 SPA,请使用 `--wait-for-selector`(CLI)或 `waitForSelector`(MCP/API)指定一个 CSS 选择器,指示您的应用已水化。
在初始框架检测后,Tactual 使用基于收敛的轮询 - 重复快照可访问树,直到目标计数稳定 - 这与框架无关。
对于 SPA-heavy 应用,这些可选的捕获帮助程序很有用:
```bash
npx tactual analyze-url https://app.example.com \
--wait-for-selector "main" \
--detect-routes \
--auto-scroll \
--descend-frames \
--diff-viewports
```
- `--detect-routes` 记录 `pushState`、`replaceState`、`popstate` 和 `hashchange` 事件,这些事件在分析期间发生。
- `--auto-scroll` 在捕获前显示 IntersectionObserver 驱动的延迟内容。
- `--descend-frames` 追加 iframe 目标并附加帧 URL 属性。 同源框架使用 Playwright 的 frame-scoped 可访问性快照;Chromium 对跨源 OOPIF 降级为 CDP(当正常路径无法访问时)。Firefox/WebKit 保留对无法访问的框架的现有跳过行为。
- `--diff-viewports` 捕获目标、地标或标题内容在桌面和移动视口之间消失的内容。
- `--dismiss-banners`、`--probe-hover` 和 `--walk-tab-order` 为常见的 SPA 叠加层和焦点顺序错误添加有针对性的运行时证据。
### 已知页面基准
要针对复杂的公共 SPA/组件库页面发布证据,请运行:
```bash
npm run benchmark:known-pages
```
脚本构建软件包,运行 `analyze-url` 并启用 SPA 帮助程序堆栈,
并在 `build/known-pages-*` 下写入 `run-results.json`、`summary.json` 和 `REPORT.md`。 它故意不是 CI 门控:公共站点会发生变化,
阻止自动化,并随着时间的推移提供不同的内容。使用报告来发现
漂移和类别级惊喜,然后使用本地固定件或项目拥有的页面进行确定性回归门控。 要进行有界冒烟运行或 APG/W3C
捕获质量探针,请直接调用脚本,例如
`node scripts/known-pages-corpus.mjs --build --limit 1` 或
`node scripts/known-pages-corpus.mjs --build --include-capture-probes`。
## 回归跟踪
比较两个分析运行以捕获回归:
```bash
# 保存基线
npx tactual analyze-url https://your-app.com --format json --output baseline.json
# 更改后再次运行并差异化
npx tactual analyze-url https://your-app.com --format json --output candidate.json
npx tactual diff-results baseline.json candidate.json`
```
差异显示了改进的目标、回归或严重性变化,以及解决和添加的惩罚。在 CI 中,使用 `comment-on-pr` 操作输入以自动在每个拉取请求上发布结果。
## 互操作风险
Tactual 包括来自 [a11ysupport.io](https://a11ysupport.io) 和 [ARIA-AT 项目](https://aria-at.w3.org) 的 ARIA 角色/属性支持数据的静态快照。已知具有已知跨 AT/浏览器支持间隙的角色会受到互操作风险惩罚。
| 角色 | 风险 | 注意 |
| --------------------------- | ---- | -------------------------------- |
| `button`、`link`、`heading` | 0 | 支持良好 |
| `dialog` | 5 | 焦点管理变化 |
| `combobox` | 8 | 最具互操作问题的模式 |
| `tree` | 10 | 在 JAWS 外支持不佳 |
| `application` | 15 | 如果滥用则危险 |
## 发现解读
触觉发现故意混合了多个证据领域:
- **屏幕阅读器导航**:地标、标题、标签、分支发现、顺序遍历成本和模拟公告。
- **键盘可操作性**:焦点移动、激活、Escape 恢复、Tab 陷阱和运行时小部件探测。
- **结构语义**:缺失的名称、标题/地标结构、降级地标、重复共享原因。
- **互操作风险**:具有已知跨 AT/浏览器支持差距的角色和状态。
- **指针相关检查**:可能影响屏幕阅读器导航模型外部用户的目标大小和图标可见性问题。
这意味着一个页面可以具有强大的屏幕阅读器导航分数,但仍会收到跳过链接、目标大小或可见性警告。应将这些视为单独的修复类别,而不是矛盾。
探测衍生的 APG 发现被衡量为一致性警告。许多小部件具有有效的实现变体,特别是组合框和类似披露的模式,因此在将其视为强制替换之前,请根据预期的模式验证警告。关键流程仍应使用目标浏览器/AT 组合进行检查。
## 输出格式建议
| 格式 | 典型大小 | 最适合 |
| ---------- | ------------ | ------------------------- |
| `console` | ~8KB | 在终端中进行人工审查 |
| `markdown` | ~11KB | 拉取请求和问题评论 |
| `json` | ~18KB | 程序化消费 |
| `sarif` | ~4-40KB | GitHub 代码扫描/CI |
所有非 SARIF 报告器格式默认发出汇总输出:统计数据、分组问题、修复候选、证据摘要和最差发现(上限为 15)。SARIF 限制在 25 个结果。当输出被截断时,会在顶部出现一个注释。库 API 公开完整的 `AnalysisResult`;CLI 和 MCP 报告器输出故意紧凑,除非请求特定字段,例如 `includeStates`。
对于 MCP 使用,`sarif` 是默认和推荐的格式。使用 `summaryOnly: true` 进行紧凑的健康检查,包括统计数据、严重性计数、诊断和前 3 个问题。
## 校准
触觉包含一个校准框架(`src/calibration/`,导出为 `tactual/calibration`),用于针对真实数据集调整评分参数。详见 `docs/CALIBRATION.md`。
校准观察还可以包括来自 OSS 审查工作的确定性公告反馈:记录 `observedAnnouncement`,当您知道测试输出时,或者记录 `observedAnnouncementTokens`,当确切的措辞很嘈杂但角色/名称/状态令牌清晰时。触觉将其与匹配目标的模拟公告进行比较,并报告缺少或意外的令牌。使用 `tactual calibration-report` 或 MCP `calibration_report` 运行数据集以获取保存的 `analyze-url --full-json` 输出并发出 `scoringSignals`。使用 `tactual observe-announcement` 从保存的分析中生成或附加仅公告的观察结果,或者在此仓库中使用 `npm run -- nvda:vm:observe -- ...` 组织受控的 NVDA VM 捕获文件夹。仓库的版本化语料库位于 `calibration/corpus/` 下;运行 `npm run calibration:corpus` 以审计覆盖率门控,并在构建后运行 `npm run calibration:matrix` 以按 MAE、偏差、方差和陈旧序列计划漂移对可达性调优工作进行排名。
发布就绪状态和已知边界在 [docs/RELEASE_TEST_MATRIX.md](docs/RELEASE_TEST_MATRIX.md)、[docs/LIMITATIONS.md](docs/LIMITATIONS.md) 和 [docs/NVDA_VM_OBSERVER.md](docs/NVDA_VM_OBSERVER.md) 中有文档记录。
## 开发
```bash
npm install # 安装依赖
npm run build # 使用 tsup 构建
npm run test # 运行单元测试 + 集成测试
npm run test:shard -- --list # 列出有界 Vitest 发布分片
npm run test:shard -- capture # 运行一个有界 Vitest 分片
npm run test:shards # 运行所有有界 Vitest 分片
npm run test:benchmark # 运行基准测试套件
npm run typecheck # TypeScript 类型检查
npm run lint # ESLint
npm run test:release # 完整的发布分段测试
```
## 安全
### 浏览器沙盒
触觉始终启用默认的 Chromium 沙盒保护运行 Playwright。它从不禁用 Web 安全或修改浏览器的安全模型。所有页面交互都在标准 Chromium 进程沙盒中进行。
### 安全操作策略
当启用探索( `--explore` )时,触觉在激活交互式元素之前将其分为三个层级:
| 层级 | 操作 | 示例 |
| ----------- | ------------------- | -------------------------------------------------------------------------------------- |
| **安全** | 已激活 | 标签、菜单项、披露、折叠面板、同页面锚点 |
| **谨慎** | 需要谨慎激活 | 外部链接、模糊按钮 |
| **不安全** | 已跳过 | 提交按钮(搜索表单外)、删除、注销、购买、部署、取消订阅 |
这是一个基于关键字的启发式方法 — 它无法检测语义欺骗(例如,实际上删除数据的“保存”按钮)或检查服务器端行为。在生产使用中,请始终在受信任或沙盒环境中运行探索。
### URL 验证
所有 URL 在导航前都会被验证。CLI 接受 `http:`、`https:` 和 `file:` 方案,以便本地固定装置起作用;MCP URL 接受工具仅接受 `http:` 和 `https:` 以避免通过代理控制的浏览器导航暴露本地文件。`javascript:`、`data:`、`blob:` 和 `vbscript:` 被拒绝。嵌入凭据的 URL(例如 `https://user:pass@host/`)也被拒绝。私有/内部 IP 范围 **不** 被过滤 — 在具有内部服务访问权限的环境中运行触觉与让任何其他 Playwright 驱动的工具访问它们相同,因此请将 URL 输入视为受信任输入。
## 许可证
Apache-2.0
### 属性
模拟器的角色/状态措辞是根据 [W3C ARIA-AT 项目](https://github.com/w3c/aria-at) 调整的,该项目根据 [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/) 许可。触觉不捆绑 ARIA-AT 数据;校准脚本(`npm run calibrate`)在运行时从上游存储库获取断言。如果您发布触觉校准结果,请将 W3C ARIA-AT 项目作为真实断言的来源进行属性。
interop 风险评分中引用的 ARIA 角色/属性支持数据来自 [a11ysupport.io](https://a11ysupport.io) 和同一个 ARIA-AT 项目。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器