1 日を 1 本の帯にして、時間帯を面で、節目を印で、いまの時刻とつまみの位置を重ねて見せるUIコンポーネントです。日の出と日の入、当番の交代、営業時間、稼働の山と谷のように、「何時か」ではなく「1日の中のどこか」を読ませたいときに使います。
この記事では、横幅の全体が何を表すかを「器」と呼びます。この帯の器は 1 日です。読み終えると、器が 1 日に固定されると何が決まるか、4 つの層をどの強さで重ねるか、ラベルと時刻の数字を別の行に置く理由、指で触ったときに時刻を確定させるタイミングを自分で判断できるようになります。
1. UI例(Preview / Live)
実装で見る(GunjoUI)
この部品を、デザインシステム GUNJO の実装で確かめられます。
2. 定義(Definition)
Day Band は、横幅の全体がちょうど 1 日を表す帯です。帯の中には 4 つの層が重なります。
- 時間帯(面):夜、薄明(空が明るみ始める時間帯)、昼のように、ひとまとまりの区間を下地として塗る
- 節目(印):日の出、交代、ラストオーダーのように、ある時刻に立てる縦の線とラベル
- いま(印):現在時刻の位置
- つまみ(操作):読み手が動かせる位置
この 4 つが層です。ほかに、帯の中に立てる目盛り(等間隔の短い線)と、帯の下に並ぶ時刻の数字が付きます。この 2 つは層とは別で、どちらも外せます。
Slider との違い:Slider は最小値と最大値のあいだで値を 1 つ選ぶコンポーネントで、横幅が何を表すかは決まっていません。Day Band の器は 1 日に固定されていて、位置がそのまま時刻を意味します。だから「25% の位置」ではなく「06:00」として読まれます。その位置が夜なのか昼なのかも、同時に読めます。
Calendar との違い:Calendar は日を選ぶコンポーネントです。Day Band は 1 日の中を動くので、日をまたぐには呼び出し側が帯の外側で日を入れ替えます。
Gantt やタイムラインとの違い:複数の行に区間を並べる図は、複数の対象を比べるためのものです。Day Band は 1 本で、対象は 1 つです。
このコンポーネントが計算しないこと:日の出の時刻も、現在時刻も、コンポーネントの中では計算されません。緯度経度から日の出を出すのは呼び出し側のデータの都合で、コンポーネントの中に天文の計算を入れると、同じ形を当番表や営業時間に使えなくなります。
3. 使い分け(When to use / When NOT to use)
3.1 When to use
- 1 日の中の位置を読ませたい:何時かよりも、朝のうちか、日が落ちた後かを知りたい画面
- 時間帯にまとまった意味がある:夜勤と日勤、営業時間と閉店後、混み合う時間と空いている時間
- 節目が判断を変える:日の入の前に着くか、交代の前に申し送りを終えるか
- 1 日の中を行き来したい:過去のある時刻の状態を見に行き、いまへ戻ってくる
- 狭い場所に 1 日を収めたい:表の行の中、カードの下端
3.2 When NOT to use
- 日が複数ある:1 本の帯は 1 日です。週や月を見せたいなら別の形の図にします
- 時間帯にまとまりが無い:塗る面が無いなら、帯の形をとる必要がありません
- 正確な時刻を入力させたい:帯は位置で選ぶので、分単位の入力には向きません
- 対象が複数ある:3 部署の当番を比べたいなら、行を並べる図にします
- 区間そのものを編集させたい:開始と終了を動かすのは別の操作です
3.3 代替UI(Alternatives)
- 値を 1 つ選ぶだけでよい : Slider(スライダー)
- 日を選ばせたい : Calendar(カレンダー)
- 日が複数ある : Heatmap Chart(ヒートマップ), Table(テーブル / 表)
- 1 日の中の量の変化を読ませたい : Line Chart(折れ線グラフ), Bar Chart(棒グラフ)
- 時間を動かす操作盤が欲しい : Time Transport(時間の操作盤)
4. 設計判断の核(Decision Principles)
この帯の核は「器が 1 日に固定されていること」。だから位置は割合ではなく時刻として読まれ、0 時をまたぐ区間は 2 つに割ることになる。
判断は 7 つあり、以下の項目はこの順に並びます。1. 器を 1 日に固定する、2. 4 つの層に強さの差を付ける、3. つまみを 2 色で描く、4. ラベルと数字の行を分ける。5. 触ったときに時刻を確定させるタイミングを決める、6. 指で触る帯での動かし方を決める、7. 単位を 1 つに決める。
- 1 本の帯は 1 日。0 時をまたぐ区間は 2 つに分ける:夜勤のように 22 時から 6 時まで続く区間は、22 時から 24 時までと、0 時から 6 時までの 2 つとして渡します。帯の反対側に黙って回り込ませると、読み手は「この面はどこから続いているのか」を推測することになります。GUNJO(群青)は UIXHERO が作っているデザインシステムで、その
DayBandは、開始時刻が終了時刻と同じかそれより後になっている区間を描かず、作っている最中に警告を出します。黙って帯の反対側を塗るのではなく、渡し方が違うと作る人に知らせるほうが正しいという判断です。 - 4 つの層に、強さの差を付ける:時間帯は下地で、その上に節目、いま、つまみが乗ります。上のデモで層を 1 つずつ足すと分かるように、4 つを同じ強さで描くとどれも読めません。色は、濃淡 2 色で 1 組になって用意されています(この組を「トークン」と呼びます)。時間帯の面には、その組の薄いほうを使います。意味を示す 3 色(正常を表す緑、注意を表す黄、異常を表す赤)の濃いほうをそのまま敷かないようにします。下地が主役になると、その上の印が沈みます。
- つまみは 2 色で描く:下地の時間帯は明るい面にも暗い面にもなります。つまみを前景色 1 色で描くと、夜の帯か昼の帯のどちらかで消えます。背景色の線に前景色の縁を重ねると、明暗が逆の 2 色が並ぶので、下地がどちらでも片方は見えます。だから、どちらの面の上でも輪郭が残ります。上のデモの下段で、消える側と残る側を並べています。
- 印のラベルは帯の上、時刻の数字は帯の下:この帯は、もともとある画面のために作られたものを、使い回せる形に整えたものです。その元の実装では、節目のラベルと時刻の数字が 1 つの行に詰まっていました。そのため、印から 48 ピクセル以内にある時刻の数字を消して衝突を避けていました。行を分ければ、消す必要がありません。情報を消して解決していた問題が、置き場所を変えるだけで消えるという形です。上のデモで「1 行に詰める」を押すと、消す判断が必要になる状態が見られます。
- 指で触ったときは、押した瞬間に動かさない:帯の上に置いた指は、そのまま縦スクロールに変わることがあります。押した瞬間に時刻を確定させると、ページをスクロールしようとしただけで時刻が飛びます。動かしたとき、または指を離したときに確定させます。マウスとペンは押した瞬間で構いません。帯の上から始まった縦スクロールも止めないようにします(
touch-action: pan-y)。 - 指で触る帯では、触った位置ではなく掴んだ距離で動かす:推奨の高さである 44 ピクセルの帯でも、置いた指は現在位置を覆います。触った位置をそのまま時刻にすると、いまどこにいるかを見ないまま飛ぶことになります。掴んだ距離ぶんだけ動かす方式にすると、現在位置を基準に寄せられます。この方式では、動かした先が 0 時より前や 24 時より後になることがあります。そのときは帯の外の値がそのまま返るので、受け取った側で 0 時より前なら前日、24 時より後なら翌日として読み替えます(0 時をまたぐ区間を 2 つに割るのは面の塗り方の決まりで、こちらとは別の話です)。
- 単位を 1 つに決める:時間帯、節目、つまみ、いま、帯の両端を、すべて同じ単位で書きます。既定は午前 0 時からの分(0 から 1440)です。実際の日付で使うなら、その日の 0 時のエポックミリ秒と、それに 1 日分を足した値を渡します。コンポーネントは単位を知らないので、混ぜると画面は崩れずに黙ってずれます。
5. 状態設計(States)
5.1 必須状態(Required)
- 読むだけ:時間帯と節目が出ていて、つまみが無い
- 操作できる:つまみがあり、押すかキーで動かせる
- 操作できない:帯全体が無効。タブ移動からも外れている
5.2 条件付き状態(Conditional)
- いまが帯の中にある:いまの印が出る。今日の帯を見ているとき
- いまが帯の外にある:過去や未来の日の帯。いまの印は出しません
- つまみといまが重なる:2 つの印が同じ位置に来る。どちらも判別できる形にします
- 時間帯が渡されていない:面が無い。目盛りと節目だけの帯になります
- フォーカスされている:キーで動かせる状態。輪郭を下地の明暗の両方で見える形にします
5.3 State Gallery
| 状態 | 必須 | 何を伝えるか |
|---|---|---|
| 読むだけ | 必須 | 1 日の形(時間帯の割合と節目の位置) |
| 操作できる | 必須 | この帯の中を動かせる |
| 操作できない | 必須 | いまは動かせない |
| いまが帯の中 | 条件付き | 読み手の現在位置 |
| いまが帯の外 | 条件付き | 今日ではない日を見ている |
| 時間帯が渡されていない | 条件付き | 目盛りと節目だけで 1 日の位置を示す |
| つまみといまが重なる | 条件付き | 選んでいる時刻が、いまである |
| フォーカスされている | 条件付き | キーで動かせる |
6. バリエーション設計(Variants)
バリエーションは、帯の高さ、渡した層の数、触ったときの動き方、操作を受け取るかどうか、置き場所の 5 つで決まります。中央の列は、GUNJO のコンポーネントでの指定です。
| バリアント | GUNJO の指定 | 典型的な用途 |
|---|---|---|
| 標準 | size="default"(44 ピクセル) | 指で触る画面。主役として置くとき |
| 小 | size="sm"(24 ピクセル) | 表の行の中、カードの下端。読むだけの帯に使う |
| 読むだけ(5.1 の「読むだけ」) | onValueChange を渡さない | 営業時間の表示。フォーカスも持ちません |
| 読むだけ・つまみも無し(5.1 の「読むだけ」) | value も渡さない | 時間帯の形だけを見せる |
| 掴んで流す | scrub="relative" | 指で触る画面(44 ピクセルの帯でも指が位置を覆うため) |
| 触った位置へ | scrub="absolute"(既定) | マウスとペンの画面 |
| 操作できない(5.1 の「操作できない」) | disabled | 読み取り専用、または対象が未決定。タブ移動からも外れます |
| いちばん小さい形 | 節目といまの印、時刻の数字、目盛りを外す | 表の行の中。時間帯の面だけを 1 行に収める |
| 操作盤の中 | 時間の操作盤(Time Transport)の、帯を置く枠に差し込む | 掴んで動かす面として |
読むだけの帯では、フォーカスも持たせない:操作を受け取らない帯にタブで止まれると、読み手は動かせると思って何度か押します。動かないと分かるまで無駄な操作が続くので、渡さなかったものは出さない作りにします。
禁止パターン:時間帯に意味を持つ色(異常の赤など)を、そのまま濃い面として敷くこと。下地が主役になり、その上の節目とつまみが読めなくなります。
7. パターン集(Good / Bad / How to fix)
7.0 よく崩れる設計パターン(4つ)
- ラベルと数字が 1 行に詰まっている:衝突するので、どちらかを消す判断が必要になります
- つまみが 1 色:明るい面か暗い面のどちらかで消えます
- 0 時をまたぐ区間を 1 つで渡している:帯の反対側に回り込み、どこから続いているのか読めません
- 押した瞬間に時刻を確定させている:ページをスクロールしようと指を置いただけで、時刻が飛びます
7.1 Bad(典型4つ)
- 「日の出 05:22」のラベルが時刻の数字と同じ行にあり、0 と 6 が隠れている
- つまみが濃い前景色 1 色で、夜の帯の上では見えない
- 夜勤(22 時から 6 時)を 1 つの区間として渡し、帯の左端と右端に分かれて塗られている
- 帯をスクロールしようと指を置いたら、時刻がその位置に飛んだ
7.2 Good(対になる4つ)
- 節目のラベルを帯の上の行、時刻の数字を帯の下の行に置く
- つまみを背景色の線に前景色の縁で描き、どちらの面でも輪郭を残す
- 夜勤を「22 時から 24 時」と「0 時から 6 時」の 2 つの区間として渡す
- 指では、動かしたときか離したときに時刻を確定させ、縦スクロールは止めない
7.3 How to fix(手順)
1 から 4 が上の 4 つの直しで、5 から 7 はあわせて確認することです。
- 節目のラベルを帯の上の行へ移し、時刻の数字を帯の下に残す
- つまみを背景色と前景色の 2 色で描き直す
- 0 時をまたぐ区間を 2 つに割って渡す
- 指の操作を、押した瞬間ではなく動かしたときか離したときに確定させる
- 時間帯の色を薄い側のトークンへ落とし、印とつまみが上に乗る関係にする
- 帯に
touch-action: pan-yを入れ、帯の上から始まる縦スクロールを止めない - 読み上げ用の文字に、時刻だけでなく時間帯の名前を入れる
8. ルール(Must / Better)
Must(守らないと壊れる)
Better(品質が跳ねる)
9. アクセシビリティ要件(必須)
この帯は、面の色に意味を載せやすく、操作の当たり判定が細くなりやすいコンポーネントです。崩れ方は「色だけに頼る」「キーで動かせない」「読み上げに時刻しか届かない」「指で触ると時刻が飛ぶ」の 4 つで、9.1 から 9.4 がその 4 つに当たります。9.5 は文字と線の読みやすさの補足です。
9.1 色だけに頼らない
- 時間帯の名前を文字で渡す:読み上げ用の文字(
aria-valuetext)に「10:40 昼」のように時間帯の名前を入れます。時刻だけを読ませると、色で分かっていたことが音では消えます - 節目はラベルを持たせる:縦の線だけの印は、線の意味が画面から読めません
- 色以外にも時間帯の名前を置く:時間帯の面に名前を持たせ、マウスを乗せたときに読めるようにします。指で触る画面ではマウスを乗せられないので、帯の外(label か hint の行)に、いま選んでいる時間帯の名前を文字で出します
- コントラスト:つまみと下地のコントラスト比は 3:1 以上を確保します。2 色で描くのは、この基準を明るい面と暗い面の両方で満たすためです
9.2 Keyboard
見た目と意味は Slider と違いますが、キーボードと読み上げへの伝え方は Slider と同じ仕組み(role="slider")に載せます。読み手にとっては「1 本の線の上で位置を選ぶ」操作が同じだからです。こうして、触らずに 1 日を歩けるようにします。
| キー | 動き |
|---|---|
| 左右の矢印 | 細かい刻み(既定は 15 分) |
| Shift と矢印 | 粗い刻み(既定は 1 時間) |
| PageUp / PageDown | 粗い刻み |
| Home / End | 1 日の始まりと終わり |
細かい刻みは画面の用途で決めます。稼働の確認なら既定の 15 分で足ります。当番の交代が 30 分単位なら、細かい刻みを 30 分にすると、目的の時刻へ少ない操作で着きます。
9.3 Screen Reader
<div class="day-band" role="group" aria-labelledby="band-label">
<p id="band-label">東京 9月12日</p>
<div
class="day-band__track"
role="slider"
tabindex="0"
aria-valuemin="0"
aria-valuemax="1440"
aria-valuenow="640"
aria-valuetext="10:40 昼"
>
<!-- 面と印は装飾。意味は valuetext と下のリストが持つ -->
<span class="day-band__phase" aria-hidden="true"></span>
<span class="day-band__mark" aria-hidden="true"></span>
</div>
<!-- 節目は文字としても置く -->
<ul class="day-band__marks">
<li>日の出 05:22</li>
<li>日の入 17:56</li>
</ul>
</div>
aria-valuetext に時間帯の名前を入れるのが、この帯でいちばん効く 1 行です。時刻だけを読ませると、目で見えていた「いまは昼のうち」という情報が音では届きません。
節目は帯の中の印としてだけ持たせず、文字のリストとしても置きます。印の位置は目で読むもので、音では位置が伝わらないためです。
9.4 Touch / Pointer
- 触る対象の高さは 44 ピクセル以上にします。小さい形(24 ピクセル)は、読むだけの帯のためのもので、マウスでも操作させません
- 押した瞬間には確定させません。指がそのまま縦スクロールに変わることがあるためです
touch-action: pan-yを入れ、帯の上から始まった縦スクロールを止めないようにします- 指で触る画面では、掴んだ距離ぶんだけ動かす方式にします。44 ピクセルの帯でも、置いた指が現在位置を覆うためです
9.5 Contrast / Readability
- 時間帯の面は下地なので、面と面の境目が見える程度の差で足ります。濃くすると上の層が沈みます
- 節目の線は 1 ピクセルで足りますが、ラベルの文字色は背景とのコントラスト比 4.5:1 以上を確保します
- 時刻の数字は帯の下に置くので、帯の色に影響されません。ここは通常の本文と同じ基準で読めます
10. 実装メモ(Implementation Notes)
ここからは、実際に組む人向けの覚え書きです。
- GUNJO の
DayBandは、帯の両端、時間帯、節目、つまみ、いまのすべてを同じ単位の数で受け取ります。既定は午前 0 時からの分(0 から 1440)です - 時間帯に、注意・異常のような意味の名前を渡すと、濃い色ではなくその組の薄いほうが使われます。空の色のように絵としての色が要るときは、任意の CSS 色をそのまま渡せます
- いまの時刻を渡すとき、
Date.now()を描画中に読まないようにします。サーバーでの描画と最初のクライアント描画が食い違うためで、画面が出たあとに走る処理の中で読みます - 渡さなかったものは描かれません。つまみの位置が無ければ読むだけの帯になり、変更を受け取る関数が無ければフォーカスも持たず、いまの位置が無ければいまの印も出ません
- 始まりが終わり以上の区間は描かれず、開発中に警告が出ます。0 時をまたぐ区間は 2 つに分けて渡します
- 時間を動かす操作盤の、掴んで動かす面として差し込めます。そのときの単位は帯側の単位(分など)に合わせるので、操作盤側の「いまとみなす許容差」(「いま」からこれだけ以内なら実時間として扱う、という値)も同じ単位で渡します
- 実装は GUNJO の DayBand(gunjo.jp を新しいタブで開く) にあります。日の出の帯、当番表、営業時間、掴んで流す形、いちばん小さい形の見本が置かれています
11. 関連リンク
- 関連するUIデザイン原則: ユーザーコントロール (User Control), 色に依存しない設計 (Color Independence), Fittsの法則とタッチターゲット (Fitts's Law), 情報密度の最適化 (Information Density)
- 用語集(定義): カラーユニバーサルデザイン (Color Universal Design), タッチターゲット (Touch Target), メンタルモデル (Mental Model)
- 関連するUIコンポーネント(横): Slider(スライダー), Time Transport(時間の操作盤), Calendar(カレンダー)
12. まとめ
この帯の設計でいちばん効くのは、器が 1 日に固定されていることを設計の前提として扱うことです。迷ったら 4. 設計判断の核 に戻り、「0 時をまたぐ区間を割っているか」「4 つの層に強さの差があるか」「つまみが両方の面で見えるか」の 3 点を確かめてください。
器が決まっているぶん、位置は割合ではなく時刻として読まれます。その上に節目、いま、つまみが重なるので、下地を強くすると上が沈みます。そして印のラベルと時刻の数字は、行を分けるだけで衝突が消えます。情報を消して避けていた衝突は、置き場所の問題だったということです。