時計UIの設計:Intl.DateTimeFormatとモノスペース・アクセシビリティ
Web上でデジタル時計を表示するとき、「数字がカクカク動かない」「スクリーンリーダーで読み上げられる」「視認距離10mでも読める」UIを作るための実装観点を整理します。ECMA-402 Intl.DateTimeFormat、等幅フォント、WCAG 2.2の基準を解説。
ECMA-402 Intl.DateTimeFormat の使い方
ブラウザに標準実装されているIntl.DateTimeFormatは、ロケール別の時刻書式化APIです。
- ・仕様:ECMA-402 Internationalization API Specification
- ・主要オプション:hour12, hourCycle(h12/h24/h23/h11), timeZone, hour, minute, second
- ・日本ロケール例:new Intl.DateTimeFormat("ja-JP", {hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false})
- ・タイムゾーン指定:timeZone:"Asia/Tokyo"でTZDB(IANAタイムゾーンデータベース)に従う
自前で「Date.getHours()を2桁ゼロパディング」のような処理を書くより、Intl.DateTimeFormatを使うほうがロケール対応・将来の仕様変更に強くなります。
数字が踊らない:等幅フォントとタブラー数字
時計の数字が秒ごとに微妙に動いて落ち着かない経験はありませんか。これは多くの欧文プロポーショナル数字(プロポーショナルナンバー)が幅可変だからです。
- ・tabular-nums:CSSの font-variant-numeric: tabular-nums; で固定幅数字を強制
- ・monospace系フォント:font-family に "ui-monospace", "SF Mono", "Consolas" などを指定
- ・OpenType機能:font-feature-settings: "tnum" 1; でも同じ効果
- ・パッディング:HTMLで <span>14</span>:<span>30</span> のように区切るより、文字列全体に tabular-nums を当てるほうが楽
OS標準フォントだけでもUIフォント(SF Pro, Segoe UI, Inter, BIZ UDPGothic)はtabular-nums対応のものが多いので、明示的にCSSで指定すれば安定します。
アクセシビリティ:aria-liveとaria-label
視覚障がい者がスクリーンリーダーを使うとき、時計が変化するたびに延々と読み上げると邪魔になります。WAI-ARIAでは次の方法が推奨されます。
- ・aria-live="off":時計表示エリアは原則「変化を読み上げない」設定が望ましい
- ・aria-label="現在時刻":要素自体にラベルを付け、フォーカス時に1回だけ読まれる
- ・aria-atomic="true":変化があったとき要素全体を1単位として読む
- ・role="timer":カウントダウンタイマーには role="timer" を付与
時計はあくまで参考表示なので、フォーカス可能要素にはせず、隣に「時刻を読み上げる」ボタンを別に置く設計のほうが親切なケースもあります。
WCAG 2.2のコントラスト比基準
会議室や教室で離れた席から読む時計は、視認性が命。W3CのWCAG 2.2は次のコントラスト比基準を定めています。
- ・SC 1.4.3(AA・通常テキスト):背景とのコントラスト比 4.5:1 以上
- ・SC 1.4.3(AA・大きいテキスト):18pt以上または14pt太字なら 3:1 以上
- ・SC 1.4.6(AAA):通常 7:1 以上、大きい 4.5:1 以上
- ・暗い画面モード:白文字・黒背景は最大級コントラスト 21:1 で目に優しい
時計の数字は大きいため大半は3:1基準で済みますが、夜間や寝室では暗い画面モードに切り替えると、まぶしさを避けつつコントラスト基準を満たせます。本サイトのデジタル時計は暗い画面切替を備えています。
setInterval(60_000) vs requestAnimationFrame
時計の更新タイミングは、表示精度と電力消費のトレードオフがあります。
| 手法 | 更新間隔 | 使い分け |
|---|---|---|
| setInterval(60_000) | 毎分 | 秒非表示時は省電力。タイマー誤差最大1分 |
| setInterval(1000) | 毎秒 | 秒表示の定番。負荷ほぼゼロ |
| setInterval(250) | 4回/秒 | 秒の境界ズレを最小化したい場合 |
| requestAnimationFrame | 画面リフレッシュごと | アナログ時計の秒針アニメ向き |
ノートPCやスマホでは、不要なrequestAnimationFrameはCPUとバッテリーを浪費するので、デジタル表示なら1秒間隔で十分です。