Tactual について
Tactualは、スクリーンリーダー利用者がページ上の操作要素を見つけ、到達し、操作するためのコストを分析します。開発者はローカルやCIで実行し、ナビゲーション経路の確認や結果比較、修正の優先順位付けを行えます。
主な機能
- 01スクリーンリーダーの操作コストを評価
- 02NVDA、JAWS、VoiceOverの読み上げをシミュレート
- 03キーボード操作で隠れたUIを探索
- 04操作要素までの経路を追跡
- 05分析結果を比較して修正案を提示
活用シーン
- 01ステージングサイトの移動上の障壁を調査
- 02メニューやダイアログのキーボード動作を確認
- 03変更前後の結果でアクセシビリティ修正を検証
MCP 対応
TactualはMCPサーバーを内蔵し、ページ分析、経路追跡、検証、プロファイル一覧、結果比較、修正案、多ページ調査のツールをMCPクライアントに提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Tactual
スクリーンリーダーのナビゲーションコスト分析ツールです。特定の支援技術プロファイル(NVDA、JAWS、VoiceOver)に基づき、スクリーンリーダー利用者がページ上のすべてのインタラクティブな対象を見つけ、移動し、操作するために必要なキーストローク数を測定します。
できること
既存のアクセシビリティツールは適合性を確認します。ARIA は正しいか、コントラスト比は十分か、といった点です。
Tactual はナビゲーションコストを測定します。スクリーンリーダー利用者がチェックアウトボタンにたどり着くには、何回操作する必要があるでしょうか。行き過ぎた場合はどうなるでしょうか。そのボタンの存在を見つけられるでしょうか。メニューは Enter キーで実際に開くでしょうか、それともクリックでしか開かないでしょうか。フォーカスは最初の menuitem に移動するでしょうか、それともトリガーにとどまったままでしょうか。
仕組み:
- Playwright のアクセシビリティスナップショットと、スクリーンリーダーの読み上げシミュレーションを取得します
- 必要に応じて、非表示の分岐(メニュー、ダイアログ、タブ、開閉パネル)を探索し、APG スタイルのウィジェット契約やフォームエラーのフローを含め、実際のキーボードイベントで検証します
- ランドマーク、見出し、Tab による順次移動などのエントリーポイントを含むナビゲーショングラフを構築し、すべての対象をスコアリングします
- 必要に応じて、予測した経路を
@guidepup/virtual-screen-readerで検証し、キャリブレーションを行います
Tactual は、自分のサイトやステージング環境を分析するための開発者ツールです。ローカル、CI、またはエディター内の MCP サーバー経由で実行できます。一般公開のスキャンサービスではありません。
位置付け
Tactual は、axe-core、Lighthouse、Pa11y などの適合性スキャナーを補完します。これらのツールは、幅広い WCAG および ARIA ルールをカバーするための最初の確認手段として、引き続き適切です。Tactual が対象とするのは、その次の問いです。ページのマークアップが正しい場合、AT 利用者が重要な対象を見つけ、移動し、操作するには、どれほどの負担がかかるでしょうか。
Tactual は、スクリーンリーダーのナビゲーションコストのトリアージ、経路の追跡、キーボード操作やウィジェットの実測エビデンス、変更前後の差分、CI での優先順位付け、エージェントがソースセレクターや修正候補を含む簡潔な検出結果を必要とする MCP ワークフローに活用できます。重要なユーザージャーニー、タイミングに左右されるフロー、ブラウザーや AT の設定、一般的な APG の例と意図的に異なる実装パターンの最終検証には、実際のスクリーンリーダーと手動テストを使用してください。
エージェント向けクイックパス: この README は、製品概要とリファレンスを兼ねています。エージェントはまず、タスクのパターンについては docs/AGENT-RECIPES.md を、MCP の完全なスキーマについては docs/MCP-TOOLS.md を確認し、その後、製品の背景、インストール手順、リリース関連の例を確認する場合にのみ、このページに戻ってください。
インストール
Node.js 20 以降が必要です。
npm install tactual
Tactual は Playwright を実行時依存関係としてインストールするため、Playwright を npx キャッシュに別途インストールしなくても、単発の npx tactual@latest ... コマンドを実行できます。MCP SDK も実行時依存関係として含まれているため、別途 SDK をインストールしなくても、インストール済みの tactual パッケージから tactual-mcp を実行できます。
クイックスタート
CLI
# Analyze a URL (default profile: generic-mobile-web-sr-v0)
npx tactual analyze-url https://example.com
# Analyze with a specific AT profile
npx tactual analyze-url https://example.com --profile voiceover-ios-v0
# Explore hidden UI (menus, tabs, dialogs, disclosures)
npx tactual analyze-url https://example.com --explore
# Use a scoring preset for your use case
npx tactual analyze-url https://shop.com --preset ecommerce-checkout
npx tactual analyze-url https://docs.example.com --preset docs-site
# Output as JSON, Markdown, or SARIF
npx tactual analyze-url https://example.com --format json --output report.json
npx tactual analyze-url https://example.com --format sarif --output report.sarif
# Compare two analysis runs
npx tactual diff-results baseline.json candidate.json
npx tactual diff-results baseline.json candidate.json --format json
# Print what NVDA would say as you Tab through the page
npx tactual transcript https://example.com
npx tactual transcript https://example.com --at voiceover
# List available AT profiles and scoring presets
npx tactual profiles
npx tactual presets
# Run benchmark suites
npx tactual benchmark
npx tactual benchmark --suite all
Benchmark fixtures ship with the npm package, so the benchmark command works from a fresh install and does not require cloning the repository fixtures into your current directory.
# Validate predicted paths against a virtual screen reader (reachability + step count)
# Requires optional deps, installed by default with tactual
npx tactual validate-url https://example.com --max-targets 10 --strategy semantic
# Initialize a tactual.json config file
npx tactual init
# Analyze a bot-protected site with stealth + real Chrome
npx tactual analyze-url https://www.npmjs.com/ --stealth --channel chrome
# Deep keyboard probing including revealed widgets and form-error flows
npx tactual analyze-url https://docs.example.com --probe --explore --probe-mode deep
# Focus probing on one opened branch, such as a dialog trigger
npx tactual analyze-url https://app.example.com/settings \
--probe \
--entry-selector "[aria-controls='profile-dialog']" \
--probe-strategy modal-return-focus
# Analyze + inline virtual-SR validation in one command (predicted vs validated steps)
npx tactual analyze-url https://example.com --validate --validate-max-targets 10
コンソール出力には、各検出結果について、スクリーンリーダー利用者がその対象にたどり着くまでの経路を示す簡潔な行が含まれます。
██████░░ 70 link:reference structural
D:47 R:71 O:100 Rec:100
getByRole('link', { name: 'Reference' })
↪ Tab ×2 "v19.2" → K "Learn" → Tab "Reference"
→ Target is not efficiently reachable via heading or landmark navigation
ここで、Tab = nextItem、H = nextHeading、; = nextLandmark、K = nextLink、B = nextButton、Enter = 有効化です。同じ操作が連続する場合はまとめて表示されます(Tab ×2)。
監査から修正へ
ローカルアプリやプレビュー環境のアクセシビリティ対応において、Tactual はレビューしやすい小規模な変更に役立つ根拠を提示します。
- 各指摘に含まれる
selector、penalties、suggestedFixes、根拠の概要 - 要約出力に含まれる、グループ化された
issueGroupsと修正候補 - ルート間で繰り返されるナビゲーションのコストを示す
analyze_pages.site.repeatedNavigation - 修正前後を検証する
diff-results/diff_results
まず広く問題を洗い出し、コードを変更する前に、1つのルートを詳しく調べます。
# Site-level triage. Redirect JSON for tool consumption.
npx tactual analyze-pages \
https://app.example.com/ \
https://app.example.com/docs \
https://app.example.com/settings \
--profile nvda-desktop-v0 \
--format json > tactual-site.json
# Deepen one route and produce a reviewable markdown report.
npx tactual analyze-url https://app.example.com/docs \
--profile nvda-desktop-v0 \
--explore --probe --probe-mode standard \
--format markdown --output tactual-report.md
# When one branch is the target, open it first and spend probe budget there.
npx tactual analyze-url https://app.example.com/docs \
--profile 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
# Save a baseline before editing, then verify the patch.
npx tactual analyze-url https://app.example.com/docs --explore --probe --format json --output baseline.json
# Edit one root cause in the local repo, rebuild/restart the preview, then re-run:
npx tactual analyze-url https://app.example.com/docs --explore --probe --format json --output candidate.json
npx tactual diff-results baseline.json candidate.json
候補セクションを、共有コンポーネント、ナビゲーションパターン、ウィジェットの契約など、繰り返し発生する根本原因を探る出発点として活用してください。該当するソースコンポーネントを確認し、プロジェクトで想定される課題や PR の形式に合わせて、ルート、コマンド、指摘の根拠、ユーザーへの影響、コードの変更、検証を含めてください。スコアの変化は補足的な根拠として役立ちますが、変更内容では、まずどのようなアクセシビリティ上の挙動が変わったのかを示してください。
MCP クライアントは同じ簡潔な出力を利用できるため、ルート、セレクター、根拠、ソースの変更、修正前後の検証に基づいたレビューのサイクルを維持できます。
ライブラリ API
import { analyze, getProfile } from "tactual";
import { captureState } from "tactual/playwright";
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
const state = await captureState(page);
await browser.close();
const profile = getProfile("generic-mobile-web-sr-v0");
const result = analyze([state], profile);
for (const finding of result.findings) {
console.log(finding.targetId, finding.scores.overall, finding.severity);
}
スクリーンリーダー読み上げシミュレーター — 各ターゲットについて、NVDA、JAWS、VoiceOver がどのように読み上げるかを、状態情報(チェック済み、展開済み、選択済み、モーダル、値、必須、無効など)とともに予測します。
import {
simulateScreenReader,
buildAnnouncement,
buildMultiATAnnouncement,
buildTranscript,
} from "tactual/playwright";
const report = await simulateScreenReader(page, state.targets);
for (const a of report.formFields) {
console.log(a.announcement);
// → "Subscribe, check box, checked"
// → "Country, combo box, collapsed"
// → "Email, edit, invalid entry, required, you must use a work address"
}
// Compare across screen readers
const tx = state.targets[5];
buildAnnouncement(tx, "nvda"); // → "Country, combo box, collapsed"
buildAnnouncement(tx, "voiceover"); // → "Country, popup button"
// All three at once
buildMultiATAnnouncement(tx);
// → { nvda: "...", jaws: "...", voiceover: "..." }
// Linear navigation transcript — what an SR user hears Tabbing through
const transcript = buildTranscript(state.targets, "nvda");
// → [{ step: 1, kind: "landmark", announcement: "Main, main landmark" }, ...]
// Multi-target navigation modes (linear, by-heading, by-landmark, by-form-control)
import { buildNavigationTranscript } from "tactual/playwright";
// Heading-only navigation (NVDA: H key)
const headings = buildNavigationTranscript(state.targets, { mode: "by-heading" });
// Navigate from one element to another
const path = buildNavigationTranscript(state.targets, {
from: "link:before-main",
to: "heading:welcome",
mode: "linear",
});
// Demoted landmarks (in DOM but stripped by HTML rules, e.g. <header> in <section>)
for (const d of report.demotedLandmarks) {
console.warn(d.demotionReason);
}
検証・キャリブレーション API — モデルの出力を、仮想スクリーンリーダーによる検証実行や人間による観察データセットと比較します。
import { validateFindingsInJsdom } from "tactual/validation";
import { runCalibration, formatCalibrationReport } from "tactual/calibration";
// Given a JSDOM instance, PageState, AnalysisResult, and calibration dataset:
const validation = await validateFindingsInJsdom(dom, state, result.findings, {
maxTargets: 10,
strategy: "semantic",
});
const calibration = runCalibration(dataset, new Map([[state.url, result]]));
console.log(validation, formatCalibrationReport(calibration));
または CLI から実行します。
npx tactual transcript https://example.com --at voiceover
npx tactual calibration-report my-calibration.json --analysis example-nvda.json
シミュレーターはヒューリスティックによる予測であり、実際のスクリーンリーダーの出力ではありません。シミュレーター自体は高速です(キャプチャ済みターゲットに対して JavaScript のみで動作し、ターゲットをメモリに読み込んだ後は 1 秒未満)。ただし、analyze-url の全工程ではブラウザーの起動、ページのキャプチャ、スコアリングが行われ、小規模なページでも数秒かかります。--probe を指定するとさらに時間がかかり(約 30 秒以上)、複雑な SPA で --explore を指定すると約 1~5 分かかります。デフォルトではヘッドレスブラウザーで分析するため、作業中にウィンドウは表示されません。(画面を表示する場合やボット対策のあるサイトでは、--no-headless または --channel chrome --stealth を使用してください。)
データ品質。 W3C ARIA-AT project のトークン単位のアサーションを基にキャリブレーションされています。3 種類すべての AT(NVDA、JAWS、VoiceOver)で、ロール/名前/状態トークンに関する 77 件のアサーションすべて(77/77)が 100% 成功しています。対象には、単一ターゲットのパターン 36 種(ボタン、トグルボタン、すべてのメニューボタンのバリエーション、開示、アコーディオン、チェックボックス/三状態チェックボックス、スイッチ、スライダー、ダイアログ、アラート、リンク、タブ、コンボボックス、ラジオグループ、スピンボタン、メニューバー)と、複数ターゲットのランドマークシナリオ 4 種が含まれます。最新のアップストリームアサーションに対する検証は、npm run build の後に npm run calibrate を実行してください。これはシミュレーターのキャリブレーションであり、ブラウズモード、読み上げの詳細度設定、タイミング、すべての有効なウィジェットのバリエーションにおいて、スクリーンリーダーの動作を完全に再現できることを示すものではありません。キャリブレーション対象外の AT 固有のオーバーライドには、ソース内で信頼度として HIGH/MEDIUM/LOW のラベルが付けられています。
MCP サーバー
Tactual には、AI エージェントが利用するための MCP サーバーが含まれています。
# Start the MCP server (stdio transport — default)
npx tactual-mcp
# Start with HTTP transport (for hosted platforms, remote clients)
npx tactual-mcp --http # listens on http://127.0.0.1:8787/mcp
npx tactual-mcp --http --port=3000 # custom port (or set PORT env var)
npx tactual-mcp --http --port 3000 # space-separated form is also supported
npx tactual-mcp --http --host=0.0.0.0 # bind to all interfaces (default: 127.0.0.1)
ネットワークに公開する MCP のデプロイでは、HTTP トランスポートを認証済み TLS プロキシの背後に配置し、信頼できるクライアントのみに範囲を限定してください。ホスティング時のチェックリストと脅威モデルについては、SECURITY.md を参照してください。
利用可能な MCP ツール:
| ツール | 説明 |
|---|---|
analyze_url | ページの SR ナビゲーションコストを分析します(デフォルトは SARIF)。オプトインの探索、キーボード/ウィジェット/フォームのプローブ、範囲指定/目標指向のプローブ、ボット対策サイト向けのステルス/チャネル、フィルタリングに対応しています。 |
trace_path | モデル化された SR の読み上げを含む、対象までのステップごとのナビゲーション経路を表示します。 |
validate_url | @guidepup/virtual-screen-reader に対して予測された経路を検証します。戦略(linear/semantic)ごとの到達可能率と平均精度を返します。予測と検証のサイクルを完結させます。 |
calibration_report | 観測済みのキャリブレーションデータセットを保存済みの完全な分析 JSON に適用し、調整/レビューのワークフロー向けに構造化されたスコアリング指標を返します。 |
list_profiles | 利用可能な AT プロファイルを一覧表示します。 |
diff_results | 2 つの分析結果を比較し、改善、リグレッション、重大度の変化を表示します。 |
suggest_remediations | 影響度順に修正案を提示します。 |
save_auth | 認証を行い、保護されたコンテンツの分析に使用するセッション状態を保存します。 |
analyze_pages | 複数ページにわたるサイトのトリアージを行い、集計統計とページ間で繰り返されるナビゲーションコストのグループを表示します。 |
パラメーターの完全なリファレンス:docs/MCP-TOOLS.md
AI ツールごとのセットアップ
まず、プロジェクトに必要なパッケージをインストールします。
npm install tactual
Claude Code — プロジェクトのルートにある .mcp.json に追加します。
{
"mcpServers": {
"tactual": {
"type": "stdio",
"command": "npx",
"args": ["tactual-mcp"]
}
}
}
GitHub Copilot — .copilot/mcp.json または ~/.copilot/mcp-config.json に追加します。
{
"mcpServers": {
"tactual": {
"type": "stdio",
"command": "npx",
"args": ["tactual-mcp"]
}
}
}
Cursor / Windsurf / Cline — エディターの MCP 設定に同じ形式で追加します。
{
"mcpServers": {
"tactual": {
"command": "npx",
"args": ["tactual-mcp"]
}
}
}
直接インストール(グローバルインストール) — npx を使用しない場合:
npm install -g tactual
tactual-mcp # starts the MCP server on stdio
GitHub Actions
GitHub Actions Marketplace の複合アクションを使用します。
jobs:
a11y:
runs-on: ubuntu-latest
permissions:
security-events: write # for SARIF upload
pull-requests: write # for comment-on-pr
steps:
- name: Analyze accessibility
uses: tactual-dev/[email protected]
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 Code Scanning にアップロードします。また、平均スコアがしきい値を下回るとビルドを失敗させます。プルリクエストに要約コメントを投稿するには、comment-on-pr: "true" を設定します(再実行時に更新されます)。後続のステップで使用する average-score と result-file の出力を提供します。アクションのバージョンは Tactual のバージョンに連動するため、パッチを適用するには uses: の行を更新してください。
デフォルト設定は保守的です。実際のキーボードイベントを送信するため、probe は有効化しない限りオフです。また、強制カラーのアイコンチェックは、nvda-desktop-v0 や jaws-desktop-v0 のような visualModes を宣言したプロファイルに対してのみ実行されます。
より細かく制御するには、CLI を直接使用する方法もあります。
- name: Install Tactual
run: npm install tactual
- name: Install browsers
run: npx playwright install chromium --with-deps
- name: Run accessibility analysis
run: npx tactual analyze-url https://your-app.com --format sarif --output results.sarif --threshold 70
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
リグレッションゲート(ベースラインより悪化した場合に CI を失敗させる)
--baseline と --fail-on-regression を組み合わせると、Tactual を厳格な CI ゲートとして使用できます。正常なビルドのベースラインを保存してから、変更によってベースラインより N 件以上の指摘が増えた場合に PR チェックを失敗させます。diff-results コマンドを個別に実行して、人が読みやすい変更前後のレポートを作成することもできます。
# One-time: snapshot main as the baseline
- name: Snapshot baseline
if: github.ref == 'refs/heads/main'
run: |
npx tactual analyze-url https://preview.your-app.com \
--format json --output tactual-baseline.json
- name: Upload baseline
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: tactual-baseline
path: tactual-baseline.json
# On PRs: compare against the baseline, fail on regressions
- name: Fetch baseline
uses: actions/download-artifact@v4
with:
name: tactual-baseline
- name: Analyze + gate on regressions
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 # fail if 3+ findings regressed
アクション経由で実行する方法もあります。
- uses: tactual-dev/[email protected]
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 は 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 | ケバブケースのフラグ | --probe-strategy modal-return-focus |
| MCP とライブラリのオプション | キャメルケースのフィールド | probeStrategy: "modal-return-focus" |
| GitHub Action | ケバブケースの入力 | probe-strategy: modal-return-focus |
設定
CLI フラグ
Options:
-p, --profile <id> AT profile (default: generic-mobile-web-sr-v0)
-f, --format <format> json | markdown | console | sarif (default: console)
-o, --output <path> Write to file instead of stdout
-d, --device <name> Playwright device emulation
-e, --explore Explore hidden branches
--explore-depth <n> Max exploration depth (default: 3)
--explore-budget <n> Max exploration actions (default: 50)
--explore-timeout <ms> Total exploration timeout; includes probe time when combined with --probe (default: 60000)
--explore-max-targets <n> Max accumulated targets before stopping (default: 2000)
--allow-action <patterns...> Allow exploring controls matching these patterns (overrides safety)
--exclude <patterns...> Exclude targets by name/role glob
--exclude-selector <css...> Exclude elements by CSS selector
--scope-selector <css...> Capture, score, and probe only these subtrees
--focus <landmarks...> Only analyze within these landmarks
--suppress <codes...> Suppress diagnostic codes
--top <n> Show only worst N findings
--min-severity <level> Minimum severity to report
--threshold <n> Exit non-zero if avg score < N
--preset <name> Scoring preset (ecommerce-checkout, docs-site, dashboard, form-heavy)
--config <path> Path to tactual.json
--no-headless Headed browser (for bot-blocked sites)
--channel <name> Browser channel: chrome, chrome-beta, msedge (uses installed browser; bypasses most bot detection)
--stealth Anti-detection defaults: realistic UA, override navigator.webdriver, spoof plugins/languages
--user-agent <ua> Override User-Agent string
--timeout <ms> Page load timeout (default: 30000)
--probe Opt-in runtime keyboard probes for interactive targets
(focus, activation, Escape, Tab).
Also probes menu, dialog, tab, disclosure, combobox/listbox,
and form-error patterns.
When combined with --explore, probes revealed-state targets too
(menu items, dialog bodies, expanded widgets).
--probe-budget <n> Override generic-probe budget (default: per --probe-mode)
--probe-mode <mode> fast | standard (default) | deep.
fast=5 generic/5 menu/3 modal/5 widget;
standard=20/20/10/20; deep=50/40/20/40.
Budget is shared across initial + all revealed states.
--probe-selector <css...> Probe only these subtrees without changing capture/scoring
--entry-selector <css> Activate this trigger before capture/probe
--goal-target <target> Exact-ish target id/name/role/kind/selector hint
--goal-pattern <pattern> Glob target id/name/role/kind/selector hint
--probe-strategy <strategy> all | overlay | composite-widget | form |
navigation | modal-return-focus | menu-pattern
--validate Run the virtual screen reader over the captured DOM and include
a predicted-vs-validated step comparison in the output.
Requires optional deps: jsdom + @guidepup/virtual-screen-reader.
Installed by default unless optional deps were omitted.
--validate-max-targets <n> Max findings to validate (default: 10)
--validate-strategy <mode> Virtual-SR nav strategy: linear | semantic (default: semantic)
--check-visibility Force per-icon contrast check across the profile's visualModes
--no-check-visibility Disable per-icon contrast check even if profile declares modes
--detect-routes Record SPA route changes during analysis
--descend-frames Include iframe accessibility targets; Chromium can recover many cross-origin OOPIFs via CDP
--auto-scroll Scroll before capture to surface lazy/infinite-scroll content
--dismiss-banners Best-effort dismissal of safe cookie/consent banners
--probe-hover Hover likely triggers to expose hover-only popup content
--walk-tab-order Record Tab traversal to detect focus-order/focus-trap issues
--diff-viewports Compare desktop and mobile captures for hidden content
--wait-for-selector <css> Wait for selector before capturing (for SPAs)
--wait-time <ms> Additional wait after page load
--storage-state <path> Playwright storageState JSON for authenticated pages
--also-json <path> Also write JSON to this path (single analysis run for CI)
--summary-only Return only summary stats, no individual findings
-q, --quiet Suppress info diagnostics
tactual.json
tactual init で作成するか、手動で作成します。
{
"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)のみです。タッチジェスチャー(シングルタップ、ダブルタップ、右スワイプ、3 本指スワイプ、ローターの回転など)はシミュレートしません。モバイルプロファイルでは、スコアの各評価軸に反映されるのは、プロファイルモデルによる予測コストであり、実測された挙動ではありません。モバイルのアクセシビリティを検証するには、実機でのテストが引き続き必要です。
表示モード。 nvda-desktop-v0 および jaws-desktop-v0 プロファイルでは、visualModes マトリックス(ライト/ダーク × 強制カラーのオン/オフ)を定義し、各組み合わせでアナライザーがアイコンごとのコントラストを取得できるようにします。モバイルプロファイルと汎用プロファイルではこれを省略しています。Windows のハイコントラストモードは、現実的なモバイル環境の考慮事項ではないためです。以下の視認性チェックを参照してください。
可視性チェック
アクティブなプロファイルで visualModes マトリックスが宣言されている場合、Tactual は初回キャプチャ後に各 (colorScheme, forcedColors) の組み合わせを再エミュレートし、アイコンごとの計算済みスタイルをサンプリングします。検出結果のビルダーは、各アイコンの計算済み fill と、最も近い透明でない祖先の background-color を比較し、コントラストが WCAG 1.4.11 の非テキスト要素のしきい値(3:1)を下回るとペナルティを出力します。
4 種類のペナルティ文言、3 段階のスコアリング:
| ペナルティ | 発生条件 | 操作性への影響 |
|---|---|---|
Icon invisible in <mode> | コントラスト < 1.5:1、隣接するテキストラベルなし | 操作性は最大 60 |
Decorative icon invisible in <mode> | コントラスト < 1.5:1、コントロールに表示されるテキストラベルあり | 操作性 −5 |
Low icon contrast in <mode> | コントラスト 1.5–3.0:1、隣接するテキストラベルなし | 操作性 −5 |
Author-set SVG fill in <mode> | Playwright ではコントラストに問題がない(≥3:1)が、モードが forced-colors: active で、fill が作成者指定の CSS リテラル(システムカラーでも currentColor でもない) | 操作性 −2 |
このチェックでは、すでに HCM に対応しているアイコンはスキップします。該当するのは、fill="currentColor"、fill: ButtonText(または任意のシステムカラー)、forced-color-adjust: none(作成者によるオプトアウト)、または計算済みの fill === color(CSS によって適用された currentColor)です。表示されるテキストラベルの隣にある低コントラストのアイコンは、完全に除外されます。ラベルがコントロールを識別し、アイコンは補助的な役割を果たすためです。
置換リスクの段階がある理由。 ユーザーごとの HCM テーマでは、Canvas/ButtonText/システムカラーの値が異なります。作成者指定のリテラルの fill(例:svg { fill: #e4e6e6 })は、Chromium のデフォルトの HCM パレットでは十分なコントラストが得られても、特定のユーザーテーマではコントラストが低くなることがあります。forced-color-adjust: preserve-parent-color(SVG パスのデフォルト)の場合、リテラル自体のブラウザー描画は Playwright、Chrome、Edge で一貫しています。具体的な懸念は、OS による非表示の描画置換ではなく、テーマのばらつきです。Tactual がこのパターンを検出するのは、Playwright のコントラスト測定が誤解を招くためではなく、代表的な HCM テーマを使って実際の Edge で確認する必要があることを知らせるためです。
--no-check-visibility、tactual.json の checkVisibility: false、または MCP の analyze_url ツールの checkVisibility: false で明示的に無効にできます。プロファイルでモードが宣言されていない場合でも、--check-visibility で強制的に有効にできます(モードがなければ何も実行されません)。
このチェックによる追加時間は、ページごとに宣言されたモードあたりおよそ +50–200ms です。メディアの再エミュレートは低コストで、モードごとに新しいブラウザーコンテキストが作成されることはありません。
スコアリングプリセット
プリセットには、一般的なユースケース向けのフォーカスフィルターと優先度マッピングがまとめられています。設定ファイルや CLI フラグよりも下位に適用されます(プリセット → tactual.json → CLI フラグ)。
| プリセット | ユースケース | フォーカス | 重要な対象 |
|---|---|---|---|
ecommerce-checkout | ショッピングフロー | main | checkout, cart, payment, buy |
docs-site | ドキュメント | main, navigation | search, nav |
dashboard | ウェブアプリ | main, navigation | save, submit, create, delete, search |
form-heavy | フォームページ | main | submit, save, next, continue, error |
npx tactual analyze-url https://shop.com --preset ecommerce-checkout
npx tactual presets # list all presets with details
プリセットでは、デフォルトで Cookie バナーとアナリティクスの対象が除外されます。上書きするには、--exclude を使うか、tactual.json で priority を設定してください。プリセットを組み合わせることはできません。有効にできる --preset は一度に 1 つだけです。
スコアリング
各対象には、5 次元のスコアベクトルが付与されます。
| 次元 | 測定内容 |
|---|---|
| 発見可能性 | ユーザーは対象が存在することを認識できますか? |
| 到達可能性 | そこへ到達するためのナビゲーションコストはどの程度ですか? |
| 操作性 | コントロールは予測どおりに動作しますか? |
| 復帰性 | 行き過ぎた場合、元の状態に戻るのはどの程度困難ですか? |
| 相互運用性リスク | AT/ブラウザーのサポートに差が生じる可能性はどの程度ですか?(ペナルティ) |
次元の重みはプロファイルごとに異なります。
| プロファイル | D | R | O | Rec | costSensitivity |
|---|---|---|---|---|---|
| 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。対数の計算で log(0) を避けるため、各次元の値は 1 を下限とします。いずれかの次元が 0 になると、その次元の寄与が幾何平均から除外され、全体スコアが大幅に低下します。到達できないものは操作できません。
重大度の区分:
| スコア | 区分 | 意味 |
|---|---|---|
| 90-100 | 良好 | 懸念は小さい |
| 75-89 | 許容可能 | 改善の余地がある |
| 60-74 | 中程度 | 優先度を付けて対処すべき |
| 40-59 | 高 | 影響のある操作上の障壁が生じる可能性が高い |
| 0-39 | 深刻 | 利用を妨げる可能性が高い |
診断
Tactual は、キャプチャの信頼性、ページ構造、視覚的なアクセス、実行時のエビデンス、 ARIA の妥当性、繰り返し発生するコストパターンに関する診断を出力します。警告は確認を促すものであり、 自動的に適合性の不合格を示すものではありません。視覚やコンテンツに関するチェックの多くは ヒューリスティックであり、不具合として報告する前に、状況に応じて確認してください。
| コード | レベル | 意味 |
|---|---|---|
blocked-by-bot-protection | error | ボット対策/チャレンジページが検出されました。取得したコンテンツは目的のページではありません。 |
empty-page | error | 対象がまったく見つかりませんでした。 |
ok | info | 信頼性に関する警告なしで、対象セットを取得できました。 |
possibly-degraded-content | warning | HTTP ページの対象が不自然に少なくなっています。 |
sparse-content | warning | 見つかった対象は 1~4 件のみです。 |
possible-login-wall | warning | 認証が必要なコンテンツ、またはログインへのリダイレクトが疑われます。 |
possible-cookie-wall | info | Cookie の同意画面がコンテンツを覆っている可能性があります。 |
redirect-detected | info/warning | 取得先が別の URL またはドメインでした。 |
timeout-during-render | warning | 要求されたレンダリング待機が、取得前に完了しませんでした。 |
framework-detected | info | 取得中にフロントエンドフレームワークのシグナルが検出されました。 |
spa-route-changes | info | 分析中に SPA のルート変更が発生しました。 |
exploration-no-new-states | warning | --explore は実行されましたが、新たな状態は見つかりませんでした。 |
frames-descended | info | iframe の子フレームへの移動により、子フレームを取得またはスキップしました。 |
auto-scrolled | info | 取得前に自動スクロールを実行し、表示された内容を報告します。 |
banners-dismissed | info | Cookie/同意バナーの非表示を試みました。 |
tab-order-walked | info/warning | Tab 順の走査でフォーカス位置を記録しました。正の tabindex がある場合は警告します。 |
viewport-divergence | warning | デスクトップ/モバイルのビューポート比較で、対象、ランドマーク、または見出しの不足が見つかりました。 |
no-headings | warning | 見出し要素が見つかりませんでした。 |
heading-skip | warning | 見出し階層でレベルが飛んでいます(例:h1 -> h3)。 |
empty-heading | warning | 見出しはありますが、テキストがありません。 |
numeric-heading | warning | 見出しテキストが数字、句読点、または意味の乏しい単一文字のみです。 |
h1-count | info/warning | ページに適切な H1 が 1 つもないか、確認が望ましい H1 が複数あります。 |
no-landmarks | warning | ランドマーク領域が見つかりませんでした。 |
no-main-landmark | warning | <main> ランドマークがありません。 |
no-banner-landmark | info | <header>/バナーランドマークがありません。 |
no-contentinfo-landmark | info | <footer>/コンテンツ情報ランドマークがありません。 |
no-nav-landmark | info | <nav>/ナビゲーションランドマークがありません。 |
landmark-demoted | warning | HTML ランドマークはありますが、入れ子のコンテキストによってその役割が格下げされています。 |
structural-summary | info | 構造の概要を 1 行で表示します。 |
no-skip-link | warning | 対象が 5 件以上あるページに、コンテンツへスキップするリンクがありません。 |
broken-skip-link | warning | スキップ用リンクが、存在しないフラグメントの対象を指しています。 |
skip-link-not-first | warning | スキップリンクはありますが、Tab 移動の最初の 2 か所でフォーカスできません。 |
visual-order-divergence | warning | 視覚的な順序が DOM/スクリーンリーダーのナビゲーション順序と異なるようです。 |
shared-structural-issue | warning | 対象の 50% 超に影響する減点要因が、ページレベルに引き上げられました。 |
redundant-tab-stops | warning | 複数のリンク対象によって、同じ宛先への Tab 停止位置が繰り返し発生しています。 |
data-flow-dependencies | info | 探索した状態から、先行する操作の後にのみ有効になるコントロールが見つかりました。 |
form-summary | info/warning | フォームを要約し、送信コントロールがないように見える場合は警告します。 |
missing-autocomplete | warning | 標準のフォームフィールドに有用な autocomplete トークンがないか、無効にされています。 |
empty-interactive | warning | インタラクティブな対象にアクセシブルな名前がありません。 |
fake-interactive-elements | warning | クリック可能な非セマンティック要素に、キーボード/スクリーンリーダーからアクセスできません。 |
cdp-click-listeners | warning | CDP により、非インタラクティブ要素上のクリックに類するリスナーが検出されました。 |
ambiguous-link-names | warning | 同じアクセシブルな名前を持つリンクが、異なる宛先を指しています。 |
media-without-controls | warning | 音声/動画にコントロールがなく、非表示にもなっていません。 |
duplicate-id | warning | id の値が重複しており、ラベルや ARIA 参照が機能しなくなる可能性があります。 |
nested-interactive | warning | インタラクティブなコントロールが、別のインタラクティブなコントロールの内側にあります。 |
meta-refresh | warning | meta refresh によってページが自動更新またはリダイレクトされます。 |
missing-image-alt | warning | 画像に alt 属性がありません。 |
suspicious-image-alt | warning | 画像の代替テキストが、埋め草またはファイル名のようなプレースホルダーに見えます。 |
missing-iframe-title | warning | iframe に title またはアクセシブルなラベルがありません。 |
missing-html-lang | warning | <html lang> がないか、BCP 47 の言語タグに見えない値です。 |
poor-document-title | warning | ドキュメントタイトルがない、空、短すぎる、または一般的すぎます。 |
viewport-blocks-zoom | warning | viewport のメタ設定によって、ユーザーのズームが制限されています。 |
low-contrast-text | warning | インタラクティブなテキストまたは見出しが、WCAG 相当のテキストコントラスト基準を満たしていません。 |
color-only-conveyance | warning | テキストが意味を伝えるために色だけに依存しているようです。 |
color-blindness-contrast-fail | warning | 色覚特性のシミュレーション下で、テキストのコントラストが失われます。 |
lang-switch-without-marker | warning | lang マーカーなしで、テキストの言語が変化しているようです。 |
invalid-aria-role | warning | 標準外の ARIA ロールがあります。 |
unknown-aria-attr | warning | 不明な aria-* 属性があります。 |
invalid-aria-attr-value | warning | ARIA 属性の値が、許可されている値の範囲外です。 |
missing-required-aria-attr | warning | ARIA ロールに必須の状態またはプロパティがありません。 |
aria-naming-prohibited | warning | 名前付けが禁止されているロールに名前が設定されています。 |
unsupported-aria-attr-for-role | warning | 要素のロールでサポートされていない ARIA 属性です。 |
探索
--explore フラグを有効にすると、範囲を限定した分岐探索が行われます。
- メニュー、タブ、開示パネル、アコーディオン、ダイアログを開きます
- 非表示の UI から新しいアクセシビリティ状態を取得します
- 発見したターゲットに
requiresBranchOpenのマークを付けます - 深度、アクション数、ターゲット数、新規性の予算を遵守します
- 安全なアクションのポリシーにより、破壊的な操作をブロックします
探索は、非表示の UI が多いページ(ドロップダウンメニュー、タブ付きインターフェース、モーダルダイアログなど)で役立ちます。
探索候補は反復処理の前に安定したキー(ロール+名前)で並べ替えられるため、同じページコンテンツであれば、実行ごとに同じ順序で探索されます。
プローブ
--probe フラグは、重要なインタラクティブパターンがアクセシビリティツリーに現れた後、それらが機能するかどうかを測定します。実際のキーボードイベントを送信し、実行時間も増加させるため、プローブはオプトインです。0.4.0 以降では、一般的なフォーカス/アクティベーションのチェック、メニューのコントラクト、モーダルダイアログのコントラクト、トリガーからダイアログへのフロー、タブ、開示パネル、コンボボックス、リストボックス、必須フィールドのエラーフローが含まれます。プローブの検出結果には根拠の要約が含まれるため、レポートでは、測定された失敗と、モデル化またはヒューリスティックによるスコアリングとを区別できます。
目的指向の制御により、複雑な SPA でも詳細なプローブを有効に活用できます。
| 必要な操作 | CLI | MCP/アクションフィールド | 効果 |
|---|---|---|---|
| 1 つのサブツリーを分析 | --scope-selector "#drawer" | scopeSelector / scope-selector | 選択したサブツリーのみを取得、スコアリング、プローブします。 |
| 1 つのサブツリーをプローブ | --probe-selector "#drawer" | probeSelector / probe-selector | ページ全体のスコアリングは維持しながら、選択したサブツリー内のみでプローブの予算を使用します。 |
| 先に 1 つの分岐を開く | --entry-selector "[aria-controls='menu']" | entrySelector / entry-selector | 取得/プローブの前にトリガーをアクティブにし、新たに表示されたターゲットを優先します。 |
| 既知のターゲットを指定 | --goal-target "checkout" | goalTarget / goal-target | 一致するターゲット ID、名前、ロール、種類、セレクターにプローブを絞り込みます。 |
| glob で指定 | --goal-pattern "*dialog*" | goalPattern / goal-pattern | glob マッチングを使用する点を除き、目的のターゲットと同じです。 |
| 意図に応じて予算を使用 | --probe-strategy modal-return-focus | probeStrategy / probe-strategy | all、overlay、composite-widget、form、navigation、modal-return-focus、menu-pattern に関連するプローブの種類を実行します。 |
たとえば、無関係なメニューを巡回せずにモーダルの分岐を評価するには、次のようにします。
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(デフォルト) | メニューを1階層開いた状態をキャプチャします |
| 複雑なアプリ(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 を多用するアプリでは、次のオプトイン型キャプチャヘルパーが便利です。
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は、フレームの URL 情報を付けて iframe のターゲットを追加します。同一オリジンのフレームでは Playwright のフレームスコープ付きアクセシビリティスナップショットを使用します。通常のスナップショット取得経路でアクセスできないクロスオリジンの OOPIF では、Chromium は CDP にフォールバックします。Firefox/WebKit では、アクセスできないフレームを従来どおりスキップします。--diff-viewportsは、デスクトップとモバイルのビューポート間で消えるターゲット、ランドマーク、見出しコンテンツを検出します。--dismiss-banners、--probe-hover、--walk-tab-orderは、よくある SPA のオーバーレイやフォーカス順序の不具合に関する、対象を絞った実行時の証拠を追加します。
既知のページのベンチマーク
複雑な一般公開の SPA/コンポーネントライブラリのページを対象にリリースの証拠を取得するには、次を実行します。
npm run benchmark:known-pages
このスクリプトはパッケージをビルドし、SPA ヘルパースタックを有効にして analyze-url を実行し、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 のように、スクリプトを直接呼び出します。
回帰の追跡
2 つの分析実行を比較して、回帰を検出します。
# Save a baseline
npx tactual analyze-url https://your-app.com --format json --output baseline.json
# After changes, run again and diff
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 と ARIA-AT project に由来する、ARIA ロール/属性のサポートデータの静的スナップショットが含まれています。既知の AT/ブラウザー間のサポート上のギャップがあるロールには、相互運用性リスクのペナルティが適用されます。
| ロール | リスク | 備考 |
|---|---|---|
button、link、heading | 0 | 十分にサポートされています |
dialog | 5 | フォーカス管理にばらつきがあります |
combobox | 8 | 相互運用性の問題が最も多いパターン |
tree | 10 | JAWS 以外ではサポートが不十分です |
application | 15 | 誤用すると危険です |
検出結果の解釈
Tactual の検出結果には、意図的に複数の証拠領域が含まれています。
- SR ナビゲーション:ランドマーク、見出し、ラベル、分岐の発見、順次移動にかかるコスト、モデル化されたアナウンス。
- キーボード操作性:フォーカスの移動、アクティベーション、Escape による復帰、Tab のトラップ、実行時のウィジェットプローブ。
- 構造上のセマンティクス:名前の欠落、見出し/ランドマークの構造、降格されたランドマーク、繰り返し発生する共通要因。
- 相互運用性のリスク:既知の AT/ブラウザー間のサポート上のギャップがあるロールと状態。
- ポインター関連のチェック:スクリーンリーダーのナビゲーションモデル外のユーザーにも影響する可能性がある、ターゲットサイズやアイコンの視認性に関する問題。
そのため、スクリーンリーダーのナビゲーションスコアが高いページでも、スキップリンク、ターゲットサイズ、視認性に関する警告が出ることがあります。これらは矛盾ではなく、別々の修正カテゴリとして扱ってください。
プローブから導出された APG の検出結果は、一貫性を測定した警告です。多くのウィジェット、特にコンボボックスやディスクロージャーに似たパターンには、有効な実装バリエーションがあります。そのため、警告を必須の置き換えと見なす前に、意図したパターンに照らして確認してください。重要なフローは、対象のブラウザー/AT の組み合わせでも確認してください。
出力形式の推奨事項
| 形式 | 一般的なサイズ | 最適な用途 |
|---|---|---|
console | ~8KB | ターミナルでの人によるレビュー |
markdown | ~11KB | PR と Issue のコメント |
json | ~18KB | プログラムによる処理 |
sarif | ~4-40KB | GitHub Code Scanning / CI |
SARIF 以外のすべてのレポーター形式では、デフォルトで要約された出力が生成されます。内容は統計情報、グループ化された問題、修正候補、エビデンスの要約、重大度の高い検出結果(最大 15 件)です。SARIF の結果は最大 25 件です。出力が切り詰められると、先頭に注記が表示されます。ライブラリ API は完全な AnalysisResult を公開しますが、CLI と MCP のレポーター出力は、includeStates などの特定のフィールドが要求されない限り、意図的に簡潔にまとめられています。
MCP で使用する場合、デフォルトかつ推奨の形式は sarif です。統計情報、重大度別の件数、診断情報、上位 3 件の問題を含む簡潔なヘルスチェックには、summaryOnly: true を使用してください。
キャリブレーション
Tactual には、正解データセットに照らしてスコアリングパラメーターを調整するキャリブレーションフレームワーク(src/calibration/、tactual/calibration としてエクスポート)が含まれています。詳細については、docs/CALIBRATION.md を参照してください。
キャリブレーションの観測データには、OSS レビュー作業から得られる決定論的な読み上げフィードバックも含められます。テストした出力がわかっている場合は observedAnnouncement を記録し、正確な言い回しにばらつきがあっても役割/名前/状態のトークンが明確な場合は observedAnnouncementTokens を記録してください。Tactual はこれらを、マッチした対象についてモデル化した読み上げと比較し、不足しているトークンや予期しないトークンを報告します。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 build の後に npm run calibration:matrix を実行すると、MAE、バイアス、分散、古くなったシーケンス計画のドリフトに基づいて、到達可能性の調整作業を順位付けできます。
リリース準備状況と既知の制約については、docs/RELEASE_TEST_MATRIX.md、docs/LIMITATIONS.md、docs/NVDA_VM_OBSERVER.md に記載されています。
開発
npm install # Install dependencies
npm run build # Build with tsup
npm run test # Run unit + integration tests
npm run test:shard -- --list # List bounded Vitest release shards
npm run test:shard -- capture # Run one bounded Vitest shard
npm run test:shards # Run all bounded Vitest shards
npm run test:benchmark # Run benchmark suites
npm run typecheck # TypeScript type checking
npm run lint # ESLint
npm run test:release # Full split release gate
セキュリティ
ブラウザーのサンドボックス化
Tactual は常に、デフォルトの Chromium サンドボックスを有効にして Playwright を実行します。ウェブセキュリティを無効にしたり、ブラウザーのセキュリティモデルを変更したりすることはありません。すべてのページ操作は、標準の Chromium プロセスサンドボックス内で行われます。
安全な操作ポリシー
探索が有効(--explore)な場合、Tactual はインタラクティブ要素を有効化する前に、次の 3 つの階層に分類します。
| 階層 | 操作 | 例 |
|---|---|---|
| 安全 | 有効化します | タブ、メニュー項目、開示コントロール、アコーディオン、同一ページ内のアンカー |
| 注意 | 注意して有効化します | 外部リンク、曖昧なボタン |
| 危険 | スキップします | 検索フォーム以外の送信ボタン、削除、サインアウト、購入、デプロイ、登録解除 |
これはキーワードベースのヒューリスティックであり、意味上の偽装(たとえば、実際にはデータを削除する「保存」ボタン)を検出したり、サーバー側の動作を調べたりすることはできません。本番環境で使用する場合は、信頼できる環境またはサンドボックス化された環境で必ず探索を実行してください。
URL の検証
すべての URL は、移動前に検証されます。CLI ではローカルのフィクスチャを使用できるように、http:、https:、file: スキームを受け付けます。MCP の URL を受け取るツールでは、エージェントが制御するブラウザーでローカルファイルが公開されるのを防ぐため、http: と https: のみを受け付けます。javascript:、data:、blob:、vbscript: は拒否されます。認証情報が埋め込まれた URL(例:https://user:pass@host/)も拒否されます。プライベート/内部 IP 範囲はフィルタリングされません。内部サービスにアクセスできる環境で Tactual を実行することは、Playwright を利用する他のツールがそれらにアクセスできる状態にするのと同じです。そのため、URL の入力は信頼できる入力として扱ってください。
ライセンス
Apache-2.0
帰属表示
シミュレーターのロール/状態の表現は、CC-BY 4.0 のライセンスに基づく W3C ARIA-AT プロジェクトを基準に調整されています。Tactual に ARIA-AT のデータは同梱されていません。調整スクリプト(npm run calibrate)は、実行時に上流リポジトリからアサーションを取得します。Tactual の調整結果を公開する場合は、正解となるアサーションの出典として W3C ARIA-AT プロジェクトを明記してください。
相互運用性リスクのスコアリングで参照される ARIA のロール/属性のサポートデータは、a11ysupport.io および同じ ARIA-AT プロジェクトから得られています。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。