Webパーツ辞典

カレンダー(月表示)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>

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" を置いて送った先の月を伝える

作るときに使えるツール

見分け方

関連するパーツ