トピックを検索
メッセージを読み上げないチャットウィジェット
サポートチャットがメッセージをただのdivに入れているため、パネルを開いたときのあいさつも、届いた返信も、スクリーンリーダーに読まれません。
messages.append(bubble) 問題
スクリーンリーダーの利用者には、サポートチャットを開いたときのあいさつも、届いた返信も聞こえません。
家具店のBirchwood Homeでは、どのページの隅にも「チャットで相談」ボタンがあります。押すと、その場所に小さなチャットのパネルが開きます。「Birchwoodのチャット」という見出し、閉じるための×、「こんにちは。ご注文についてお気軽にお尋ねください。」というあいさつ、メッセージ欄と「送信」ボタンがあります。スクリーンリーダーの利用者がボタンを押すと、フォーカスがメッセージ欄に移り、スクリーンリーダーは「メッセージ、編集テキスト」と読むだけで、あいさつは読みません。
利用者は「配送先の住所を変更できますか?」と入力してEnterを押します。質問が会話に表示され、数秒後、その下に「はい。発送前であれば『ご注文履歴』から変更できます。」という返信が表示されます。スクリーンリーダーは何も言いません。利用者はしばらく待ち、あいさつの内容と返事があったかを確かめるために、メッセージ欄を離れて会話を読み返します。
スクリプトは、各メッセージを新しい段落として、ただの div の中に追加します。支援技術が変化を読み上げるのは、一般にライブリージョンの中だけで、この div はライブリージョンではないため、返信は表示されても読み上げられません。返信は、利用者が今した操作についての知らせで、フォーカスを動かさずに届きます。これは達成基準4.1.3が扱う場面で、W3Cが role="log" を使うこの基準の達成方法の例にも、チャットの履歴が使われています。スクリーンリーダーの利用者も読み返せば返信を見つけられますが、いつ確かめればよいかは何も伝えられません。
あいさつが読まれない理由は別にあります。あいさつはパネルが表示された時点ですでにあり、フォーカスをメッセージ欄に移しても、読まれるのはメッセージ欄の名前だけで、周りの内容は読まれません。あいさつは読み進めれば見つかるため、これだけでは達成基準を満たさないことにはなりません。しかし利用者は、あいさつに何が書かれているかを知らないままチャットを始めることになります。あいさつには、返信までの時間や、用意しておくものなど、最初に知りたいことが書かれている場合もあります。
パネルを見ている人には、この問題は見えません。開いたときにあいさつが、届いたときに返信が見えるからです。影響を受けるのは、読み上げられる内容に頼っている人です。
10グループ中1グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響なし)
- 届く範囲・力 (影響なし)
- 認知 (影響なし)
- 光過敏 (影響なし)
- 視覚を使わない スクリーンリーダーの利用者は、チャットを開いてもメッセージ欄しか聞こえず、質問を送っても何も返ってきません。あいさつの内容や返事があったかを知るには、メッセージ欄を離れて会話を何度も読み返す必要があり、その間、サポート担当者のほうが返事を待っていることもあります。
ページを走査するチェックには、ボタン、ラベルのある入力欄、文字の入った div が見えるだけで、問題は見つかりません。この問題は時間の中にしかありません。返信が届くにはメッセージを送る必要があり、実際のサイトで診断がメッセージを送ることは決してあってはなりません。本物のサポート担当者に届くからです。多くのチャットウィジェットはサービス提供元のスクリプトで、提供元のドメインから配信されるフレームの中に描かれることも多く、サイトのチームの誰もそのコードを読んでいません。チャットウィジェットを頼まれたAIアシスタントも、この形を書くことがあります。吹き出しが表示され、パネルがスクロールし、新しいメッセージが見える位置に保たれるという、一つ一つは完成して見える部品の組み合わせだからです。
試してみる
チャットを開いてメッセージを送ってください。スクリーンリーダーは何を読みますか?
- Tabで「チャットで相談」へ移動し、Enterを押します。フォーカスがメッセージ欄に移ります。あいさつも一緒に読まれますか?
- Enterで質問を送り、返信が届いたときのスクリーンリーダーの読み上げを、ページの下の欄で確かめます。
- Escapeか×でチャットを閉じ、もう一度開きます。今度は何が読まれますか?
修正方法
パネルを開いたときに会話をメッセージ欄と一緒に読ませ、各返信はページに最初からあるログかライブリージョンに追加します。
- 1パネルを開くと、それまでの会話が読まれる。
- 2返信はすべて、スクリーンリーダーが監視する領域に入り、フォーカスは動かない。
- 3パネルは名前のあるボタンとEscapeで閉じられ、フォーカスは起動ボタンに戻る。
<button type="button" class="chat-launcher">チャットで相談</button><section id="chat" class="chat-panel" aria-labelledby="chat-title" hidden> <div class="chat-header"> <h2 id="chat-title">Birchwoodのチャット</h2> <button type="button" class="chat-close" aria-label="チャットを閉じる">×</button> </div> <div class="chat-messages"> <p class="message from-us"><span class="visually-hidden">Birchwood:</span>こんにちは。ご注文についてお気軽にお尋ねください。</p> </div> <form class="chat-form"> <label for="chat-input" class="visually-hidden">メッセージ</label> <input id="chat-input" autocomplete="off"> <button>送信</button> </form></section>メッセージのログと、開いたときの読み上げ
<button type="button" class="chat-launcher">チャットで相談</button><section id="chat" class="chat-panel" aria-labelledby="chat-title" hidden> <div class="chat-header"> <h2 id="chat-title">Birchwoodのチャット</h2> <button type="button" class="chat-close" aria-label="チャットを閉じる">×</button> </div> <div class="chat-messages" role="log" aria-label="メッセージ"> <p id="message-0" class="message from-us"><span class="visually-hidden">Birchwood:</span>こんにちは。ご注文についてお気軽にお尋ねください。</p> </div> <form class="chat-form"> <label for="chat-input" class="visually-hidden">メッセージ</label> <input id="chat-input" autocomplete="off"> <button>送信</button> </form></section>パネルを開くとフォーカスがメッセージ欄に移り、ラベルの「メッセージ」だけが読まれるため、あいさつは読まれません。会話はただの div なので、返信を追加しても変わるのは画面だけです。パネルのほかの部分は動きます。起動ボタンと閉じるボタンは名前のあるボタンで、Escapeでもパネルが閉じ、フォーカスはパネルに入り、閉じると起動ボタンに戻ります。
パネルを開くと、メッセージ欄の aria-describedby にすべてのメッセージのidが入るため、メッセージ欄はそれまでの会話と一緒に「メッセージ、編集テキスト。Birchwood:こんにちは。ご注文についてお気軽にお尋ねください。」と読まれます。後でもう一度開くと、最初からのすべてのメッセージが読まれます。idで指すのはログではなく各メッセージです。ログを指すと、説明が中身ではなく、ログの名前の「メッセージ」になることがあるからです。説明はフォーカスが最初にメッセージ欄を離れたときに外れるため、「送信」へ移って戻っても、会話がもう一度読まれることはありません。
role="log" は、会話をライブリージョンにします。初期値がpoliteなので、スクリーンリーダーは読み上げ中の内容を終えてから、末尾に追加された各メッセージを読み、フォーカスは動きません。ログは返信が届く前からページのHTMLにあり、そのためにブラウザーは各返信を変化として捉えられます。スクリーンリーダーが何の領域かを伝えられるよう、ログには名前を付けます。ここでは「メッセージ」です。各メッセージには送り手が視覚的に隠したテキストで入っているため、返信は「Birchwood:はい。発送前であれば『ご注文履歴』から変更できます。」と読まれます。
どちらも、パネルを開いたときに会話を読み、フォーカスを動かさずにすべての返信を伝えます。ログはコードが少なく、W3Cがチャットの履歴について示している方法です。会話が名前のある一つの領域になり、二つの内容をそろえておく必要もありません。ただし、追加されたものはすべて読むため、送った瞬間の利用者自身のメッセージも読まれます。送信の確認として役立つと感じる人もいれば、うるさく感じる人もいます。会話が末尾にメッセージを足す以外のことをする場合は、別の読み上げ領域を選んでください。さかのぼってスクロールすると過去のメッセージを先頭に読み込む、メッセージのたびに一覧全体を描き直す、入力中の表示や既読の表示を一覧の中に入れる、といった場合です。ログはそれらもすべて読みます。別の領域は書き込んだ内容だけを伝えますが、その代わり、各返信の二つ目の写しを一つ目とそろえて管理する必要があります。
多くのチャットウィジェットは、サービス提供元のスクリプトです。起動ボタンもパネルも、提供元のドメインから配信されるフレームの中に描かれることが多く、その場合は自社のCSSやスクリプトから中には一切手が届きません。できることは次のとおりです。
- 導入する前に、「修正の確認」の手順でウィジェットを試す。提供元のデモページか、自社のテスト用ページで行います。
- ウィジェットの設定を確認する。数秒後に自動で開くパネルや、ページの上に飛び出すあいさつの吹き出しは無効にし、設定で選べるなら、起動ボタンにはアイコンだけでなく文字のラベルを表示します。
- 提供元にアクセシビリティ適合報告書(ACR)を求める。そのうえで、パネルを開いたときにあいさつやそれまでのメッセージが読まれるか、新しいメッセージがログかライブリージョンに入るか、起動ボタンと閉じる操作が名前のあるボタンか、Escapeでパネルが閉じて起動ボタンにフォーカスが戻るか、どのスクリーンリーダーで試したかを、具体的に尋ねます。
- 提供元のスクリプトが新着メッセージのイベントを提供している場合は、二つ目の修正のように、ページ側に用意した自前の読み上げ領域へ各返信を書き込めます。その前に、ウィジェット自身が読み上げていないかを確認してください。読み上げていれば、返信が2回ずつ聞こえます。
- うまくいかない点を、再現手順を添えて提供元に報告する。直るまでは、アクセシビリティ方針に既知の問題として記載し、チャットの横に、メールアドレスなど別の問い合わせ方法を示します。
提供元のスクリプトは、自社のリリースと関係なく変わります。提供元の更新やウィジェットの設定の変更のたびに、確認を繰り返してください。
領域を先に置く。 ログや読み上げ領域は、最初の返信より前にページにある必要があります。コンポーネントのフレームワークでは、パネルと一緒に描画し、最初のメッセージと一緒に現れる条件の中には決して入れません。メッセージと一緒に追加された領域は読み上げられません。仕組みは画面に出ても読み上げられないステータスメッセージで説明しています。領域を含むパネルの一部に aria-hidden="true" を付けた場合も同じです。
後から届くあいさつ。 多くのウィジェットは、パネルを開くと入力中の表示を出し、少し後にあいさつを追加します。ログなら、そのあいさつは返信と同じように読み上げられます。別の読み上げ領域を使う場合は、あいさつもその領域に書き込みます。どちらの場合も、そのあいさつはまだメッセージ欄の説明に入っていないため、2回ではなく1回だけ読まれます。
長い会話。 履歴の長いパネルを開くと、メッセージ欄と一緒にそのすべてが読まれます。スクリーンリーダーの利用者はCtrlキーで読み上げを止め、読みたいところから読み進められます。会話が長くなりがちなら、メッセージ欄の説明には、前回パネルを開いてからのメッセージか、最後の数件だけを入れます。
誰の発言かを伝える。 左右に分かれた2色の吹き出しは、誰が書いたかを晴眼の利用者に伝えますが、ほかには何も伝えません。スニペットのように、各メッセージに送り手を視覚的に隠したテキストで入れるか、吹き出しの上に名前を表示します。
雑音を入れない。 入力中の表示を数秒ごとにログへ書き込むと、数秒ごとに読まれます。ログの外に表示するか、1回だけ伝えます。返信に aria-live="assertive" や role="alert" は使いません。利用者が読んでいる内容に割り込むからです。
閉じている間に届いた返信。 パネルを閉じた後に届いた返信は、隠れた領域に入るため読み上げられません。代わりに起動ボタンで伝えます。ボタンの文字を「チャットで相談、新着メッセージ1件」のように変えます。返信を見せるために、パネルを自動で開くことはしません。
新しいメッセージにフォーカスを移さない。 そうすれば読まれはしますが、利用者が次の質問を入力している途中で、カーソルがメッセージ欄から外れてしまいます。
モーダルにするかどうか。 パソコンではパネルがページを操作不能にしないため、利用者はチャットしながらページを読めます。Tabでパネルを閉じずに外へ出てもかまいません。ただし、フォーカスが離れたときに閉じることもしません。それで何が起きるかはTabで閉じてフォーカスを見失う検索パネルを参照してください。パネルが画面全体を覆うことが多いスマートフォンでは、モーダルとして振る舞います。背後にフォーカスが残るモーダルのように、showModal() でダイアログとして開きます。
閉じるボタン。 ×は名前のある本物の button にします。クリック処理を付けた span で描くと、Tabでは届かず、パネルが起動ボタンの代わりに開くため、キーボードの利用者にはチャットを閉じる方法がなくなります。Tabキーで到達できないdivのボタンと同じ問題です。
起動ボタン。 div で描いた起動ボタンも同じように問題になり、アイコンだけの起動ボタンには、名前がないアイコンだけのボタンのように名前が必要です。隅に浮いている間は、フォーカス中の操作要素を覆うこともあります。フォーカス中のボタンを一部隠すチャットボタンを参照してください。拡大表示では、起動ボタンや開いたパネルが、ほかの浮いた要素とともに画面のずっと大きな部分を占めます。拡大するとページを覆う、浮いた購入バーを参照してください。パネル自体はページの最後、フッターの後に追加されることがよくあります。ここでの修正は開いたときにフォーカスをパネルへ移しますが、パネルを起動ボタンの次に置く方法はフォーカス順の最後に回る、フローティングボタンとパネルで扱っています。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
修正の確認
マウスを使わない5つの確認
確認方法: スクリーンリーダー、キーボード
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。チャットウィジェット、その提供元のスクリプト、または設定を変更した際は、再度確認します。
制限
このサンプルは、ページに重なるチャットのパネルのための出発点であり、どのウィジェットでも同じ結果になることを保証するものではありません。ライブリージョンの動きは、スクリーンリーダーとブラウザーによって異なります。特に、隠してから表示する領域や、フレームの中の領域では差が出るため、利用者が使うものでテストしてください。入力欄の説明がいつ読まれるかも、スクリーンリーダーとその設定によって異なります。多くは入力欄の名前の後に読みますが、少し間を置いてから読むものや、ヒントの設定が有効なときだけ読むものもあります。チャットの各返信がステータスメッセージに当たるかは、診断する人によって判断が分かれます。4.1.3の解説書は role="log" を処理の進み具合を伝える場面に挙げており、その達成方法はチャットを例にしています。どの基準で数えるとしても、読み上げられない返信は、スクリーンリーダーの利用者を待たせ続けます。Enterで送信するメッセージ欄には、別の問題もあります。IMEの変換を確定するEnterで、言葉が確定する前にメッセージが送られることがあります。IMEの変換確定のEnterで送信される入力欄を参照してください。IMEは変換の取り消しにEscapeも使うため、上のEscapeの処理は、変換中に押されたキーには反応しないようにしています。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。