カレンダー(月表示)Month calendar
1か月の日付を曜日の見出しの下にマス目で並べ、定休日やイベント、予約の空き(○△×)を各日に書き込んで見せる表。今日の日付は枠で囲む。
表示例
アイコン: Font Awesome 7 Free(fa-solid / fa-regular)
別名・英語表記
- 月間カレンダー
- マンスリーカレンダー
- イベントカレンダー
- 営業日カレンダー
- 空き状況カレンダー
- 予約カレンダー
- Month calendar
実装例
<div class="x-month-calendar">
<div class="x-month-calendar__top">
<button class="x-month-calendar__nav" type="button" aria-label="前の月">
<i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
</button>
<!-- 月を送ったら見出しの文字を書き換える。aria-live="polite" で新しい月が読み上げられる -->
<h2 class="x-month-calendar__h" id="x-month-calendar-h" aria-live="polite">2026年10月の空き状況</h2>
<button class="x-month-calendar__nav" type="button" aria-label="次の月">
<i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
<!-- 見るための表なので role="grid" にしない。空きのある日だけがリンクで、Tab で順に移れる -->
<table class="x-month-calendar__t" aria-labelledby="x-month-calendar-h">
<thead>
<tr>
<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="金曜日">金</th>
<th scope="col" abbr="土曜日">土</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td aria-current="date"><a class="x-month-calendar__c" href="/reserve?date=2026-10-01"><span class="x-month-calendar__n">1</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-02"><span class="x-month-calendar__n">2</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><span class="x-month-calendar__c"><span class="x-month-calendar__n">3</span><span class="x-month-calendar__m x-month-calendar__m--full" aria-hidden="true">×</span><span class="x-month-calendar__sr">満席</span></span></td>
</tr>
<tr>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-04"><span class="x-month-calendar__n">4</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-05"><span class="x-month-calendar__n">5</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-06"><span class="x-month-calendar__n">6</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">7</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-08"><span class="x-month-calendar__n">8</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-09"><span class="x-month-calendar__n">9</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><span class="x-month-calendar__c"><span class="x-month-calendar__n">10</span><i class="fa-solid fa-star x-month-calendar__ev" aria-hidden="true"></i><span class="x-month-calendar__sr">イベントあり</span><span class="x-month-calendar__m x-month-calendar__m--full" aria-hidden="true">×</span><span class="x-month-calendar__sr">満席</span></span></td>
</tr>
<tr>
<td><span class="x-month-calendar__c"><span class="x-month-calendar__n">11</span><span class="x-month-calendar__m x-month-calendar__m--full" aria-hidden="true">×</span><span class="x-month-calendar__sr">満席</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-12"><span class="x-month-calendar__n">12</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-13"><span class="x-month-calendar__n">13</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">14</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-15"><span class="x-month-calendar__n">15</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-16"><span class="x-month-calendar__n">16</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-17"><span class="x-month-calendar__n">17</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
</tr>
<tr>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-18"><span class="x-month-calendar__n">18</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-19"><span class="x-month-calendar__n">19</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-20"><span class="x-month-calendar__n">20</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">21</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-22"><span class="x-month-calendar__n">22</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-23"><span class="x-month-calendar__n">23</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-24"><span class="x-month-calendar__n">24</span><i class="fa-solid fa-star x-month-calendar__ev" aria-hidden="true"></i><span class="x-month-calendar__sr">イベントあり</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
</tr>
<tr>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-25"><span class="x-month-calendar__n">25</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-26"><span class="x-month-calendar__n">26</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-27"><span class="x-month-calendar__n">27</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">28</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-29"><span class="x-month-calendar__n">29</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-30"><span class="x-month-calendar__n">30</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-31"><span class="x-month-calendar__n">31</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
</tr>
</tbody>
</table>
<!-- 記号の意味は凡例に文字で書く。記号そのものは読み上げから外し、各日に文字で持たせている -->
<ul class="x-month-calendar__key" role="list" aria-label="記号の意味">
<li><span class="x-month-calendar__m--ok" aria-hidden="true">○</span>空きあり</li>
<li><span class="x-month-calendar__m--few" aria-hidden="true">△</span>残りわずか</li>
<li><span class="x-month-calendar__m--full" aria-hidden="true">×</span>満席</li>
<li><span aria-hidden="true">休</span>定休日</li>
<li><i class="fa-solid fa-star x-month-calendar__ev" aria-hidden="true"></i>イベント</li>
</ul>
</div>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-month-calendar,
.x-month-calendar *:not([class*="fa-"]),
.x-month-calendar *:not([class*="fa-"])::before,
.x-month-calendar *: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-month-calendar {
width: 320px;
max-width: 100%;
padding: 10px 10px 8px;
border: 1px solid #e3e6ea;
border-radius: 10px;
background: #fff;
color: #1b2028;
}
.x-month-calendar__top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
}
.x-month-calendar__h {
margin: 0;
font-size: 13px;
font-weight: 700;
}
.x-month-calendar__nav {
width: 28px;
height: 28px;
border: 1px solid #7f8a99;
border-radius: 6px;
background: #fff;
color: #1b2028;
font-size: 11px;
cursor: pointer;
}
.x-month-calendar__t {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
font-variant-numeric: tabular-nums;
text-align: center;
}
.x-month-calendar__t th {
height: 18px;
color: #5b6472;
font-size: 10.5px;
font-weight: 700;
}
/* 日曜は赤、土曜は青。曜日の見出しの文字があるので、色は補助にとどまる */
.x-month-calendar__t th:first-child,
.x-month-calendar__t td:first-child .x-month-calendar__n {
color: #b91c1c;
}
.x-month-calendar__t th:last-child,
.x-month-calendar__t td:last-child .x-month-calendar__n {
color: #1d4ed8;
}
.x-month-calendar__t td {
height: 30px;
padding: 1px;
border-top: 1px solid #e3e6ea;
vertical-align: top;
}
.x-month-calendar__c {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
min-height: 28px;
padding-top: 1px;
border-radius: 5px;
color: #1b2028;
text-decoration: none;
}
.x-month-calendar__t a:hover {
background: #eef2ff;
}
.x-month-calendar__t a:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 0;
}
.x-month-calendar__n {
font-size: 10.5px;
line-height: 1.2;
}
.x-month-calendar__m {
font-size: 13px;
font-weight: 700;
line-height: 1.15;
}
.x-month-calendar__m--ok {
color: #1d4ed8;
}
.x-month-calendar__m--few {
color: #b45309;
}
.x-month-calendar__m--full {
color: #5b6472;
}
/* 定休日は地の色を変え、「休」の文字でも示す */
.x-month-calendar__off .x-month-calendar__c {
background: #f1f3f6;
}
.x-month-calendar__off .x-month-calendar__m {
color: #4b5563;
font-size: 11px;
line-height: 1.45;
}
.x-month-calendar__c .x-month-calendar__ev {
position: absolute;
top: 2px;
right: 3px;
font-size: 8px;
}
.x-month-calendar__ev {
color: #b45309;
}
/* 今日は枠線で囲む。色ではなく形で示す */
.x-month-calendar__t td[aria-current="date"] .x-month-calendar__c {
box-shadow: inset 0 0 0 2px #1b2028;
}
.x-month-calendar__t td[aria-current="date"] .x-month-calendar__n {
font-weight: 700;
}
/* 画面には出さず、読み上げにだけ渡す */
.x-month-calendar__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.x-month-calendar__key {
display: flex;
flex-wrap: wrap;
gap: 2px 8px;
margin: 6px 0 0;
padding: 6px 2px 0;
border-top: 1px solid #e3e6ea;
color: #5b6472;
font-size: 10.5px;
list-style: none;
}
.x-month-calendar__key li {
display: flex;
align-items: center;
gap: 3px;
}
.x-month-calendar__key span {
font-weight: 700;
}
.x-month-calendar__nav:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
AIへの指示文
「カレンダー(月表示)(Month calendar)」を実装してください。
## これは何か
1か月の日付を曜日の見出しの下にマス目で並べ、定休日やイベント、予約の空き(○△×)を各日に書き込んで見せる表。今日の日付は枠で囲む。
## 使いどころ
- お店の営業日と定休日を、1か月まとめて見せたいとき
- 予約の空き状況を日ごとに○△×で示し、空いている日から予約へ進ませたいとき
- イベントや講座の開催日を、曜日の並びの中で見せたいとき
## 実装の要件
- 見出し(h2・月名)+前後の月のボタン + table(thead に曜日7列、tbody に週ごとの行)+ 凡例の ul
- 1マスは日付の数字と状態の記号の2段。状態ごとに記号の色を変えつつ、記号の形そのものでも区別できるものを選ぶ
- 月の初めと終わりの空きマスは空の td にする。前後の月の日付を薄く出すなら、その日にもリンクや状態を付けるかを決めておく
- 日付の組み立ては年・月・日の数値で行う。"2026-10-01" のような文字列を new Date() に渡すと UTC の0時として扱われ、時間帯によって1日ずれる
- 日付を1つ選んで入力欄に入れるための部品は日付ピッカー。こちらは月全体の予定や空きを「見せる」表で、選んだ値をフォームに返さない
- ○△×や色だけで状態を伝えない。凡例に文字で意味を書き、各日にも「空きあり」「満席」のような文字を読み上げ用に持たせる
- 日曜の赤・土曜の青は曜日の見出しの文字があって初めて補助になる。祝日を赤くするだけにせず、祝日名か「祝」を添える
- 狭い画面では1マスが40px前後まで縮む。マスの中に長い文言を入れず、記号と1文字の略記にとどめて、詳しくは日を押した先で見せる
- 祝日や定休日を手で書き込むと年をまたいで古くなる。祝日は内閣府の「国民の祝日」一覧から毎年取り直す
## アクセシビリティの要件
- 見るための表なので table に th scope="col" で曜日の見出しを付ける。矢印キーで日を動かす操作を付けないなら role="grid" にしない
- 曜日の見出しは th に abbr="日曜日" を付け、1文字の略記でも読み上げで分かるようにする
- 今日の日付に aria-current="date" を付け、見た目は枠線のように形でも示す
- 記号(○△×・星)は aria-hidden="true" で読み上げから外し、同じ意味の文字を視覚的に隠して添える。凡例も文字で書く(WCAG 2.2 の 1.4.1 色の用途、1.3.1 情報及び関係性)
- 予約へ進める日だけをリンクにして Tab で順に移れるようにし、:focus-visible の枠を見せる。マスの大きさは 24px 四方以上にする(WCAG 2.2 の 2.5.8 ターゲットのサイズ(最低限))
- 前後の月へ送るボタンには aria-label(「前の月」「次の月」)を付け、月の見出しに aria-live="polite" を置いて送った先の月を伝える
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<div class="x-month-calendar">
<div class="x-month-calendar__top">
<button class="x-month-calendar__nav" type="button" aria-label="前の月">
<i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
</button>
<!-- 月を送ったら見出しの文字を書き換える。aria-live="polite" で新しい月が読み上げられる -->
<h2 class="x-month-calendar__h" id="x-month-calendar-h" aria-live="polite">2026年10月の空き状況</h2>
<button class="x-month-calendar__nav" type="button" aria-label="次の月">
<i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
<!-- 見るための表なので role="grid" にしない。空きのある日だけがリンクで、Tab で順に移れる -->
<table class="x-month-calendar__t" aria-labelledby="x-month-calendar-h">
<thead>
<tr>
<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="金曜日">金</th>
<th scope="col" abbr="土曜日">土</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td aria-current="date"><a class="x-month-calendar__c" href="/reserve?date=2026-10-01"><span class="x-month-calendar__n">1</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-02"><span class="x-month-calendar__n">2</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><span class="x-month-calendar__c"><span class="x-month-calendar__n">3</span><span class="x-month-calendar__m x-month-calendar__m--full" aria-hidden="true">×</span><span class="x-month-calendar__sr">満席</span></span></td>
</tr>
<tr>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-04"><span class="x-month-calendar__n">4</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-05"><span class="x-month-calendar__n">5</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-06"><span class="x-month-calendar__n">6</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">7</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-08"><span class="x-month-calendar__n">8</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-09"><span class="x-month-calendar__n">9</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><span class="x-month-calendar__c"><span class="x-month-calendar__n">10</span><i class="fa-solid fa-star x-month-calendar__ev" aria-hidden="true"></i><span class="x-month-calendar__sr">イベントあり</span><span class="x-month-calendar__m x-month-calendar__m--full" aria-hidden="true">×</span><span class="x-month-calendar__sr">満席</span></span></td>
</tr>
<tr>
<td><span class="x-month-calendar__c"><span class="x-month-calendar__n">11</span><span class="x-month-calendar__m x-month-calendar__m--full" aria-hidden="true">×</span><span class="x-month-calendar__sr">満席</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-12"><span class="x-month-calendar__n">12</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-13"><span class="x-month-calendar__n">13</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">14</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-15"><span class="x-month-calendar__n">15</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-16"><span class="x-month-calendar__n">16</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-17"><span class="x-month-calendar__n">17</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
</tr>
<tr>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-18"><span class="x-month-calendar__n">18</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-19"><span class="x-month-calendar__n">19</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-20"><span class="x-month-calendar__n">20</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">21</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-22"><span class="x-month-calendar__n">22</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-23"><span class="x-month-calendar__n">23</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-24"><span class="x-month-calendar__n">24</span><i class="fa-solid fa-star x-month-calendar__ev" aria-hidden="true"></i><span class="x-month-calendar__sr">イベントあり</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
</tr>
<tr>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-25"><span class="x-month-calendar__n">25</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-26"><span class="x-month-calendar__n">26</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-27"><span class="x-month-calendar__n">27</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td class="x-month-calendar__off"><span class="x-month-calendar__c"><span class="x-month-calendar__n">28</span><span class="x-month-calendar__m" aria-hidden="true">休</span><span class="x-month-calendar__sr">定休日</span></span></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-29"><span class="x-month-calendar__n">29</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-30"><span class="x-month-calendar__n">30</span><span class="x-month-calendar__m x-month-calendar__m--ok" aria-hidden="true">○</span><span class="x-month-calendar__sr">空きあり</span></a></td>
<td><a class="x-month-calendar__c" href="/reserve?date=2026-10-31"><span class="x-month-calendar__n">31</span><span class="x-month-calendar__m x-month-calendar__m--few" aria-hidden="true">△</span><span class="x-month-calendar__sr">残りわずか</span></a></td>
</tr>
</tbody>
</table>
<!-- 記号の意味は凡例に文字で書く。記号そのものは読み上げから外し、各日に文字で持たせている -->
<ul class="x-month-calendar__key" role="list" aria-label="記号の意味">
<li><span class="x-month-calendar__m--ok" aria-hidden="true">○</span>空きあり</li>
<li><span class="x-month-calendar__m--few" aria-hidden="true">△</span>残りわずか</li>
<li><span class="x-month-calendar__m--full" aria-hidden="true">×</span>満席</li>
<li><span aria-hidden="true">休</span>定休日</li>
<li><i class="fa-solid fa-star x-month-calendar__ev" aria-hidden="true"></i>イベント</li>
</ul>
</div>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-month-calendar,
.x-month-calendar *:not([class*="fa-"]),
.x-month-calendar *:not([class*="fa-"])::before,
.x-month-calendar *: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-month-calendar {
width: 320px;
max-width: 100%;
padding: 10px 10px 8px;
border: 1px solid #e3e6ea;
border-radius: 10px;
background: #fff;
color: #1b2028;
}
.x-month-calendar__top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
}
.x-month-calendar__h {
margin: 0;
font-size: 13px;
font-weight: 700;
}
.x-month-calendar__nav {
width: 28px;
height: 28px;
border: 1px solid #7f8a99;
border-radius: 6px;
background: #fff;
color: #1b2028;
font-size: 11px;
cursor: pointer;
}
.x-month-calendar__t {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
font-variant-numeric: tabular-nums;
text-align: center;
}
.x-month-calendar__t th {
height: 18px;
color: #5b6472;
font-size: 10.5px;
font-weight: 700;
}
/* 日曜は赤、土曜は青。曜日の見出しの文字があるので、色は補助にとどまる */
.x-month-calendar__t th:first-child,
.x-month-calendar__t td:first-child .x-month-calendar__n {
color: #b91c1c;
}
.x-month-calendar__t th:last-child,
.x-month-calendar__t td:last-child .x-month-calendar__n {
color: #1d4ed8;
}
.x-month-calendar__t td {
height: 30px;
padding: 1px;
border-top: 1px solid #e3e6ea;
vertical-align: top;
}
.x-month-calendar__c {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
min-height: 28px;
padding-top: 1px;
border-radius: 5px;
color: #1b2028;
text-decoration: none;
}
.x-month-calendar__t a:hover {
background: #eef2ff;
}
.x-month-calendar__t a:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 0;
}
.x-month-calendar__n {
font-size: 10.5px;
line-height: 1.2;
}
.x-month-calendar__m {
font-size: 13px;
font-weight: 700;
line-height: 1.15;
}
.x-month-calendar__m--ok {
color: #1d4ed8;
}
.x-month-calendar__m--few {
color: #b45309;
}
.x-month-calendar__m--full {
color: #5b6472;
}
/* 定休日は地の色を変え、「休」の文字でも示す */
.x-month-calendar__off .x-month-calendar__c {
background: #f1f3f6;
}
.x-month-calendar__off .x-month-calendar__m {
color: #4b5563;
font-size: 11px;
line-height: 1.45;
}
.x-month-calendar__c .x-month-calendar__ev {
position: absolute;
top: 2px;
right: 3px;
font-size: 8px;
}
.x-month-calendar__ev {
color: #b45309;
}
/* 今日は枠線で囲む。色ではなく形で示す */
.x-month-calendar__t td[aria-current="date"] .x-month-calendar__c {
box-shadow: inset 0 0 0 2px #1b2028;
}
.x-month-calendar__t td[aria-current="date"] .x-month-calendar__n {
font-weight: 700;
}
/* 画面には出さず、読み上げにだけ渡す */
.x-month-calendar__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.x-month-calendar__key {
display: flex;
flex-wrap: wrap;
gap: 2px 8px;
margin: 6px 0 0;
padding: 6px 2px 0;
border-top: 1px solid #e3e6ea;
color: #5b6472;
font-size: 10.5px;
list-style: none;
}
.x-month-calendar__key li {
display: flex;
align-items: center;
gap: 3px;
}
.x-month-calendar__key span {
font-weight: 700;
}
.x-month-calendar__nav:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- お店の営業日と定休日を、1か月まとめて見せたいとき
- 予約の空き状況を日ごとに○△×で示し、空いている日から予約へ進ませたいとき
- イベントや講座の開催日を、曜日の並びの中で見せたいとき
注意点
- 日付を1つ選んで入力欄に入れるための部品は日付ピッカー。こちらは月全体の予定や空きを「見せる」表で、選んだ値をフォームに返さない
- ○△×や色だけで状態を伝えない。凡例に文字で意味を書き、各日にも「空きあり」「満席」のような文字を読み上げ用に持たせる
- 日曜の赤・土曜の青は曜日の見出しの文字があって初めて補助になる。祝日を赤くするだけにせず、祝日名か「祝」を添える
- 狭い画面では1マスが40px前後まで縮む。マスの中に長い文言を入れず、記号と1文字の略記にとどめて、詳しくは日を押した先で見せる
- 祝日や定休日を手で書き込むと年をまたいで古くなる。祝日は内閣府の「国民の祝日」一覧から毎年取り直す
アクセシビリティの要点
- 見るための表なので table に th scope="col" で曜日の見出しを付ける。矢印キーで日を動かす操作を付けないなら role="grid" にしない
- 曜日の見出しは th に abbr="日曜日" を付け、1文字の略記でも読み上げで分かるようにする
- 今日の日付に aria-current="date" を付け、見た目は枠線のように形でも示す
- 記号(○△×・星)は aria-hidden="true" で読み上げから外し、同じ意味の文字を視覚的に隠して添える。凡例も文字で書く(WCAG 2.2 の 1.4.1 色の用途、1.3.1 情報及び関係性)
- 予約へ進める日だけをリンクにして Tab で順に移れるようにし、:focus-visible の枠を見せる。マスの大きさは 24px 四方以上にする(WCAG 2.2 の 2.5.8 ターゲットのサイズ(最低限))
- 前後の月へ送るボタンには aria-label(「前の月」「次の月」)を付け、月の見出しに aria-live="polite" を置いて送った先の月を伝える