表示例
年/月/日 で入力
2026年9月
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
アイコン: Font Awesome 7 Free(fa-solid / fa-regular)
別名・英語表記
- カレンダー入力
- デートピッカー
- 日付選択
- カレンダーピッカー
- 日付入力欄
- カレンダーから選ぶ
- Date picker
実装例
<div class="x-date-picker">
<div class="x-date-picker__top">
<label class="x-date-picker__l" for="x-date-picker-i">予約日</label>
<span class="x-date-picker__fmt" id="x-date-picker-fmt">年/月/日 で入力</span>
</div>
<div class="x-date-picker__row">
<input class="x-date-picker__i" id="x-date-picker-i" type="text" name="date" inputmode="numeric"
value="2026/09/28" aria-describedby="x-date-picker-fmt">
<button class="x-date-picker__b" type="button" aria-label="カレンダーから選ぶ"
aria-haspopup="dialog" aria-expanded="true" aria-controls="x-date-picker-d">
<i class="fa-solid fa-calendar-days" aria-hidden="true"></i>
</button>
</div>
<!-- 見本なので開いた状態。実際は開いたときだけ表示し、aria-modal="true" を付けてフォーカスを選択中の日へ移す -->
<div class="x-date-picker__d" id="x-date-picker-d" role="dialog" aria-labelledby="x-date-picker-h">
<div class="x-date-picker__nav">
<button class="x-date-picker__m" type="button" aria-label="前の月">
<i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
</button>
<h2 class="x-date-picker__h" id="x-date-picker-h" aria-live="polite">2026年9月</h2>
<button class="x-date-picker__m" type="button" aria-label="次の月">
<i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
<table class="x-date-picker__g" role="grid" aria-labelledby="x-date-picker-h">
<thead>
<tr>
<th scope="col" abbr="日曜日" class="x-date-picker__sun">日</th><th scope="col" abbr="月曜日">月</th>
<th scope="col" abbr="火曜日">火</th><th scope="col" abbr="水曜日">水</th>
<th scope="col" abbr="木曜日">木</th><th scope="col" abbr="金曜日">金</th>
<th scope="col" abbr="土曜日" class="x-date-picker__sat">土</th>
</tr>
</thead>
<tbody>
<tr><td></td><td></td><td tabindex="-1">1</td><td tabindex="-1">2</td><td tabindex="-1">3</td><td tabindex="-1">4</td><td tabindex="-1">5</td></tr>
<tr><td tabindex="-1">6</td><td tabindex="-1">7</td><td tabindex="-1">8</td><td tabindex="-1">9</td><td tabindex="-1">10</td><td tabindex="-1">11</td><td tabindex="-1">12</td></tr>
<tr><td tabindex="-1">13</td><td tabindex="-1">14</td><td tabindex="-1">15</td><td tabindex="-1">16</td><td tabindex="-1">17</td><td tabindex="-1">18</td><td tabindex="-1">19</td></tr>
<tr><td tabindex="-1">20</td><td tabindex="-1">21</td><td tabindex="-1">22</td><td tabindex="-1">23</td><td tabindex="-1">24</td><td tabindex="-1" aria-current="date">25</td><td tabindex="-1">26</td></tr>
<tr><td tabindex="-1">27</td><td tabindex="0" aria-selected="true">28</td><td tabindex="-1">29</td><td tabindex="-1">30</td><td></td><td></td><td></td></tr>
</tbody>
</table>
</div>
</div>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-date-picker,
.x-date-picker *:not([class*="fa-"]),
.x-date-picker *:not([class*="fa-"])::before,
.x-date-picker *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-date-picker {
width: 232px;
color: #1b2028;
}
.x-date-picker__top {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 6px;
}
.x-date-picker__l {
font-size: 12px;
font-weight: 700;
}
.x-date-picker__fmt {
color: #5b6472;
font-size: 11px;
}
.x-date-picker__row {
display: flex;
gap: 6px;
}
.x-date-picker__i {
flex: 1 1 auto;
min-width: 0;
height: 40px;
padding: 0 10px;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
color: #1b2028;
font: inherit;
font-size: 14px;
font-variant-numeric: tabular-nums;
}
.x-date-picker__b {
width: 40px;
height: 40px;
flex: none;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
color: #1b2028;
cursor: pointer;
}
/* 実際のページでは position: absolute で入力欄の下に重ねる。見本は流れの中に置いている */
.x-date-picker__d {
margin-top: 6px;
padding: 6px 8px 8px;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
box-shadow: 0 6px 16px rgba(27, 32, 40, .12);
}
.x-date-picker__nav {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 2px;
}
.x-date-picker__h {
margin: 0;
font-size: 13px;
font-weight: 700;
}
.x-date-picker__m {
width: 28px;
height: 28px;
border: 0;
border-radius: 6px;
background: none;
color: #1b2028;
font-size: 12px;
cursor: pointer;
}
.x-date-picker__g {
width: 100%;
border-collapse: collapse;
font-size: 12px;
font-variant-numeric: tabular-nums;
text-align: center;
}
.x-date-picker__g th {
height: 20px;
color: #5b6472;
font-size: 10.5px;
font-weight: 700;
}
.x-date-picker__g th.x-date-picker__sun,
.x-date-picker__g td:first-child {
color: #b91c1c;
}
.x-date-picker__g th.x-date-picker__sat,
.x-date-picker__g td:last-child {
color: #1d4ed8;
}
.x-date-picker__g td {
height: 26px;
padding: 0;
border-radius: 6px;
cursor: pointer;
}
.x-date-picker__g td:empty {
cursor: default;
}
/* 今日は枠線で、選択中は塗りで示す。色だけに頼らず形も変える */
.x-date-picker__g td[aria-current="date"] {
box-shadow: inset 0 0 0 1.5px #1b2028;
font-weight: 700;
}
.x-date-picker__g td[aria-selected="true"] {
background: #1d4ed8;
color: #fff;
font-weight: 700;
}
.x-date-picker__i:focus-visible,
.x-date-picker__b:focus-visible,
.x-date-picker__m:focus-visible,
.x-date-picker__g td:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 1px;
}
AIへの指示文
「日付ピッカー(Date picker)」を実装してください。
## これは何か
日付を入れる欄に、月のカレンダーを開いて選べる仕組みを付けたもの。予約日のように、曜日を見ながら決めたい日付に使う。
## 使いどころ
- 予約日・配達希望日のように、曜日や今日からの近さを見ながら選びたいとき
- 選べない日(定休日・過去の日)をカレンダーの上で見せたいとき
## 実装の要件
- label + input(直接入力)+ button[aria-haspopup=dialog][aria-expanded] + div[role=dialog] > table[role=grid]
- 曜日の見出しは th に abbr="日曜日" を付け、省略しない名前でも読めるようにする
- 選べない日は aria-disabled="true" にして見た目も薄くし、理由(定休日など)を文字で添える
- min / max で選べる範囲を決める(input type="date" でも同じ属性が使える)
- 生年月日にカレンダーを使わない。数十年前まで月送りを繰り返させることになる。年・月・日を別の欄にするか、数字で直接入れてもらう
- 直接入力の道を残す。カレンダーだけにすると、日付が決まっている人ほど遅くなる
- 入力の形式の揺れ(2026/09/28・2026-9-28・全角数字)を受け止める。受け付けない形式があるなら、欄の近くに形式を書いておく
- 和暦で入れてもらうなら、内部では西暦で持ち、表示と入力だけ和暦にする。元号の切り替わり(令和元年は2019年5月1日から)をまたぐ日付に注意する
- 日付だけの文字列 "2026-09-28" を JavaScript の new Date() に渡すと UTC の0時として扱われ、UTC より遅れる時間帯(南北アメリカなど)では前日の日付になる。年・月・日の数値から組み立てる
- 週の始まり(日曜か月曜か)を利用者の慣れに合わせる。日本のカレンダーは日曜始まりが多い
## アクセシビリティの要件
- まず input type="date" で足りるか考える。カレンダーとキーボード操作はブラウザが用意し、送られる値は常に yyyy-mm-dd の形になる。ただし見た目と表示の形式(ブラウザや OS の言語設定に従う)は変えられない
- 独自のカレンダーを作るなら、開くボタンに aria-label(「カレンダーから選ぶ」)を付け、開いたら role="dialog" のパネルで選択中の日へフォーカスを移す。閉じたらボタンへ戻す
- 日付の並びは role="grid" の表にし、矢印キーで1日・1週、PageUp / PageDown で1か月、Home / End で週の端へ動かす(WAI-ARIA APG の Date Picker Dialog)
- 選んでいる日は aria-selected="true"、今日は aria-current="date" で示す。見た目は塗りと枠線のように、形でも区別する
- 月の見出しに aria-live="polite" を付け、月を送ったときに「2026年10月」と伝わるようにする
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<div class="x-date-picker">
<div class="x-date-picker__top">
<label class="x-date-picker__l" for="x-date-picker-i">予約日</label>
<span class="x-date-picker__fmt" id="x-date-picker-fmt">年/月/日 で入力</span>
</div>
<div class="x-date-picker__row">
<input class="x-date-picker__i" id="x-date-picker-i" type="text" name="date" inputmode="numeric"
value="2026/09/28" aria-describedby="x-date-picker-fmt">
<button class="x-date-picker__b" type="button" aria-label="カレンダーから選ぶ"
aria-haspopup="dialog" aria-expanded="true" aria-controls="x-date-picker-d">
<i class="fa-solid fa-calendar-days" aria-hidden="true"></i>
</button>
</div>
<!-- 見本なので開いた状態。実際は開いたときだけ表示し、aria-modal="true" を付けてフォーカスを選択中の日へ移す -->
<div class="x-date-picker__d" id="x-date-picker-d" role="dialog" aria-labelledby="x-date-picker-h">
<div class="x-date-picker__nav">
<button class="x-date-picker__m" type="button" aria-label="前の月">
<i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
</button>
<h2 class="x-date-picker__h" id="x-date-picker-h" aria-live="polite">2026年9月</h2>
<button class="x-date-picker__m" type="button" aria-label="次の月">
<i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
<table class="x-date-picker__g" role="grid" aria-labelledby="x-date-picker-h">
<thead>
<tr>
<th scope="col" abbr="日曜日" class="x-date-picker__sun">日</th><th scope="col" abbr="月曜日">月</th>
<th scope="col" abbr="火曜日">火</th><th scope="col" abbr="水曜日">水</th>
<th scope="col" abbr="木曜日">木</th><th scope="col" abbr="金曜日">金</th>
<th scope="col" abbr="土曜日" class="x-date-picker__sat">土</th>
</tr>
</thead>
<tbody>
<tr><td></td><td></td><td tabindex="-1">1</td><td tabindex="-1">2</td><td tabindex="-1">3</td><td tabindex="-1">4</td><td tabindex="-1">5</td></tr>
<tr><td tabindex="-1">6</td><td tabindex="-1">7</td><td tabindex="-1">8</td><td tabindex="-1">9</td><td tabindex="-1">10</td><td tabindex="-1">11</td><td tabindex="-1">12</td></tr>
<tr><td tabindex="-1">13</td><td tabindex="-1">14</td><td tabindex="-1">15</td><td tabindex="-1">16</td><td tabindex="-1">17</td><td tabindex="-1">18</td><td tabindex="-1">19</td></tr>
<tr><td tabindex="-1">20</td><td tabindex="-1">21</td><td tabindex="-1">22</td><td tabindex="-1">23</td><td tabindex="-1">24</td><td tabindex="-1" aria-current="date">25</td><td tabindex="-1">26</td></tr>
<tr><td tabindex="-1">27</td><td tabindex="0" aria-selected="true">28</td><td tabindex="-1">29</td><td tabindex="-1">30</td><td></td><td></td><td></td></tr>
</tbody>
</table>
</div>
</div>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-date-picker,
.x-date-picker *:not([class*="fa-"]),
.x-date-picker *:not([class*="fa-"])::before,
.x-date-picker *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-date-picker {
width: 232px;
color: #1b2028;
}
.x-date-picker__top {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 6px;
}
.x-date-picker__l {
font-size: 12px;
font-weight: 700;
}
.x-date-picker__fmt {
color: #5b6472;
font-size: 11px;
}
.x-date-picker__row {
display: flex;
gap: 6px;
}
.x-date-picker__i {
flex: 1 1 auto;
min-width: 0;
height: 40px;
padding: 0 10px;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
color: #1b2028;
font: inherit;
font-size: 14px;
font-variant-numeric: tabular-nums;
}
.x-date-picker__b {
width: 40px;
height: 40px;
flex: none;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
color: #1b2028;
cursor: pointer;
}
/* 実際のページでは position: absolute で入力欄の下に重ねる。見本は流れの中に置いている */
.x-date-picker__d {
margin-top: 6px;
padding: 6px 8px 8px;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
box-shadow: 0 6px 16px rgba(27, 32, 40, .12);
}
.x-date-picker__nav {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 2px;
}
.x-date-picker__h {
margin: 0;
font-size: 13px;
font-weight: 700;
}
.x-date-picker__m {
width: 28px;
height: 28px;
border: 0;
border-radius: 6px;
background: none;
color: #1b2028;
font-size: 12px;
cursor: pointer;
}
.x-date-picker__g {
width: 100%;
border-collapse: collapse;
font-size: 12px;
font-variant-numeric: tabular-nums;
text-align: center;
}
.x-date-picker__g th {
height: 20px;
color: #5b6472;
font-size: 10.5px;
font-weight: 700;
}
.x-date-picker__g th.x-date-picker__sun,
.x-date-picker__g td:first-child {
color: #b91c1c;
}
.x-date-picker__g th.x-date-picker__sat,
.x-date-picker__g td:last-child {
color: #1d4ed8;
}
.x-date-picker__g td {
height: 26px;
padding: 0;
border-radius: 6px;
cursor: pointer;
}
.x-date-picker__g td:empty {
cursor: default;
}
/* 今日は枠線で、選択中は塗りで示す。色だけに頼らず形も変える */
.x-date-picker__g td[aria-current="date"] {
box-shadow: inset 0 0 0 1.5px #1b2028;
font-weight: 700;
}
.x-date-picker__g td[aria-selected="true"] {
background: #1d4ed8;
color: #fff;
font-weight: 700;
}
.x-date-picker__i:focus-visible,
.x-date-picker__b:focus-visible,
.x-date-picker__m:focus-visible,
.x-date-picker__g td:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 1px;
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- 予約日・配達希望日のように、曜日や今日からの近さを見ながら選びたいとき
- 選べない日(定休日・過去の日)をカレンダーの上で見せたいとき
注意点
- 生年月日にカレンダーを使わない。数十年前まで月送りを繰り返させることになる。年・月・日を別の欄にするか、数字で直接入れてもらう
- 直接入力の道を残す。カレンダーだけにすると、日付が決まっている人ほど遅くなる
- 入力の形式の揺れ(2026/09/28・2026-9-28・全角数字)を受け止める。受け付けない形式があるなら、欄の近くに形式を書いておく
- 和暦で入れてもらうなら、内部では西暦で持ち、表示と入力だけ和暦にする。元号の切り替わり(令和元年は2019年5月1日から)をまたぐ日付に注意する
- 日付だけの文字列 "2026-09-28" を JavaScript の new Date() に渡すと UTC の0時として扱われ、UTC より遅れる時間帯(南北アメリカなど)では前日の日付になる。年・月・日の数値から組み立てる
- 週の始まり(日曜か月曜か)を利用者の慣れに合わせる。日本のカレンダーは日曜始まりが多い
アクセシビリティの要点
- まず input type="date" で足りるか考える。カレンダーとキーボード操作はブラウザが用意し、送られる値は常に yyyy-mm-dd の形になる。ただし見た目と表示の形式(ブラウザや OS の言語設定に従う)は変えられない
- 独自のカレンダーを作るなら、開くボタンに aria-label(「カレンダーから選ぶ」)を付け、開いたら role="dialog" のパネルで選択中の日へフォーカスを移す。閉じたらボタンへ戻す
- 日付の並びは role="grid" の表にし、矢印キーで1日・1週、PageUp / PageDown で1か月、Home / End で週の端へ動かす(WAI-ARIA APG の Date Picker Dialog)
- 選んでいる日は aria-selected="true"、今日は aria-current="date" で示す。見た目は塗りと枠線のように、形でも区別する
- 月の見出しに aria-live="polite" を付け、月を送ったときに「2026年10月」と伝わるようにする