UIXHERO

Calendar(カレンダー)

日付の選択・予定の表示・期間の選択を行う高度な複合UIコンポーネント。Date Picker・Event Calendar・Date Range Pickerの3種類の使い分け・キーボードナビゲーション・aria-labelの実装を解説する。

2026年3月3日
更新: 2026年9月3日
17
by Dengen Yosho(DGYS)

日付の選択・予定の表示・期間の選択を行う高度な複合コンポーネント。Date Picker(単一日付選択)・Event Calendar(予定表示)・Date Range Picker(期間選択) の3種類がある。日付という「時間の概念」をグリッドでし、ユーザーが直感的に日付を認識・選択できるようにする。予約システム・スケジューラ・フォームの日付入力など幅広い場面で使われる。

この記事を読むと、3種類のCalendarの使い分け・月ナビゲーションの設計・Date Range Pickerの開始/終了日の管理・キーボードナビゲーション(矢印キー移動)・role="grid"aria-selected の実装が自分でできるようになります。


1. UI例(Preview / Live)

実装で見る(GunjoUI)

この部品を、デザインシステム GUNJO の実装で確かめられます。

2. 定義(Definition)

日付を月単位のグリッドで視覚化し、選択・閲覧・入力を可能にする複合UIコンポーネント。内部的にはグリッド(7列 × 最大6行)・ボタン・曜日ヘッダー・日付セルの4つのサブコンポーネントで構成される。

3種類のCalendar

種類目的選択使用例
Date Picker単一日付の選択1日フォームの誕生日・期日入力
Date Range Picker期間の選択開始〜終了ホテル予約・レポート期間
Event Calendar予定の閲覧・表示なし/単一スケジューラ・カレンダーアプリ

3. 使い分け(When to use / When NOT to use)

3.1 When to use

  • Date Picker:フォームで日付の入力が必要な場面(予約日・生年月日・期日)
  • Date Range Picker:期間の指定が必要な場面(宿泊期間・フィルター期間・レポート範囲)
  • Event Calendar:予定・イベントをカレンダービューで閲覧する場面

3.2 When NOT to use

  • 年・月のみの選択<input type="month"> またはカスタムのMonth Pickerの方が適切
  • 遠い過去・未来の日付入力(例:生年月日の1980年代):テキスト入力または年・月・日のセレクト3つの方が操作が速い
  • 日付入力が省略できる場合:カレンダーは重いUIなので、日付入力が必須でなければ省略する

4. 設計判断の核(Decision Principles)

Calendarの核は「キーボードナビゲーション」——カレンダーはマウスで操作するUIに見えるが、矢印キーで日付を移動・選択できることがアクセシビリティの要件。これなしではキーボードユーザーとスクリーンリーダーユーザーが完全に排除される。

判断の優先順位:① キーボードナビゲーション(矢印キー) → ② role="grid" + aria-selected → ③ 月ナビゲーションの見出し aria-live → ④ 無効日付の設計

  • 矢印キーで日付を移動できるようにするonKeyDownArrowLeft/Right/Up/Down を処理し、それぞれ前日/翌日/1週間前/1週間後にフォーカスを移動する。月をまたぐ移動も適切に処理する
  • role="grid" + 各セルに role="gridcell" + aria-selected:カレンダーグリッド全体に role="grid" を付与し、日付セルに role="gridcell" + aria-selected="true/false" を設定する。aria-label で「2026年3月5日」のように日付を読み上げられるようにする
  • 月ナビゲーション後に aria-live="polite" で月を読み上げる:「前の月」「次の月」ボタンを押した後、月の見出しが変わったことをスクリーンリーダーに伝える。見出し要素に aria-live="polite" を設定する
  • 無効日付(past dates / disabled dates)を aria-disabled="true":過去の日付や選択不可の日付には aria-disabled="true" を設定し、視覚的に非する。pointer-events: none と合わせて実装する

5. 状態設計(States)

5.1 日付セルの状態

状態視覚的表示aria
通常デフォルトスタイルaria-selected="false"
今日リング・太字aria-current="date"
選択済み青塗りaria-selected="true"
範囲内(Range)薄い青aria-selected="true"
無効グレーアウトaria-disabled="true"
当月外薄い表示role="presentation"
フォーカスフォーカスリングtabindex="0"

5.2 月ナビゲーション

  • 前月・次月ボタン:aria-label="前の月" / aria-label="次の月"
  • 月の見出し:aria-live="polite" で月変更を通知

6. バリエーション設計(Variants)

バリアント選択モード特記事項
Single Date Picker1日フォームに組み込む場合は Popover と組み合わせる
Date Range Picker開始〜終了開始日・範囲内・終了日で3種類の視覚スタイルが必要
Multi-select複数の任意の日シフト管理・複数日程選択
Event Calendar(月表示)なし/単一セル内に予定ドット・タイトルを表示
Event Calendar(週表示)なし/単一時間軸グリッドで時間帯ごとに表示

7. パターン集(Good / Bad / How to fix)

7.0 よく崩れる設計パターン(3つ)

  • マウス専用のカレンダーonClick のみ実装し、キーボードで日付を移動・選択できない
  • 月ナビゲーション後にフォーカスが消える:「次の月」ボタンを押した後、フォーカスがどこかに飛んでしまう
  • Date Range Pickerで選択中状態が不明:開始日をクリック後、終了日をクリックするまで何が選択されているか分からない

7.1 Bad(典型3つ)

  • 日付セルが <div onClick> で実装されており、Tabキーでフォーカスできず矢印キーでも移動できない
  • role="grid" がなく、スクリーンリーダーが単なるテキストの羅列として読み上げる
  • Date Range Pickerで開始日クリック後、終了日にマウスを乗せても選択範囲がプレビューされない

7.2 Good(対になる3つ)

  • グリッドに role="grid" + 各セルに role="gridcell" tabIndex={focusedDate === date ? 0 : -1}rovingTabIndex を実装し、矢印キーで移動できる
  • 月ナビゲーション後、月の見出しに aria-live="polite" を付与し、「2026年4月」と読み上げられる
  • Range選択中、マウスホバーした日付まで選択範囲をプレビュー表示し「どこまで選択されるか」を可視化する

7.3 How to fix(手順)

  1. 日付セルの <div><button> に変えるか role="gridcell" tabIndex を設定して、キーボードフォーカス可能にする
  2. グリッドに role="grid" + 月見出しに aria-live="polite" を追加する
  3. onKeyDownArrow キーの処理を実装し、フォーカスを移動する
  4. Range選択中の onMouseEnter でプレビュー状態を管理する

8. ルール(Must / Better)

Must(守らないと壊れる)

Better(品質が跳ねる)


9. アクセシビリティ要件(必須)

Keyboard Navigation

キー動作
/ 前日 / 翌日
/ 1週間前 / 1週間後
Enter / Space日付を選択
PageUp / PageDown前月 / 次月
Home / End月の最初 / 最後の日

Screen Reader

<div role="grid" aria-label="2026年3月のカレンダー">
  <div role="row">
    <div role="columnheader" abbr="日曜日">日</div>
    <!-- ... -->
  </div>
  <div role="row">
    <div
      role="gridcell"
      aria-label="2026年3月1日"
      aria-selected="false"
      tabindex="-1"
    >1</div>
    <div
      role="gridcell"
      aria-label="2026年3月2日、今日"
      aria-current="date"
      aria-selected="true"
      tabindex="0"
    >2</div>
  </div>
</div>

10. 実装メモ(Implementation Notes)

  • shadcn/ui の Calendarreact-day-picker ライブラリのラッパー。キーボードナビゲーション・AR対応・Date Range Pickerがすべて含まれており、カスタムCalendarを1から実装するより推奨される
  • react-day-pickermode="single" / mode="range" / mode="multiple" で3種類の選択モードを切り替えられる
  • フォームと組み合わせる場合は Popover + Calendar のパターンが一般的(shadcn/ui のDate Pickerの実装例がこのパターン)。Popover内にCalendarを配置し、選択後にPopoverを閉じてフォームフィールドに値を反映する
  • キーボードナビゲーションを自前実装する場合:rovingTabIndex パターンを使い、フォーカス中の日付だけ tabIndex={0}、それ以外は tabIndex={-1} にすることで、Tabキーでカレンダー全体を1つのウィジェットとして扱える

11. 関連リンク


12. まとめ

Calendarの設計で最重要なのは「キーボードナビゲーション」と「role="grid" + aria-selected のARIA実装」です。Calendarはビジュアル重視になりがちですが、矢印キーで日付を移動できることはが要求する最低限の実装です。shadcn/ui の Calendarreact-day-picker ベース)を使うと、これらのアクセシビリティ要件がすべて実装済みのため、特別な理由がない限りは自前実装よりライブラリの活用を推奨します。

更新のお知らせ

サイトに載せていない実例や、新しい記事のお知らせはこちらで出しています。

読んだ内容を、自分の画面に当てるとき

UIXHEROは、記事を書くほかに、画面の検品・判定、デザインシステムの構築、実装と改善の伴走を受けています。何を頼めばいいか決まっていない段階の相談も、同じ窓口で受けます。

UIXHEROに頼めることを見る

※ 記事の内容についての質問や、書いてほしいテーマの要望も同じ窓口で受けています

記事をシェア

メールでお知らせを受け取る

最終更新: 2026年9月3日

この記事を書いた人

Dengen Yosho(DGYS)

Dengen Yosho(DGYS)

UI/UXデザイナー兼デザインエンジニア デザイン歴25年以上 情報設計〜UIデザイン〜React/TypeScriptによる実装までを一気通貫で担当 大手企業のDXプロジェクトや決済アプリ領域で、デザインリードとして改善と構築を経験 現在はフリーランスとして、プロダクトのUI/UX改善とデザインシステム構築を支援 長年の実務の中で感じてきたのは、 「良いUI」はセンスではなく、再現可能な判断の積み重ねであるということ UIXHEROは、 心理学や行動科学の知見を、現場で使える設計言語に翻訳するための場所 関心領域は「ユーザーの行動が変わるUI」 人の認知・注意・意思決定の構造を理解し、 “なぜそのUIが機能するのか”を言語化することを目指している

あわせて読みたい

この記事に関連する記事をご紹介します

Bar Chart(棒グラフ)

カテゴリごとの量を棒の長さで比べるチャート。ゼロ基線の扱い、並び順、縦横の選び方、しきい値の見せ方という設計判断と、色だけに頼らないアクセシビリティ要件を解説する。

2026年8月27日
18

Donut Chart(ドーナツチャート)

中央をくり抜いた円で構成比を示すチャート。中央に置く値の選び方、円グラフとの使い分け、リングの太さ、凡例の作りという設計判断と、色だけに頼らないアクセシビリティ要件を解説する。

2026年8月27日
17

Gauge Chart(ゲージチャート)

1つの値を範囲の中に置いて示す半円のチャート。範囲の両端に意味があるかという条件、しきい値の帯の設計、色だけで良否を伝えない書き方という設計判断とアクセシビリティ要件を解説する。

2026年8月27日
16

もっと深く知りたいですか?

ここに掲載されていないトピックについても、リクエストがあれば解説記事を追加します。 わかりにくい点や、具体的な事例について知りたいことがあれば教えてください。

リクエストを送る