WCAG 達成基準 · Level A

WCAG 3.3.1: エラーの特定

WCAG 3.3.1 では、入力エラーが自動的に検出された場合、エラーがある項目を特定し、そのエラーをユーザーにテキストで説明することが求められています。これにより、障害のあるユーザーがフォーム入力時の誤りを認識し、理解し、修正できるようになります。

この規定の意味

WCAG 3.3.1「エラーの特定」は、理解可能という原則の下にあるレベルAの達成基準です。そこでは次のように定義されています。「入力エラーが自動的に検出された場合、エラーがある項目が特定され、そのエラーが利用者に対してテキストで説明される。」 実務的には、あなたのウェブサイトやアプリケーションがユーザー入力を自動的に検証するあらゆる場面(フォーム送信時、フォーカスが外れたとき、リアルタイム検証など)でエラーが検出された場合、2つのことが必ず行われなければなりません。エラーを含む特定のフィールドまたはコントロールが明確に特定されること、そしてエラーの内容が実際のテキストコンテンツ(色やアイコン、音だけではなく)によって伝えられることです。

この達成基準は、ユーザーから入力を収集し、検証が自動的に行われるあらゆる状況に適用されます。これには、<input><select><textarea> といったHTMLフォーム要素や、ARIAで構築されたカスタムインタラクティブウィジェットが含まれます。また、JavaScriptによってトリガーされるクライアントサイド検証、requiredpatternminlengthtype などの属性を用いたブラウザのネイティブ制約検証、ページ再読み込み後にレンダリングされるサーバーサイド検証結果や、DOMに動的に挿入される検証結果も対象です。

適合(パス)とみなされるには、次のことが必要です。(1) エラーのある各フィールドがエラーメッセージとプログラム的に関連付けられていること — 一般的には aria-describedbyaria-errormessage を通じて行い、支援技術がそれを読み上げられるようにします。(2) エラーメッセージがUI上で視覚的に確認できるプレーンテキストであること(視覚ユーザーから隠されていないこと)。(3) テキストが「何がうまくいかなかったのか」を明確に説明していること(単に「何かがうまくいかなかった」と伝えるだけではないこと)。たとえば、「メールアドレスは必須です」は有効なエラー説明ですが、赤い枠線や感嘆符アイコンだけを表示し、テキストによる代替を一切提供しない場合は不適合(フェイル)です。

不適合(フェイル)となるのは、エラーが色の変化(テキストのない赤い枠線など)だけで示されている場合、どのフィールドに影響があるかを特定せずに role="alert" のみでエラーが通知される場合、エラーメッセージのテキストが空または汎用的すぎる場合(例:「無効な入力です」)、あるいはエラーメッセージがDOM内に存在していても対応するフィールドとプログラム的に関連付けられておらず、スクリーンリーダーがそれらを結び付けられない場合です。

WCAG 3.3.1は、自動検出が行われない場合には適用されません。たとえば、フォームが送信されてもサーバーが単にページを再読み込みするだけで、何が問題だったのか一切示さない場合、それは別のユーザビリティ上の問題ではありますが、この達成基準の厳密な適用範囲外です。しかし、システムが自動検出を行う場合(実際にはほぼすべての現代的なフォームが該当します)、この達成基準は完全に適用されます。特定の入力タイプやユースケースに対する例外は、この達成基準自体には定義されていません。

なぜ重要か

エラーの特定は、複数の障害当事者グループに対して重大な影響を与えます。NVDA、JAWS、VoiceOver などのスクリーンリーダーに依存する全盲およびロービジョンのユーザーにとって、赤い枠線や警告アイコンは何も伝えません。エラーメッセージが実際のテキストとして存在せず、エラーのあるフィールドとプログラム的に関連付けられていない場合、スクリーンリーダーはそれを読み上げず、ユーザーはなぜフォーム送信に失敗し続けるのか理解できないままになる可能性があります。世界保健機関によれば、世界で約22億人が何らかの視覚障害を抱えており、そのうち何百万人もの人々が、ウェブコンテンツとやり取りするために日々支援技術に依存しています。

認知障害(ディスレクシア、注意欠如障害、記憶障害など)を持つユーザーにとって、「何か問題が発生しました」のような曖昧なエラーメッセージは大きな障壁となります。これらのユーザーは、どのフィールドを修正すべきか、正しい形式や値は何かを正確に伝える、具体的で説明的なエラーテキストから大きな恩恵を受けます。たとえば、「無効な日付です」ではなく、「生年月日は DD/MM/YYYY 形式で入力してください」といったメッセージの方がはるかに行動に移しやすくなります。

運動障害を持ち、キーボードやスイッチデバイスで操作するユーザーは、フォーム内を移動するだけでも大きな労力を要します。エラーが不明瞭または特定されていない場合、問題箇所を見つけるためにフォーム全体を再度たどる必要があり、フォーム入力に伴う認知的・身体的負荷が大幅に増大します。エラーが明確に特定されていれば、これらのユーザーは問題のあるフィールドに直接移動できます。

現実のシナリオを考えてみましょう。障害給付を申請するために、あるトルコ国民が政府の電子行政ポータルに登録しようとしています。フォームでは、特定の形式での国民ID番号の入力が求められます。全盲のこのユーザーはID番号を入力します。送信すると、フォームは再読み込みされ、いくつかのフィールドが赤くハイライトされますが、テキストによるエラーは一切関連付けられていません。スクリーンリーダーは再びフィールドラベルだけを読み上げるだけで、何が問題だったのか、どのフィールドに問題があるのか、ユーザーにはまったく分かりません。ユーザーは手続きを完全に諦め、重要な公共サービスへのアクセスを失ってしまいます。これはまさに、WCAG 3.3.1が防ぐことを目的としている状況です。

アクセシビリティの観点を超えても、明確なエラーの特定は、すべてのユーザーにとってのコンバージョン率とユーザビリティの向上につながります。UXリサーチの研究では、説明的なインラインエラーメッセージがフォーム離脱を減らすことが一貫して示されています。SEOの観点からも、サイト滞在時間の増加やフォーム完了率の向上といったユーザーエンゲージメント指標の改善は、時間の経過とともに検索順位に良い影響を与えます。

関連する axe-core のルール

WCAG 3.3.1の完全な検証には、手動テストが必要です。これは、自動ツールが aria-describedbyaria-errormessage といった構造パターンの有無は検出できても、エラーメッセージのテキスト内容が意味を持ち、正確で、ユーザーがエラーを理解し修正するのに十分かどうかを評価できないためです。自動ツールは role="alert" 要素の存在を検出できますが、「4行目でエラーが発生しました」というメッセージが、スクリーンリーダーユーザーにとって検証エラーを十分に説明しているかどうかを判断することはできません。

  • aria-required-attr: この axe-core ルールは、特定のARIAロールを持つ要素に、必要なARIA属性がすべて存在するかをチェックします。エラー特定専用のルールではありませんが、関連性があります。なぜなら、エラー状態にあるフォームフィールドが role="textbox" などを使用しているにもかかわらず、必要な属性を欠いている場合、支援技術に状態情報が伝わらず、エラーの伝達が損なわれる可能性があるためです。
  • aria-valid-attr-value: このルールは、ARIA属性がDOM内に存在しないIDを参照しているケースを検出します。たとえば、あるフィールドが aria-describedby="email-error" を使用しているにもかかわらず、id="email-error" を持つ要素が存在しない場合、関連付けは壊れており、スクリーンリーダーはエラーメッセージを読み上げません。これは3.3.1における典型的なパターンの失敗です。
  • 手動評価の必要性: テスターは、検証エラーを手動で発生させ、そのうえでスクリーンリーダーを使用して次の点を確認する必要があります。エラーのあるフィールドが名前またはラベルによって特定されているか、エラーの説明が読み上げられるか、そのメッセージが意味があり行動可能か、そしてエラーが非テキスト手段だけで伝えられていないか。自動スキャンはユーザーの操作シーケンスをシミュレートしたり、メッセージテキストの意味的な質を評価したりできないため、この達成基準には人間の判断が不可欠です。

テスト方法

  1. axe DevTools または Lighthouse による自動スキャン: 検証エラーを発生させる前後で、フォームを含むページに対して axe DevTools のスキャンを実行します。特に、壊れた aria-describedbyaria-errormessage の参照、動的なエラー挿入に使用される role="alert"aria-live 領域の欠如、ラベルのないフォームフィールド(適切なエラー関連付けも妨げる)に関連する違反を探します。Lighthouse では、アクセシビリティ監査でフォーム関連の違反を確認します。なお、自動レポートに違反がないからといって3.3.1への適合が保証されるわけではなく、特定の構造的な失敗が除外されるに過ぎません。
  2. キーボードのみでの手動ナビゲーションテスト: キーボードだけ(Tab、Shift+Tab、Enter、Space)を使用して、意図的に誤った値や未入力の値でフォーム送信を試みます。次の点を確認します。フォーカスが最初のエラーのあるフィールド、またはその近くに移動するか、各エラーメッセージがビューポート内で視認できるか、エラーメッセージが特定のフィールド名を示し、問題をプレーンテキストで説明しているか。
  3. NVDA + Firefox によるスクリーンリーダーテスト: Firefox でフォームを開き、NVDA を有効にします。エラーを含んだ状態でフォームを送信します。注意深く聞きます。NVDA はどのフィールドにエラーがあるかを読み上げるか。エラーの説明は読み上げられるか。エラーのあるフィールドにTabで移動したとき、そのフィールドのアクセシブルな説明の一部として関連付けられたエラーメッセージを読み上げるか。aria-live 領域を使用している場合、アナウンスが遅延したり抑制されたりしていないかを確認します。
  4. VoiceOver + Safari(macOS/iOS)によるスクリーンリーダーテスト: Safari 上で VoiceOver を使用し、同じ手順を繰り返します。フォーム送信後、VO+右矢印でフォームを読み進めます。エラーのある各フィールドに、そのエラーテキストがアクセシブルネームまたは説明として含まれているかを確認します。iOS では、タッチナビゲーションやスワイプジェスチャーを用いてテストします。
  5. JAWS + Chrome によるスクリーンリーダーテスト: Chrome で JAWS を起動し、エラーを含んだ状態でフォームを送信します。JAWS の仮想カーソルを使って、エラーのある各フォームフィールドに移動します。そのフィールドを読み上げる際に、エラーメッセージテキストが含まれているかを確認します。また、フォームモードでは一部のライブリージョンのアナウンスが抑制されるため、JAWS のフォームモードの挙動も併せて確認します。
  6. 色および非テキストの手がかりの監査: 各エラー状態を視覚的に確認します。ブラウザの開発者ツールやブックマークレットを使って一時的にCSSを削除または無効化し、エラーの特定がDOM内のテキストとして依然として存在するかを確認します。これにより、エラーの伝達が色やアイコンだけに依存していないことを検証できます。
  7. 動的挿入の確認: シングルページアプリケーションやリアルタイム検証を行うフォームでは、エラー発生後にブラウザの開発者ツールでDOMを確認します。エラーメッセージ要素がDOM内に存在し、空でないテキストを含み、対応する入力要素から aria-describedby または aria-errormessage を通じて参照されていることを確認します。

修正方法

シナリオ1: 色だけでエラーを示している — 不適切な例

<!-- Fail: red border added via class, no error text associated -->
<label for='email'>Email Address</label>
<input type='email' id='email' name='email' class='input-error'>
<!-- CSS sets border: 2px solid red on .input-error -->
<!-- No error message text is rendered in the DOM -->

シナリオ1: 色だけでエラーを示している — 適切な例

<!-- Pass: error text is present, visible, and linked to the input -->
<label for='email'>Email Address</label>
<input
  type='email'
  id='email'
  name='email'
  class='input-error'
  aria-describedby='email-error'
  aria-invalid='true'
>
<!-- aria-invalid signals the error state to assistive technologies -->
<!-- aria-describedby links the input to its error message by ID -->
<p id='email-error' role='alert'>
  Please enter a valid email address, for example: [email protected]
</p>

シナリオ2: フィールドを特定しない汎用的なエラーサマリー — 不適切な例

<!-- Fail: a summary alert exists but does not identify which fields failed -->
<div role='alert'>
  <p>There are errors in the form. Please correct them and try again.</p>
</div>
<label for='phone'>Phone Number</label>
<input type='tel' id='phone' name='phone' class='is-invalid'>
<!-- No per-field error message; user cannot determine which field failed -->

シナリオ2: フィールドを特定しない汎用的なエラーサマリー — 適切な例

<!-- Pass: summary lists specific fields in error, and each field has inline error text -->
<div role='alert' aria-labelledby='error-heading'>
  <h2 id='error-heading'>2 errors found. Please fix the following:</h2>
  <ul>
    <li><a href='#phone'>Phone Number: must contain only digits and be 10 characters long</a></li>
    <li><a href='#dob'>Date of Birth: required field — please enter your date of birth</a></li>
  </ul>
</div>
<label for='phone'>Phone Number</label>
<input
  type='tel'
  id='phone'
  name='phone'
  aria-describedby='phone-error'
  aria-invalid='true'
>
<!-- Inline error linked via aria-describedby -->
<p id='phone-error'>Phone Number must contain only digits and be 10 characters long.</p>

シナリオ3: aria-describedby の参照が壊れている — 不適切な例

<!-- Fail: aria-describedby references an ID that does not exist in the DOM -->
<label for='username'>Username</label>
<input
  type='text'
  id='username'
  name='username'
  aria-describedby='username-msg'
  aria-invalid='true'
>
<!-- The element with id='username-msg' was never rendered or was removed -->
<!-- Screen readers find no target and announce nothing for the description -->

シナリオ3: aria-describedby の参照が壊れている — 適切な例

<!-- Pass: the referenced element exists in the DOM with matching ID -->
<label for='username'>Username</label>
<input
  type='text'
  id='username'
  name='username'
  aria-describedby='username-msg'
  aria-invalid='true'
>
<!-- Element with matching id is present and contains descriptive text -->
<span id='username-msg'>
  Username must be between 4 and 20 characters and contain only letters and numbers.
</span>

シナリオ4: リアルタイム検証エラーが動的に挿入される — 不適切な例

<!-- Fail: error injected into DOM but not associated with input; no live region -->
<label for='password'>Password</label>
<input type='password' id='password' name='password'>
<!-- After blur, JavaScript appends this element, but no aria-describedby on input -->
<div class='error-text'>Password is too short.</div>

シナリオ4: リアルタイム検証エラーが動的に挿入される — 適切な例

<!-- Pass: error container pre-exists in DOM (empty), input references it; aria-live announces changes -->
<label for='password'>Password</label>
<input
  type='password'
  id='password'
  name='password'
  aria-describedby='password-error'
  aria-invalid='false'
>
<!-- Empty span present at page load; JavaScript populates text and sets aria-invalid='true' on error -->
<span id='password-error' aria-live='polite'></span>
<!-- JavaScript on blur: -->
<!-- document.getElementById('password-error').textContent = 'Password must be at least 8 characters.'; -->
<!-- document.getElementById('password').setAttribute('aria-invalid', 'true'); -->

よくある間違い

  • エラーを示すために、DOM内に対応するテキストを一切追加せず、CSSクラスの変更(例: border-color: red を追加)だけを用いること。 色だけでは全盲のユーザーや色覚障害のあるユーザーには知覚できず、3.3.1と1.4.1の両方に不適合となります。
  • 入力要素に aria-describedby を記述しているが、その参照先IDが、検証リセット時に条件付きでレンダリングされたりDOMから削除されたりする要素を指していること。 参照が壊れているため、スクリーンリーダーは関連コンテンツを見つけられず、エラーは支援技術ユーザーにとって事実上見えないものになります。
  • エラーメッセージとして placeholder テキストだけを使用すること。 プレースホルダーテキストはユーザーが入力を始めると消えてしまい、コントラストが低いことも多く、エラー状態においてフィールドの説明の一部としてすべてのスクリーンリーダーが確実に読み上げるとは限りません。
  • エラーメッセージをDOMに動的に挿入しながら、aria-describedby を通じて親フィールドと関連付けることを怠ること。 視覚的に隣接しているエラーメッセージであっても、自動的に入力要素と関連付けられるわけではなく、プログラム的なリンクを明示的に行う必要があります。
  • ページレベルのエラーサマリーだけを表示し、各サマリー項目をエラーのある特定のフィールドにリンクしないこと。 スクリーンリーダーやキーボードナビゲーションのユーザーは、エラー説明から修正が必要なフィールドへ直接移動できる必要があります。
  • フィールドに aria-invalid='true' を設定しながら、エラーメッセージテキストを提供しないこと。 aria-invalid 属性はフィールドがエラー状態であることを示すだけで、エラーの内容は説明しません。必ず説明的なメッセージと組み合わせる必要があります。
  • 「無効な入力です」や「フィールドにエラーがあります」のように、あまりに汎用的なエラーメッセージを提供すること。 これらの説明は何が問題なのか、どう修正すべきかを示しておらず、3.3.1の説明要件を満たさないため、すべてのユーザーにとってエラー修正を不必要に困難にします。
  • フォームをリセットする際に、エラーコンテナから aria-live 領域や role='alert' を削除し、スクリーンリーダーが内容の読み上げを終える前にコンテナを消してしまうこと。 エラーのアナウンスが途中で途切れ、ユーザーが検証結果を認識できないままになる可能性があります。
  • カスタマイズを行わず、ブラウザネイティブの検証バブル(デフォルトのツールチップポップアップ)に依存すること。 ネイティブのブラウザ検証UIは、ブラウザや支援技術の組み合わせによって挙動が一貫せず、複雑なフォームシナリオでは、エラーの特定と説明に関するWCAG要件を満たさないことがよくあります。
  • エラーメッセージを、関連するフィールドから視覚的に離れた場所(ヘッダーのアラートボックスのみなど)に配置し、各フィールドの近くにインラインメッセージを提供しないこと。 画面を拡大しているロービジョンのユーザーは、入力エリアにフォーカスしている間、ヘッダーレベルのアラートを視認できない場合があり、キーボードユーザーはエラーを読むために長い距離を移動しなければなりません。

トルコのアクセシビリティ規制との関係

トルコの大統領通達 2025/10は、2025年6月21日付官報第32933号で公布され、トルコで事業を行う幅広い主体に対してウェブアクセシビリティ要件を義務付けています。この通達は技術標準としてWCAG 2.2を採用しており、すべてのレベルA達成基準 — WCAG 3.3.1「エラーの特定」を含む — を対象組織にとって法的に必須のものとしています。

通達で定められた遵守期限は、公布日から公的機関は1年民間部門の事業者は2年です。つまり、公的機関は2026年6月までにWCAG 2.2レベルAへの適合を達成しなければならず、民間部門の対象事業者は2027年6月までに達成する必要があります。

通達の対象となる主体には、トルコのデジタルサービスの広範なセクターが含まれます。公的機関としては、すべての中央政府省庁、自治体、国立大学、公的機関がデジタルサービスのアクセシビリティ確保を義務付けられています。民間部門では、電子商取引プラットフォーム、銀行および金融機関、病院や民間医療提供者、20万人以上の加入者を持つ通信会社、旅行代理店、民間交通事業者、国民教育省(MoNE)に認可された私立学校が通達の対象です。

これらすべての主体にとって、オンラインフォームが使用されるあらゆる場面でWCAG 3.3.1は直接的に関係します。実際には、ほぼすべてのデジタル接点が該当します。電子商取引のチェックアウトフォーム、銀行口座開設フロー、病院の患者登録ポータル、政府給付申請フォーム、航空・バス予約システム、学校の入学申込ページなどは、いずれもフォーム検証に依存しています。これらのシステムのいずれかが入力エラーを自動検出しながら、フィールドを特定せず、エラーをテキストで説明しない場合、通達の要件に直接違反することになります。

通達に違反すると、トルコのデジタルアクセシビリティの執行枠組みが成熟するにつれ、対象組織は規制当局による監視、評判リスク、潜在的な制裁にさらされる可能性があります。法令遵守を超えて、3.3.1の遵守はインクルーシブなサービス提供へのコミットメントを示すものでもあります。TÜİKのデータによれば、トルコには約850万人の障害当事者が存在し、そのすべての市民がオンラインで重要なサービスに障壁なくアクセスできるようにすることが求められます。Accsible のSDKフレームワークの下で事業を行う組織は、自動的な構造面での適合だけでなく、エラーハンドリングがこの基本的な達成基準を完全に満たしていることを確認するために、徹底した手動テストにも優先的に取り組むべきです。