トピックを検索
バッジしか変わらない「カートに入れる」ボタン
「カートに入れる」を押すと、ヘッダーにあるカートのバッジの数字が増えます。しかしバッジはライブリージョンの外にあり、ボタンもフォーカスを持ったまま名前が変わらないため、スクリーンリーダーは何も読み上げません。
cartCount.textContent = cart.count; 問題
スクリーンリーダーの利用者は「カートに入れる」を押しても何も聞こえず、商品が入ったか分からないため、もう一度押したり、ページを離れて確かめたりします。
生活雑貨のショップの商品ページに、ホーローマグとその価格、「カートに入れる」ボタンがあります。ヘッダーには買い物袋のアイコンがあり、カートの商品数を示す小さな丸いバッジが付いています。デザインは落ち着いた画面を優先していて、ポップアップもスライドして出るパネルもありません。「カートに入れる」を押すと、1秒ほどしてショップのサーバーが応答したところで、バッジが0から1に変わります。知らせはそれだけで、それまでの1秒ほどは、押したことが受け付けられたかどうかも分かりません。画面が見える人は、視界の端で数字が変わるのに気づきます。スクリーンリーダーの利用者がボタンを押しても、待っている間もそのあとも、何も聞こえません。フォーカスはボタンに残り、名前も「カートに入れる」のままで、バッジはページの反対の端にあります。マグが入ったのか分からないため、もう一度押して2個にしてしまうか、ページを離れてカートを開いて確かめ、元の位置を探しながら戻ってきます。
「カートの商品は1点です」はステータスメッセージです。利用者の操作の結果を伝え、フォーカスを奪いません。達成基準4.1.3は、こうしたメッセージを役割やプロパティによって支援技術に伝え、フォーカスを動かさずに読み上げられるようにすることを求めています。これはW3Cがこの基準の例として挙げている場面そのものです。「カートに入れる」ボタンを押すと、カートのアイコンの近くの表示が「5 items」に変わり、スクリーンリーダーがそれを読み上げる、という例です。このページのバッジは、カートのリンクの中にあるただの span です。画面上の数字は変わっても、変化を読み上げるよう支援技術に伝える役割やプロパティがないため、失敗例F103に当たります。ボタンも代わりにはなりません。フォーカスも名前も変わらないので、スクリーンリーダーが新しく読むものがないのです。それに、仮に読まれたとしても「1」だけでは意味が伝わりにくいため、W3Cは「in shopping cart」のような言葉を数字に添えることを勧めています。
バッジは、ページ全体を一度に見ている人に向けて置かれています。縦に長い商品ページでは、ボタンまで来たときにヘッダーが画面の外に出ていることも多く、画面が見える人でも変化を見逃すことがあります。
10グループ中2グループに影響
スキャンは、名前のあるボタンと名前のあるリンクを見て、どちらも合格にします。実際のショップで押すと本物のカートに商品が入るため、「カートに入れる」は押しません。そのためバッジの変化を見ることはなく、マークアップにもバッジが結果を伝えるものだとは書かれていません。EaseWebの診断では、「カートに入れる」や「リンクをコピー」のボタンがあるのにライブリージョンが一つもないページを、手作業で確かめる手がかりとして報告します。カートをアクセシブルにするよう頼まれたAIアシスタントは、カートのリンクに「カート、1点」のような aria-label を付けることがあります。リンクにたどり着いたときに読まれる名前は分かりやすくなりますが、フォーカスのない要素の名前が変わっても読み上げられません。カートのリンクへフォーカスを移す例もありますが、見ていた商品から利用者を引き離してしまいます。スクリーンリーダーを動かしてボタンを押して、初めて何が読まれるかが分かります。
修正方法
押す前からページにあるステータス領域に、何が起きたかを書き込みます。
- 1ステータス領域は最初からページに置き、言葉だけを変える。
- 2成功も失敗も、フォーカスを動かさず、ボタンも無効にせずに言葉で伝える。
- 3伝えるのは利用者の操作の結果だけ。押すたびに一度、ページの読み込み時には何も伝えない。
<header> <a href="/" class="logo">Larkfield</a> <a href="/cart" class="cart"> <svg aria-hidden="true">…</svg> <span class="visually-hidden">カート</span> <span id="cart-count" class="badge">0</span> </a></header><main> <h1>ホーローマグ</h1> <p class="price">$18</p> <button type="button" id="add">カートに入れる</button></main>ボタンのそばの確認の行
<header> <a href="/" class="logo">Larkfield</a> <a href="/cart" class="cart"> <svg aria-hidden="true">…</svg> <span class="visually-hidden">カート</span> <span id="cart-count" class="badge">0</span> </a></header><main> <h1>ホーローマグ</h1> <p class="price">$18</p> <button type="button" id="add">カートに入れる</button> <p id="add-status" role="status"></p></main>この方法を選ぶ理由:メッセージは対象のそばに出す
サーバーが応答すれば数字は正しく変わります。しかし span はライブリージョンではなく、フォーカスのあるボタンも何も変わらないため、待っている間もそのあとも、スクリーンリーダーには読み上げるものがありません。最初の追加の処理中にもう一度押すと、マグがもう1個入ります。追加に失敗すると、何も変わりません。
role="status" の p は空の状態で最初からHTMLにあるため、文言が変わる前から、ブラウザーはこの領域の変化を見張っています。スクリーンリーダーは読み上げ中の内容を終えてから「ホーローマグを追加しました。カートの商品は1点です」と読みます。ショップの応答に0.5秒以上かかるときは、その前に「ホーローマグを追加しています…」と読まれるため、待っている間も無音になりません。すぐに応答があれば省かれるので、二度続けて聞かされることもありません。フォーカスはボタンに残り、ボタンも有効なままです。待っている間に押しても無視され、マグが2個入ることはありません。押すたびに文中の件数が変わるので、2回目も読み上げられます。この行はボタンのすぐ下にあり、画面拡大ソフトの利用者がもともと見ている場所です。ヘッダーが見えなくなるまでスクロールしていても、画面が見える人の目に入ります。行はページが変わるまで残しておけます。追加に失敗したときも、黙らずにそのことを言葉で伝えます。
三つとも、フォーカスを動かさずに結果を伝えます。ボタンのそばの行を使ってください。コードは隠した領域と同じで、劣る点がありません。メッセージが利用者の見ている場所にも表示され、追加に時間がかかっていることや失敗したことも伝えられます。隠した領域が合うのは、変更できないテーマを使っている場合など、デザインをそのまま保つ必要があるときです。サイト中の「カートに入れる」が一度に直ります。件数をステータス領域にする方法は、包む要素を一つ足すだけでスクリプトも変えない最小の変更ですが、それだけでは足りません。ヘッダーがどの画面幅でも同じで、件数がHTMLに入っている場合に限って使い、失敗には別にメッセージを用意してください。
アイコンが変わるだけのほかの操作。 操作の結果が絵だけで示される場面では、同じ失敗が起き、同じ方法で直せます。
- リンクのコピー:2秒間チェックマークに変わるアイコンは聞こえません。「リンクをコピーしました」を確認の行か領域に書き込み、ブラウザーがクリップボードへの書き込みを拒んだときもそう伝えます。
- お気に入りへの保存:塗りつぶされるハートはトグルボタンで、その状態はボタン自体に
aria-pressedで持たせます。フォーカスのあるボタンの状態は読み上げられます。押された状態が伝わらないトグルボタンを参照してください。ステータスメッセージが要るのは、「保存しました。リストを残すにはログインしてください」のように、ボタンでは伝えられないことがある場合だけです。 - 商品一覧のクイック追加:どのカードのボタンも「カートに入れる」なので、メッセージには商品名を入れます。
追加している間。 ボタンを押してからサーバーが応答するまでの時間も、確認の一部です。確認の行と隠した領域では、読み上げられない読み込み中の表示と同じように扱います。すぐに応答があれば結果だけを伝え、遅いときはタイマーで先に「追加しています…」と伝え、応答が来たらタイマーを止めます。ボタンは有効なままにして、処理中の押下は無視します。フォーカスのあるボタンを disabled にすると見た目は整いますが、ブラウザーがフォーカスをボタンから外すことがあります。Chromeではフォーカスがページ自体に落ち、ボタンが有効に戻っても戻らないため、スクリーンリーダーの利用者は買い物の途中で現在位置を失います。ボタンにスピナーを描くのは、名前が変わらない限りかまいません。言葉はステータス領域に任せます。無効なコントロール全般については見つからず理由も分からない無効なボタンを参照してください。
領域を先に用意します。 コンポーネントベースのフレームワークでは、領域をレイアウトか商品のマークアップに置きます。メッセージと一緒に現れる {#if} の中や、追加したときに表示されるミニカートの中には置きません。メッセージと一緒に現れる領域は、たいてい読み上げられません。この失敗は画面に出ても読み上げられないステータスメッセージで詳しく扱っています。
カートのパネルは別のデザインです。 追加するとミニカートのパネルを開くショップもあります。パネルの中へフォーカスを移してダイアログとして動くなら、ステータスメッセージではなくコンテキストの変化にあたり、何が起きたかはパネルの見出しが伝えます。背後にフォーカスが残るモーダルに従ってください。フォーカスを移さずにスライドして出るだけのパネルは、バッジと同じく何も読まれないため、ここで紹介した修正のどれかが必要です。
読み上げの割り込み。 role="status" は、スクリーンリーダーが読み終えるのを待ってから読まれるため、確認の知らせに合っています。カートへの追加に role="alert" や aria-live="assertive" を使わないでください。利用者が聞いていた内容を遮ります。
同じ文言が続く場合。 失敗が2回続くと同じ文を書き込むことになり、領域の文言が変わらないと何も読まないスクリーンリーダーもあります。件数が読み上げられない検索・絞り込み結果と同じように、先に領域を空にし、少し間を置いてから新しい文言を書き込みます。
修正にならないもの。 ボタンの文言を2秒間「追加しました」に変えても、フォーカスのあるボタンの変化を読み上げるかどうかはスクリーンリーダーによって異なり、カートの状態も伝わりません。カートのリンクやメッセージへフォーカスを移せば読まれますが、利用者を商品から引き離し、4.1.3が求める方法でもありません。件数に合わせて変わるカートのリンクの aria-label は、リンクにフォーカスがないため読み上げられません。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
デザインの案
確認は、さりげなくても読み上げられるようにできます。
「カートに入れる」や「リンクをコピー」がうまくいったことを示す、4通りの描き方です。それぞれ押してみてください。2つは起きたことを言葉にしてスクリーンリーダーにも渡し、2つは絵だけで示します。
デザインファイルで
- 画面にはチェックマークや数字、動く絵しか出ない場合でも、各操作の確認の文を書き添える。たとえば「ホーローマグを追加しました。カートの商品は1点です」。
- ショップが応答するまでボタンに何を表示するかを、ラベルと同じ幅で、ボタンに見えるまま描く。
- 成功のメッセージの隣に、失敗したときのメッセージも書く。
- 確認の色の横にコントラスト比を書く。文字は最低4.5:1。
合格
- 「追加しました」と変わるボタン ラベルが小さなスピナーと「追加中…」に、続いて淡い緑の地にチェックマークと「追加しました」(6.46:1)に2秒間変わります。幅は変えないので、周りがずれません。フォーカスのあるボタンのラベルの変化は、環境によっては読まれないため、同じ文を隠したステータスの行にも書き込みます。
ホーローマグ $18
商品の写真が入ったトースト 下から暗い色の丸いトーストが上がり、マグの写真と白い文字の「ホーローマグを追加しました。カートの商品は1点です」(14.81:1)を6秒間表示します。何かを追加する前からページにあるステータスの領域の中に描くので、表示と同時に読み上げられます。
不合格
- 写真がカートへ飛んでいくWCAG 4.1.3 写真の複製が0.5秒でカートのアイコンへ縮みながら飛び込み、アイコンの数字が弾みます。動きは変化を示して止まりますが、それが唯一の知らせで言葉がないため、スクリーンリーダーは何も読みません。動きを減らす設定では、数字が変わるだけです。
ホーローマグ $18 このマグを共有
チェックマークに変わる「リンクをコピー」のアイコンWCAG 4.1.3 鎖のアイコンが2秒間チェックマークに変わり、元に戻ります。言葉が何もないため、スクリーンリーダーには何も聞こえず、チェックマークだけでは誰にとっても「コピーした」とは伝わりにくいものです。横のステータスの行に「リンクをコピーしました」と書き込み、コピーに失敗したときもそう伝えます。
修正の確認
マウスを使わない4つの確認
確認方法: スクリーンリーダー、キーボード、ズーム
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。ヘッダー、商品ページのテンプレート、カートのスクリプトを変更した際は、再度確認します。
制限
ライブリージョンの動きは、スクリーンリーダーやブラウザーによって異なります。特に、リンクの中の領域、移動した領域、ダイアログ内の領域では差が出ます。このパターンは、一つの商品ページの一つのボタンを扱っています。カートのボタン自体の名前は名前がないアイコンだけのボタンで、「カートに入れる」の隣の数量はボタンに名前がなく、数を入力できない数量ステッパーで扱っています。ニュースレターの登録のように、送信して結果を表示するフォームは別のトピックです。パネル、クイック追加ボタン、スマートフォン用のヘッダーを含めた実際のカートを、利用者が使うスクリーンリーダーで確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。