トピックを検索
スクリーンリーダーが読み上げ続けるカウントダウンタイマー
セールのカウントダウンが aria-live="assertive" の領域にあるため、スクリーンリーダーは1行読むたびに新しい時間を読み、または行を途中で切って時間を読み上げ、ページにはカウントダウンを止める手段もありません。
<p class="sale-banner" aria-live="assertive"> 問題
1行ごとに新しい時間が読まれるか、時間が行を途中で切るため、スクリーンリーダーの利用者は商品ページを通して読めず、数字の動きで気が散る人にも止める手段がありません。
リネンの店が秋のセールを開いていて、どの商品ページも「セール終了まで 02:14:09」というバナーで始まります。数字は1秒ずつ減っていきます。開発者は、セールの終わりが近いことをスクリーンリーダーの利用者にも伝えたいと考え、バナーに aria-live="assertive" を付けました。
スクリーンリーダーの利用者がティータオルのページを開き、説明を1行ずつ読みます。JAWS、NVDA、ナレーターでは、行は最後まで読まれ、そのあとに時計が続きます。「手織りのリネンのティータオル。洗いをかけてあり、最初から柔らかな手触りです。02:14:06」。次の行のあとには「02:14:03」。少し手を止めて考えていると、時間が1秒ごとに読まれ続けます。MacやiPadのVoiceOverでは、新しい時間が行を途中で切ります。「手織りのリネンの、02:14:08」と聞こえ、行の残りは読まれません。どちらでも、説明、価格、「カートに入れる」ボタンは、時間と時間の合間に読むことになります。数字の動きで気が散る人にも、止める手段がありません。バナーにはボタンがなく、ページを開いている限り数字は減り続けます。
このバナーには、二つの問題があります。一つは4.1.3です。aria-live="assertive" の領域は、変化のたびにすぐ伝えるようスクリーンリーダーに求め、ARIAは待っていた内容を捨ててもよいとしています。Understanding 4.1.3は、ステータスメッセージを「作業を不必要に妨げない方法で」伝えるべきだとし、重要でも急ぎでもない内容に role="alert" や aria-live="assertive" を使うことを失敗例に挙げています。過ぎたばかりの1秒は、重要でも急ぎでもありません。ただし、スクリーンリーダーがすべて領域の求めどおりに動くわけではありません。2026年のリリースでは、JAWS、NVDA、ナレーターは assertive の領域を polite と同じように扱い、読んでいる文や行が終わってから読みました。割り込んだのは、macOSとiPadOSのVoiceOverです。つまり、変化はすべて読まれ、領域の求めに従うスクリーンリーダーでは、ページの読み上げを押しのけて読まれます。バナーはもともとステータスメッセージではありません。スクリプトが数字を一つ変えるだけなのに、領域がその変化を毎回の読み上げにしています。
もう一つは2.2.2です。カウントダウンは自動更新される情報です。Understanding 2.2.2の言葉では、あらかじめ決めた時間の間隔で更新される内容です。自動で始まり、商品と並んで表示されるため、WCAGは、一時停止、停止、非表示のいずれか、または更新の頻度を調整する手段を求めています。自動更新には、5秒以内なら許されるという例外もありません。Understanding 2.2.2は、こうした内容が「スクリーンリーダーにも問題を起こす」ことがあるとも述べています。この例では、ライブリージョンがまさにそれを引き起こしています。
セールの間はどの商品ページにもバナーがあるため、時間の読み上げは店の中のどこへ行ってもついてきて、セールが終わるまで止まりません。スマートフォンやタブレットでは、スクリーンリーダーの利用者の多くがVoiceOverを使っています(WebAIMの2024年の調査では、モバイルの回答者の70.6%)。先のテストで、macOSとiPadOSで割り込んだスクリーンリーダーです。
10グループ中2グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響なし)
- 届く範囲・力 (影響なし)
- 認知 (影響あり)
- 光過敏 (影響なし)
- 視覚を使わない 1行読むたびに新しい時間が読まれ、VoiceOverでは時間が行を途中で切ります。カウントダウンが動いている間は、商品の説明も、価格も、ボタンも、通して読めません。
- 言語・認知・学習の能力が限られる ページの上で毎秒変わる数字に何度も目を引かれ、止める手段もないため、注意の持続が難しい人は、その下の商品の説明を読むのに苦労します。
スキャンは、最初からページにあり、文字の入ったライブリージョンを見て合格にします。その文字が毎秒変わることは、マークアップのどこにも書かれていません。この診断は、ページを操作する前の数秒間、ページを見ています。assertive の領域の中で文字が自動で3回以上変われば問題として報告し、自動で変わり続ける文字は、どの領域にあっても、人が確認する対象として挙げます。ただし、ページの別の場所にある停止ボタンがその文字を止めるかどうかは分かりません。カウントダウンをアクセシブルにするよう頼まれたAIアシスタントは、aria-live を付けることがあり、このバナーもそうして生まれました。role="timer" と一緒に aria-live="assertive" を付けて、割り込みを元に戻してしまう例もあります。カウントダウンが動いている間にページを聞いてみて、初めて問題に気づきます。どう聞こえるかは、スクリーンリーダーによって違います。
試してみる
スクリーンリーダーの利用者が下矢印キーを押すように「次の行を読む」を押し、セールのカウントダウン中に商品の説明を読んでください。
読み始めると時計が動き出します。下の欄に、JAWS、NVDA、ナレーターの読み方でスクリーンリーダーの読み上げが出て、ページでは読み終えた部分が色付きになります。各行のあとに何が読まれますか? VoiceOverの読み方に切り替えると行が途中で切れる様子も分かります。時計を止める方法も探してみてください。
問題あり 修正できますか?
リネンのティータオル
手織りのリネンのティータオル。洗いをかけてあり、最初から柔らかな手触りです。
幅の狭い織機で1枚ずつ織るため、端がほつれません。
40度で洗濯機洗いができ、干して乾かします。
1枚$18、セール中は3枚で$45です。
スクリーンリーダーの読み上げ:
(「次の行を読む」を押してください)
修正方法
終了時刻を示すか、カウントダウンを role="timer" で静かにし、止めるボタンを付けます。
- 1自動で変わる時計、スコア、カウンターを assertive のライブリージョンに入れない。
- 2自動で変わる文字には止めるボタンを付けるか、固定の時刻を示す。
- 3残り時間を伝えるのは、区切りのよい時点に一度だけ、ステータス領域で。
<p class="sale-banner" aria-live="assertive"> セール終了まで <span id="sale-clock">02:14:09</span></p>一度だけ書く終了時刻
<p class="sale-banner"> セールは <time id="sale-end" datetime="2026-10-31T23:59:00Z">10月31日 23:59</time> まで</p>この方法を選ぶ理由:自動で動くものは、止められるようにする
スクリプトは数字を毎秒変え、その領域は assertive です。そのため、変化が読み上げられます。JAWS、NVDA、ナレーターでは行のたびに、VoiceOverでは行を押しのけて読まれます。ページには、それを止めるものが何もありません。
ページを読み込んだあとは何も変わらないため、読み上げるものも、止めるものもありません。2.2.2が求めることもなくなります。バナーはライブリージョンではないので、スクリーンリーダーは利用者がそこまで来たときに、ほかの行と同じように「セールは10月31日 23:59まで」と一度読みます。time 要素の datetime が正確な時点を持ち、スクリプトが見える文字を訪問者のタイムゾーンで書き直すため、時差を計算する必要もありません。代わりに、数字が刻む動きはなくなります。店によっては、セールの切迫感を出すために使っている動きです。
どちらも4.1.3と2.2.2を満たします。終了時刻は作る手間がかからず、誰にとっても一度読まれるだけで、止めるものもありませんが、数字が刻む動きはなくなります。静かなカウントダウンは刻む動きを残せますが、ボタンと少し多いスクリプトが必要で、ボタンを見つけるまでは誰にとってもバナーが動き続けます。刻む動きがなくても困らない店なら終了時刻を、刻む動きがセールの売り方の一部なら、ボタン付きの静かなカウントダウンを選んでください。
politeにしても直りません。 領域を aria-live="polite" に変えると、VoiceOverが行の途中に割り込むことはなくなります。それでも、どのスクリーンリーダーも行を読むたびに新しい時間を読むため、ページは時間と時間の合間に読むことになります。JAWS、NVDA、ナレーターが assertive の領域ですでにしていることと同じです。Understanding 4.1.3は、ライブリージョンやアラートを使うと、アプリケーションがスクリーンリーダーの利用者にとって「おしゃべり」になりすぎるおそれがあると警告しています。カウントダウンへの答えは、assertive の場合と同じで、時計をライブリージョンに入れないことです。
role="timer" で静かにします。 timer は aria-live が既定で off のライブリージョンで、変化は読み上げられません。aria-live を付け足さないでください。role="timer" aria-live="assertive" は修正前に戻るだけです。aria-live を外し、役割を付けないままでも読み上げは止まります。役割は、変わり続ける数字が何なのかを支援技術に伝えます。中身の前に名前を読むスクリーンリーダーもあります。
大事なら、区切りに一度だけ伝えます。 セールの終わりが近いことを聞く必要は、ほとんどありません。数分だけ確保される予約のように必要な場合は、区切りのよい時点(「残り10分」)に一度だけ role="status" の領域へメッセージを書き込み、時計は静かなままにします。MDNのtimerの例は、残り10秒でタイマー自体の役割を alert に変えていますが、別のステータス領域を使えば、時計をライブリージョンにしないまま、区切りを一度だけ伝えられます。利用者自身の作業にかかる制限時間は別の問題で、警告も延長もないセッションタイムアウトで扱っています。
選んだ状態を保ちます。 止めた状態を sessionStorage などに保存し、訪問者が別の商品ページを開くたびにバナーが動き出さないようにします。
非表示でも構いません。 2.2.2は、一時停止、停止、非表示のどれでもよいとしています。バナーを消す「閉じる」ボタンでも満たしますが、セールの終わりの情報も一緒に消えるため、ページの別の場所に終了時刻を示してください。
スコア、フィード、価格。 自動で変わるものがある場所では、同じ問題が起き、修正の考え方も同じです。
- ライブのスコア:試合の時計は
role="timer"にするかライブリージョンに入れず、スコアは変わったときだけrole="status"の領域に書き込み(「ゴール:Rovers 2、City 1」)、更新を一時停止するボタンを付ける。 - ライブブログやニュースのフィード:新しい項目は polite の
role="log"の領域に追加し、「更新を一時停止」ボタンを付ける。自動で伸びていくフィードも、自動更新される内容にあたる。 - 更新される価格や在庫数:数字をライブリージョンに入れず、更新を止めるか頻度を下げるボタンや設定を付ける。
動きと緊急度は別のトピックです。 横に流れるティッカーは動くコンテンツで、修正は一時停止できない、流れる文字、カルーセル、アニメーションで扱っています。本当に急ぎのメッセージには role="alert" が正しく、その使い分けは定型の通知でもVoiceOverの読み上げを遮るアラートで扱っています。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
デザインの案
セールの終わりの見せ方は一つではありません。
秋のセールがいつ終わるかの見せ方を、店の配色のまま、さらに5つ紹介します。3つは時を刻まずに伝え、2つは自動で変わり続けるのに、そばに止める手段がありません。
デザインファイルで
- カウントダウンを置きたい場所には、セールが終わる日付と時刻を書く。
- 刻むカウントダウンを残すなら、その前に「カウントダウンを止める」ボタンを描き、押したあとのバナーも、時計の代わりに終了時刻を入れて描く。
- カウントダウンには、変わるたびには読み上げないと書き添える。スクリーンリーダーは、利用者がそこまで来たときに一度読む。
合格
- 終了時刻のピル カレンダーのアイコンと日付・曜日・時刻を入れた柔らかなピルです。読み込み後は何も変わらないため、止めるものがなく、スクリーンリーダーもほかの行と同じように一度読むだけです。
- 残り日数のバー 「残り2日」と、セールの経過を示す細いバーです。ページを読み込んだときに一度決まり、開いている間は動きません。数は文字が伝えるため、バーにライブリージョンは要りません。
- 最終日のリボン 最終日にだけ現れ、終了時刻も入ったリボンです。切迫感は時計ではなく「最終日」という言葉から生まれ、変わるのは多くても1日に1回、ページを読み込んだときだけです。
不合格
- めくれる時計のカウントダウンWCAG 2.2.2 駅の案内板のように1秒ごとに数字がめくれる、にぎやかな見せ方ですが、止まることがありません。一時停止、停止、非表示のボタンもありません。見本は4回めくれると止まりますが、この見せ方そのものは止まりません。
- 分単位のカウントダウンWCAG 2.2.2 「セール終了まで2時間14分」は落ち着いて見えますが、商品の横で1分ごとに自動で変わります。2.2.2は、更新が遅いことを自動更新の例外にしていません。秒単位の時計と同じく、止めるボタンが必要です。
基本原則:自動で動くものは、止められるようにする
修正の確認
マウスを使わない4つの確認
確認方法: スクリーンリーダー、キーボード
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。バナー、そのスクリプト、セールのコンポーネントを変更した際は、再度確認します。
制限
ライブリージョンの動きは、スクリーンリーダーやブラウザーによって、またバージョンによっても異なります。このガイドの記述は、2026年時点の各リリースでの動きです。Understanding 4.1.3も、利用者の設定によっては、支援技術がステータスメッセージを遅らせたり、抑えたり、形を変えたりすることがあると述べています。このガイドは、一つのページで、ほかの内容と並ぶカウントダウンを扱っています。オークションの時計のように、更新が活動に欠かせないかどうかはチームの判断で、その場合2.2.2は止める手段を求めません。それでも、時計が毎秒読み上げる必要はありません。利用者が使うスクリーンリーダーで確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
参考資料
- WCAG Understanding: status-messages(新しいタブで開きます)
- WCAG Understanding: pause-stop-hide(新しいタブで開きます)
- WCAG Technique G186(新しいタブで開きます)
- w3.org: wai-aria-1.2(新しいタブで開きます)
- developer.mozilla.org: timer_role(新しいタブで開きます)
- developer.mozilla.org: aria-live(新しいタブで開きます)
- developer.mozilla.org: time(新しいタブで開きます)
- webaim.org: screenreadersurvey10(新しいタブで開きます)
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。