Calendar

Month grids for picking a date or a date range.

Month grid

A <table> with one button per day. Mark days from adjacent months with .outside, the current date with .today, and days that carry something with .marked. Render six rows every month so the block size never jumps.

Code

<div class="calendar">
  <header>
    <button type="button" class="prev" aria-label="Previous month">…</button>
    <span class="title" id="cal-2027-01">January 2027</span>
    <button type="button" class="next" aria-label="Next month">…</button>
  </header>
  <table role="grid" aria-labelledby="cal-2027-01">
    <thead>
      <tr><th scope="col" abbr="Monday">Mo</th> … </tr>
    </thead>
    <tbody>
      <tr>
        <td class="outside"><button type="button" tabindex="-1">28</button></td>
        …
        <td class="marked"><button type="button">1</button></td>
        <td class="today"><button type="button" aria-current="date">26</button></td>
      </tr>
      <!-- always six rows -->
    </tbody>
  </table>
</div>
January 2027
Mo Tu We Th Fr Sa Su

Single date

Add .selected to the cell and aria-pressed="true" to its button.

Code

<td class="selected">
  <button type="button" aria-pressed="true">14</button>
</td>
January 2027
Mo Tu We Th Fr Sa Su

Date range

.range-start and .range-end paint the ends, .in-range paints the band between them. The band is a cell background, so it runs edge to edge across a week.

Code

<td class="range-start"><button type="button" aria-pressed="true">8</button></td>
<td class="in-range"><button type="button">9</button></td>
<td class="in-range"><button type="button">10</button></td>
…
<td class="range-end"><button type="button" aria-pressed="true">14</button></td>
January 2027
Mo Tu We Th Fr Sa Su

Several months

Wrap calendars in .calendar-group. Omit .prev on the first month and .next on the last: the header keeps its title centred either way.

Code

<div class="calendar-group">
  <div class="calendar">
    <header>
      <button type="button" class="prev" aria-label="Previous month">…</button>
      <span class="title">January 2027</span>
    </header>
    …
  </div>
  <div class="calendar">
    <header>
      <span class="title">February 2027</span>
      <button type="button" class="next" aria-label="Next month">…</button>
    </header>
    …
  </div>
</div>
January 2027
Mo Tu We Th Fr Sa Su
February 2027
Mo Tu We Th Fr Sa Su

Compact

.calendar--compact shrinks cells to one line for tight panels.

Code

<div class="calendar calendar--compact">…</div>
January 2027
Mo Tu We Th Fr Sa Su

Behaviour

The calendar is state, not behaviour. Month navigation and selection are set by the server render or a small script: change the cell classes and the aria-* attributes, nothing else. Cells are buttons, so keyboard users can tab through days and activate them without extra work.

Change the cell size with --calendar-cell and the range band colour with --calendar-range-bg.