よくある質問
「core 1.14にアップグレードしたら、結果が変わった」
1.14は、意図してcantTellを多く報告し、主張することを減らしています。パイプラインに表れやすい変更は次のとおりです。
- WCAGの集約は
passになりません。達成基準がpassになるのは、その達成基準が求めることをすべてルールがチェックする場合だけで、完全にチェックできるものとして挙げられた達成基準はまだありません。そのため、ルールがすべてpassの達成基準はcantTell(composite.rollup.cantTell.partialCheck)になります。ルールが何も見つけなかった達成基準がnotApplicableになるのは2.4.3、2.4.4、2.4.7、2.4.9、2.5.3、4.1.1だけで、それ以外はcantTell(composite.rollup.cantTell.partialCoverage)です。不合格のルールがあれば、その達成基準は引き続き不合格になります。満たされた達成基準を数えるダッシュボードやゲートには、1つも表示されません。後述の項目とWCAG適合性との対応を参照してください。 - 2つのルールが確認を求める範囲が広がります。
page-title-patternsは、汎用的なタイトルやテンプレートのタイトルだけでなく、すべてのページタイトルについて確認を求めます。タイトルがページを説明しているかどうかは人が判断することだからです(G88)。duplicate-idは、WCAG 2.0と2.1でも4.1.1について確認を求めます。2023年の正誤表が4.1.1を満たされているものとみなしているからです。ただし、en301549-v3.2.1のようにkeepsParsing: trueを宣言したプロファイルは例外です。 - 11の新しいルールが追加され、その中には3.3.1(
invalid-field-error-message)、4.1.3(live-region-announcements)、1.4.5(image-of-text)の最初のルールがあります。ほとんどは確認を求めるものですが、click-handler-keyboard-operable、focus-not-removed-by-script、aria-owns-validは不合格を報告することがあります。属性間の制約(aria-valuemaxを上回るaria-valueminなど)をチェックするようになったaria-valid-attr-valueも同様です。ルールカタログを参照してください。 - 一部の不合格は確認に変わり、新しい不合格も出ます。
td-has-headerは、見出しセルのない大きな表について、すべてのセルを不合格にする代わりに一度だけ確認を求めます。form-control-single-labelは、ラベルが2つあるフィールドについて確認を求めます。コントラストは、ページが描画するもののより多くの部分で計測されるようになった(各配色でのページのキャンバス上のテキスト、::first-line、font-size-adjust、ページが色を選んだプレースホルダー)ため、新たにコントラストの不合格が出ることがあります。 - 一部の検出結果に新しい理由コードが付きます。理由コードを付けていなかった17のルールが付けるようになり、いくつかのコードは原因ごとに分割されました。1.13で書いたベースラインは1.15まで引き続き一致します。
migrateBaselineEntriesで書き換えてください(後述の項目を参照)。そうした検出結果のCode Scanningのアラートは、いったん閉じて新しいものとして再び表示されます。 - すべての
cantTellに、答え方が示されます。coreのルールの検出結果にはhowToCheckの手順が付き、一部にはページの手がかりがどちらを示しているかを表すleanが付きます。判定結果はcantTellのままで、指定しない限り(leanFailAs)leanでビルドが止まることはありません。HTMLレポートは、これらの検出結果を、不適合の可能性が高いもの、適合の可能性が高いもの、人による確認が必要なものに分けて表示します。出力スキーマを参照してください。 - 1.14.1では、テキストの間隔による不合格が減ります。
text-spacing-content-lossは、ページが失っていないテキストを不合格にしなくなりました。同意バナーの裏でよくあるようにbody { position: fixed }でスクロールを固定したページと、ビューポートより高い固定ボックスは、VIEWPORT_SCROLL_LOCKEDとして確認を求めます。Angular Materialのフィールドのフローティングラベルのように、ページのスクリプトがテキストに合わせて大きさを決めるボックスで切れたテキストは、TEXT_CLIPPED_SCRIPT_SIZEDとして確認を求めます。それらのベースラインの項目やCode Scanningのアラートは、修正されたバグと同じように閉じます。また、36の質問の手順は、不合格につながる前に、ページ上の別の方法で同じことができるかどうかも確認するようになりました。
変更の全一覧は、coreのCHANGELOG.mdにあります。
「runOnlyがエラーを投げた:no rule or tag named …、またはeither rule ids or tags」
1.10.0以降、配列そのものや文字列として渡したrunOnlyは、ルールIDかタグとして読まれます(ENGINE_OPTIONS.mdを参照してください)。次の2つの場合はエラーを投げます。
- どのルールもタグも指していない値。たいていはタイプミスです(
runOnly: ['img-alt-presnt'])。IDをgetChecksCatalog()と照らし合わせてください。大文字小文字は原因になりません。ルールIDとタグは大文字小文字を問わず照合されるので、IMG-ALT-PRESENTはimg-alt-presentを選択します。 - 1つの配列にルールIDとタグが混在している場合(
runOnly: ['img-alt-present', 'wcag2a'])。組み合わせるにはオブジェクト形式を使います。
runOnly: {
includeRuleIds: ['img-alt-present'],
tags: ['wcag2a'],
includeMode: 'or',
}
1.10.0より前は、配列そのものを渡すと無視され、すべてのルールが実行されていました。
オブジェクト形式({ includeRuleIds }、{ tags }、{ type: 'tag', values })や、engineOptions.rules.include/.tags.includeも、リスト内のどの値もルールやタグを指していない場合は、code: 'INVALID_RUN_ONLY'付きでエラーを投げます(runOnly.tags: no tag named "wcag2.2aa".)。以前はルールが1つも実行されず、問題のない合格のように見えていました。既知の値と並んだ未知の値が1つだけの場合や、除外リスト内の未知の値は、console.warnを出力するだけです。
デフォルトのincludeMode: 'and'のもとでは、タグとルールIDの両方を含む選択は、両方に一致するルールだけを実行します。regionはwcag2aタグを持たないベストプラクティスなので、.withTags('wcag2a').withRules('region')は何も実行せず、ルールを1つも実行しないスキャンには不合格がありません。エンジンはページのコンソールに警告を出しますが、テストの実行ではそれは表示されません。INVALID_RUN_ONLYを投げるのはstrictOptionsのもとでだけです。どちらかに一致するものを実行するには、そう指定します。バインディングでは.includeMode('or')、runOnlyではincludeMode: 'or'(上記)、CLIでは--include-mode orです。バージョン1.3.0以降のCLIは、2つに共通するルールがない場合、そのヒントを示して終了コード2で終了します。
何も選ばない選択を実行前に見つけるには、ビルダーのplannedChecks()を確認してください。スキャンが実行するルールを、ページなしで求めます(Cypressではchainableを返します)。
const builder = new A11yCoreBuilder({ page })
.withTags('wcag2a')
.withRules('region')
.includeMode('or'); // the wcag2a rules, plus region
if (builder.plannedChecks().length === 0) {
throw new Error('This selection runs no rule.');
}
選択の各部分がどう組み合わさるかは、エンジンオプションを参照してください。
「runOnly.tags: no rules for WCAG 2.2 Level A ("wcag22a")」
WCAGのバージョンとレベルを表す9つのタグ(wcag2aからwcag22aaaまで)は常に既知ですが、そのうち3つにはルールがありません。wcag21aaa、wcag22a、wcag22aaaで、これらの達成基準には自動チェックがないためです。ほかのタグと並べてこれらを指定すると、console.infoでこの行が出力されます。リストの残りは実行され、これらの達成基準には手動確認が必要です。出力しないようにするにはengineOptions.logUntestedWcag: falseを渡すか、タグを指定しない目標による選択を使ってください。runOnly: { wcag: { version: '2.2', level: 'AA' } }です(WCAGの目標で選ぶを参照)。このようなタグだけのリストでは何も実行されないため、同じ説明とともにINVALID_RUN_ONLYを投げます。
「runOnly.wcag.version must be …」または「runOnly.bestPractices must be true or false …」
runOnly.wcagは{ version, level }を受け取ります。versionは'2.0'、'2.1'、'2.2'のいずれか(文字列です。数値の2.2は拒否されます)、levelは'A'、'AA'、'AAA'のいずれかです。runOnly.bestPracticesはtrueかfalseを受け取ります。それ以外の値は、意図しない選択で実行するのではなく、フィールドと値を示してINVALID_RUN_ONLYを投げます。
「engineOptions: unknown option "lcoale" (did you mean "locale"?)」、またはスキャンがINVALID_ENGINE_OPTIONSを投げた
既知のオプションと1文字か2文字だけ違う(大文字小文字は問いません)オプションのキーは、警告されたうえで無視されます。スキャンはそれがないものとして実行されるので、綴りを直してください。それ以外の未知のキーには何も出力されません。カスタムルールはctx.engineOptionsから独自の設定を読めるためです。engineOptions.strictOptions: trueを指定すると、エンジンが読まないキー(入れ子のものを含む)や、型が誤っているか集合の外にある値に対して、スキャンは代わりにcode: 'INVALID_ENGINE_OPTIONS'付きでエラーを投げ、それぞれを最も近い既知のキーとともに示します。エラーのproblemsがそれらを列挙します。オプションの誤りを見つけるを参照してください。
「カスタムルールは不合格なのに、WCAGの集約は不合格にならない」
カスタムルールは、対応する各達成基準のWCAGの集約(rulesResults)に算入されます。meta.wcagSc: ['1.1.1']か、meta.normativeMappingsのWCAGのエントリーを指定してください。どちらもなければ、どの集約にも入りません。実行されなかったカスタムルール(skippedCustomRulesに列挙されます)も算入されません。組み込みルールの上書きは、それ自身の対応付けが示す場所にだけ算入されます。そのため、対応付けを宣言しない上書きは、組み込みルールが算入されていた集約から外れ、警告(customRules: "…" overrides a built-in rule but maps to none of the criteria of …)がそれらを示します。そこに残すには、組み込みルールの達成基準を指定してください。
「範囲を絞ったスキャンですべてがnotApplicableになる」、または「contextSelectorがエラーを投げた:… is not a valid CSS selector」
1.10.0以降、どの要素にも一致しないcontextSelectorは何もスキャンせず、ブラウザーが解析できないものはエラーを投げます(ENGINE_OPTIONS.mdを参照してください)。
- すべてが
notApplicableになる:result.contextMatchを確認してください。elementCount: 0はセレクターが何にも一致せず、何もスキャンされなかったことを意味します。これは問題のない結果ではありません。unmatchedSelectorsにそのセレクターが示されます。タイプミスがないか、またスキャンの時点でその要素がもうページ上にあるか(読み込み後にスクリプトが描画するコンテンツなど)を確認してください。 code: 'INVALID_CONTEXT_SELECTOR'付きでスキャンがエラーを投げた:メッセージと、エラーのselectorに示されたセレクターが、有効なCSSではありません。たとえば'#main['や>>>結合子です。あるいは、範囲が要素や{ include, exclude }オブジェクトのように、そもそもセレクターではない場合で、メッセージは渡されたものを示します。その要素に一致するセレクターを渡し、除外はengineOptions.excludeSelectorsで指定してください。
以前は、これらの場合はどれも代わりにページ全体をスキャンしていました。何にも一致しないセレクターや解析できないセレクターは1.10.0まで、セレクターではない範囲は1.10.0でもそうでした。
「スキャンがPAGE_BUILTINS_BROKENを投げた」
ページ自身のスクリプトが、エンジンが依存するJavaScriptの組み込みオブジェクト(Array.prototype.filter、Map、JSON.stringifyなど)を変更しているため、そのページのスキャンは、もっともらしく見えて誤っている結果を返す可能性があります。エラーのメッセージとそのbrokenリストが、それぞれを示します。ページのスクリプトがそれらを変更する前にスキャンしてください(バンドルをもっと早く注入するか、それを行うスクリプトを含まないビルドをスキャンします)。既知の制限事項を参照してください。
「スキャンがいつまでも返らない」
ページ内の何かがスキャンを止めています。戻らないゲッターやスクリプト、あるいはパックやカスタムルールのループです。いったん始まったスキャンをページ内から止める手段はないので、スキャンを開始する側で制限時間を設け、それを過ぎたらそのタブを諦めてください。PlaywrightとPuppeteerでの方法はスタンドアロンにあります。jsdomでは、スキャンをワーカースレッドか子プロセスで実行してください。
「アップデート後にスキャンが遅くなった」
スキャンにかかる時間の大半はブラウザーでのスタイルとレイアウトの処理なので、エンジンだけでなく、ページ、マシン、ブラウザーにも左右されます。それでもリリースによって変わることはあります。ページのより多くの部分を確認するようになったルールは、その分時間がかかります。リリースでルールのコストが変わったときは、coreのCHANGELOG.mdに記載されます。GitHub上のPERFORMANCE.mdには、リリースごとのスキャン時間が、同じページでの以前のリリースに対する比率として、計測方法とともに記録されています。遅くなっても検出が増えたルールは、新たに見つけるものの分だけ時間がかかっているのかもしれません。結果が同じなのに遅くなったルールは退行なので、報告してください。
「パックが実行されなかった」、またはskippedPacksが空でない
coreの範囲がインストールされているコアを含まないパック、妥当でないパック、コアと両立しないパック(名前空間の外にあるルールID、コアが持つ辞書のキー)はスキップされます。スキャンはそのパックなしで実行され、警告を出し、理由とともにresult.skippedPacksに列挙します。strictOptionsのもとではエラーを投げます。ページ内では、engineOptions.packsで指定されたのに、どのpackScriptの呼び出しも登録していないパックや、別のバージョンのコアで登録されたパックが、同じように列挙されます。バインディングはこれを'pack-skipped'のスキャンギャップとして報告し、test-matchersはアサーションを失敗させ、CLIは終了コード2で終了します。パックは@surea11y/coreおよびそれを実行するパッケージと並べてインストールし、3つが同じコアを共有するようにしてください。パックを参照してください。
「カスタムルールが、はっきりした理由もなく常にcantTellを返す」
まず結果のerrorフィールドを確認してください。"<something> is not defined"と書かれている場合、runInPageが関数本体の外にある変数(モジュールスコープのconst、インポートしたヘルパーなど、ctx.*を経由せずに参照しているもの)を参照しています。これは実際によくある落とし穴です。runInPageはソースコードの文字列にシリアライズされ、後でページのコンテキスト内で評価し直されます。そのためビルドではこれを検出できず、ルールを実行して初めてわかります。しかも失敗はクラッシュではなく、(情報に乏しいとはいえ)ふつうの結果に見えます。詳しい説明と修正方法(値をrunInPageの中に移すか、ctx.rule/ctx.helpers経由で渡す)はRULE_AUTHORING.mdの§1.1を参照してください。
「ジオメトリに依存するルール(target-size-minimumなど)が常にnotApplicableを返す」
素のjsdom(実際のブラウザーなし)はCSSレイアウトを実装していないため、getBoundingClientRect()は常にゼロのジオメトリを返します。実際のレイアウトが必要なルールは、jsdomでは推測せずにnotApplicableを報告します。これらのルールで実際の検出結果を得るには、実際のブラウザー(Puppeteer/Playwright。INTEGRATION.mdのPattern 2を参照)で実行してください。LIMITATIONS.mdも参照してください。
「WebコンポーネントがcantTell(name_internals)になる」
プライベートフィールド(#internals)に保持したElementInternalsを通じて、自分に名前を付けたり、状態を設定したり、ロールを与えたりするカスタム要素は、ブラウザーによって公開されますが、ページのスクリプトはそれを読み戻せません。1.12以降、エンジンはプロパティ(_internals、internalsなど)に保持されたinternalsを読み取り、読み取れる名前や必須の状態がないほかの定義済みカスタム要素については、不合格にする代わりに確認を求めます(name_internals、state_internals)。
そうした要素をスキャンで判定できるようにするには、ページのスクリプトが動く前にinternalsフックをインストールしてください。各バインディングのinstallInternalsHook()(Playwrightではページかコンテキスト、Puppeteerではページ、Seleniumではドライバー、WebdriverIOではブラウザー、CypressではサポートファイルかonBeforeLoadで呼び出します)、test-matchersのセットアップファイル、自分で用意したページならcoreのinternalsHookScript()を使います。フックが動いたかどうかは、結果のengine.environment.internalsHook('early'または'late')でわかります。フックがあると、ページ上のどのスクリプトもすべてのコンポーネントのinternalsを読めるため、テストと監査のための実行に限って使うものです。既知の制限事項を参照してください。
「fail/cantTellの検出箇所しか表示されない。合格した要素の一覧はどこにあるのか」
このエンジンは設計上、ルールが合格とした要素を列挙しません。列挙するのは指摘した要素だけです。ルール全体のoutcome: 'pass'は「適用対象が存在し、どれも指摘されなかった」ことを意味し、occurrencesは[]です。例外はコントラストのルールで、そのpassには、要素ではなくスキャンについて説明する(対象となり計算できたテキストがどれだけあったか)、selectorが空の出現箇所が1つ付きます。具体的にどの要素がチェックされて問題なしとされたのかを知りたい場合、その情報は現在公開されていません。OUTPUT_SCHEMA.mdを参照してください。
「反応するはずのルールが、問題があるとわかっているページでnotApplicableを返した、または何も見つけなかった」
よくある原因は2つあります。可能性の高い順に挙げます。
- 要素がアクセシビリティツリーから除外されている:
aria-hidden="true"、display: none、visibility: hidden、hidden、またはinertの祖先要素。ほとんどのルールは、支援技術からすでに見えないコンテンツをスキップします。非表示の要素をチェックしても意味がなく、どのユーザーも出会わないコンテンツに対して誤解を招くfailを出しかねないからです。この除外が意味をなさない場合に、明示的に除外しないルールもあります(たとえばno-autoplay-audio。非表示のオーディオでも音は鳴ります)。src/checks/にある該当ルールのファイル先頭のコメント(@applicability)を確認してください。 excludeSelectors:これを設定している場合(直接、または共有設定から継承している場合)、対象の要素がそれに一致していないか確認してください。これはengineOptions.rules[ruleId].excludeSelectorsで単一のルールに限定することもできます(ENGINE_OPTIONS.mdを参照)。反応するはずのルールが1つの要素に対してだけnotApplicable/passを返し続ける場合は、グローバルの除外リストだけでなく、そのルール固有の除外リストが設定されていないかも確認してください。
「チェックはすべてpassなのに、なぜWCAGの各達成基準がcantTellなのか」
1.14からは、rulesResultsのWCAGの集約がpassになるのは、その達成基準が求めることをすべてルールがチェックする場合だけで、そうした達成基準はまだ1つも挙げられていません(src/coverage/complete-applicability.jsのCOMPLETE_CHECK)。達成基準のルールがすべてpassのとき、その集約はreasonCodeがcomposite.rollup.cantTell.partialCheckのcantTellになります。ルールがチェックする部分は満たされていて、残りは人が確認するということです。html-lang-attr-presentは<html lang="fr">が言語を示していることを確かめますが、ページがフランス語で書かれていることは確かめません。同じように、ルールが何も見つけなかった達成基準がnotApplicableになるのは、その達成基準が適用されるすべてのケースをルールが見つけられる場合だけで、それ以外はcomposite.rollup.cantTell.partialCoverageのcantTellです。不合格のルールがあれば、その達成基準は引き続き不合格になります。これらは「チェックは問題なし、確認が必要」「チェックする対象が見つからない」として表示し、passとして表示しないでください。WCAG適合性との対応を参照してください。
「スキャン結果に不合格がないなら、そのページはWCAGに適合しているのか」
いいえ。WCAG_CONFORMANCE.mdを参照してください。ルールのpassが意味するのは、実行されたチェックが、スキャンしたページの範囲で問題なしだったということです。ページの一部に絞ったスキャン、一部のルールに限ったスキャン、jsdomで実行したスキャンは、チェックできる範囲が狭くなります。WCAGのかなりの部分は、人の判断(正確な代替テキスト、理解しやすいエラーメッセージ)や、このエンジンの設計ではまったく行えない動的なテスト(キーボードトラップ、ズーム時のリフロー)を必要とします。明示的な、意図的に網羅を目指していない一覧はLIMITATIONS.mdを参照してください。
「cantTellは失敗として扱うべきか」
「人が確認する必要がある」として扱ってください。設計上、合格でも不合格でもありません。よくある選択は、cantTellの検出結果を記録するだけで、CIを失敗させないことです。エンジンが判定できないと明言したものでビルドを失敗させると、みんながゲートを無視するようになりがちだからです。マージ前にすべてをレビューしたいチームは、それでゲートを設けることもできます。どちらの場合も、不合格とは分けて扱ってください。その中間として、ページの手がかりが不合格を示している、不合格の方向に傾く検出結果だけでビルドを止めることもできます。JUnitとSARIFのレポートのleanFailAs、またはleaningFailures(result)を使います(出力スキーマを参照)。coreのルールのcantTellの検出結果は、それぞれhowToCheckで答え方を示します。この動作を変えたい場合(カスタムポリシーの契約を使うなど)はPOLICY.mdを、CIゲートの具体例はINTEGRATION.mdを参照してください。
「ロケールが部分的にしか翻訳されていない場合はどうなるのか」
欠けているキーは文字列ごとに英語へフォールバックします(空欄や壊れた結果にはなりません)。そのため、部分的なロケールでも完全に失敗することはなく、品質が段階的に下がるだけです。仕組みと現在の対応範囲はI18N.mdを参照してください。キーの一致は期待任せではなく、core自身のビルドで強制しています。英語に追加された、または名前が変わったキーはすべて、ほかのすべてのロケールファイルに反映され、誰かが翻訳するまで英語のテキストを保持します。そのため、ロケールの文言が遅れることはあっても、キーが遅れることはありません。
「runDomRulesInPageとruna11yCoreInPage、どちらを使えばよいのか」
同じNodeプロセス内で直接呼び出す場合(jsdom)はrunDomRulesInPageです。関数そのものを別のJSレルムに渡す場合はruna11yCoreInPageです。ほぼ常にPuppeteer/Playwrightのpage.evaluateのケースで、これは関数をソースコードの文字列にシリアライズし、ブラウザーのタブ内で再実行します(タブからはNodeのモジュールスコープにアクセスできません)。両方のパターンの完全な例はINTEGRATION.mdを参照してください。
「アップグレード後、ベースラインが既知の検出結果を新規として、そのエントリーをstaleとして報告する」
検出結果の識別子には理由コードが含まれます。1.14では、理由コードを付けていなかった17のルールにコードが付き、いくつかのコードが原因ごとに分割されました。1.13で書いたベースラインは、src/baseline-moves.jsonによって、1.14.xでも引き続きそれらの検出結果と一致します。アップグレードで判定結果が変わった検出結果は、それでも新規になったり消えたりします。その理由は変更履歴に書かれています。1.15より前に、@surea11y/core/baselineのmigrateBaselineEntriesでファイルのコードを書き換えるか、ベースラインを再生成して差分を確認してください。ベースライン/許可リストを参照してください。
「css-orientation-lockがSTYLESHEETS_NOT_READABLEで確認を求める」
ページのスクリプトは、別のオリジン(CDN、フォントサービス)が配信するスタイルシートを読めないため、CSSを読むルールはその内容がわかりません。そのテキストをengineOptions.styleSheetTextsでスキャンに渡してください。preloadStyleSheets()、またはブラウザーバンドルのruna11yCoreInPageAsyncは、サーバーが共有しているものを取得します。ブラウザーを操作するツールなら、それ以外のものもページの外から取得できます。GitHub上の統合ガイドINTEGRATION.mdの「Style sheets from another origin」を参照してください。