- ds
- Components
- Calendar and date range
Calendar and date range
A month grid for dates and ranges, and the time range picker.
Date
Code
<div class="cal">
<div class="cal-month">
<div class="cal-head">
<button class="btn icon xs ghost" aria-label="Previous month">…</button><b>October 2026</b><button class="btn icon xs ghost" aria-label="Next month">…</button>
</div>
<div class="cal-grid" role="grid" aria-label="October 2026">
<span class="dow" aria-hidden="true">Mo</span> …
<span data-out aria-hidden="true">28</span> …
<button disabled aria-label="11 October 2026">11</button>
<button aria-current="date" aria-label="12 October 2026">12</button>
<button aria-selected="true" aria-label="16 October 2026">16</button> …
</div>
</div>
</div>A month on a seven-column grid, weeks from Monday. Today is in the accent with a dot under it; the chosen date is filled. Dates that cannot be picked, here anything before today for a scheduled backfill, are faint and struck through. Days from the next and previous month fill the grid but are not buttons.
Range
Code
<div class="cal">
<div class="cal-months">
<div class="cal-month">…</div>
<div class="cal-month">…</div>
</div>
<div class="cal-foot"><span>27 Oct 2026 to 4 Nov 2026 · 9 days</span><div class="btn-row">…</div></div>
</div>
<button data-range="start" aria-selected="true">27</button>
<button data-range="mid">28</button> …
<button data-range="end" aria-selected="true">4</button>Two months side by side. The ends are filled and the days between are washed edge to edge, rounding where a week wraps. Click a start and an end to try it. The footer says the range in words with its length, because "9 days" is what people check.
Time range
Relative like now-6h or a date like 2026-10-12 09:00. Times are UTC.
Show code
<div class="btn-row"><button class="btn sm" type="button"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>Last 24 hours<svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button><button class="btn sm icon" type="button" aria-label="Refresh"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg></button></div>
<div class="trange">
<div class="trange-abs">
<span class="label">Absolute range</span>
<label class="fld"><span>From</span><input class="inp mono" value="now-24h" /></label>
<label class="fld"><span>To</span><input class="inp mono" value="now" /></label>
<p class="hint">Relative like <code>now-6h</code> or a date like <code>2026-10-12 09:00</code>. Times are UTC.</p>
<button class="btn sm primary" type="button">Apply</button>
</div>
<ul class="trange-rel" role="listbox" aria-label="Relative ranges">
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Last 5 minutes</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Last 15 minutes</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Last 1 hour</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Last 6 hours</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="true"><span>Last 24 hours</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Last 7 days</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Last 30 days</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>Today so far</span></button></li>
<li><button class="mi" type="button" role="option" aria-checked="false"><span>This week so far</span></button></li>
</ul>
</div>The picker every dashboard needs: quick relative ranges on the right, an absolute range on the left that also accepts expressions like now-6h. It is a floating panel with the menu shadow, opened from a button that always shows the current range.
API
| Class | On | What it does |
|---|---|---|
.cal | div | The surface. Holds one .cal-month, or several in .cal-months, and an optional .cal-foot. |
.cal-head | div | Previous button, month name, next button. |
.cal-grid | div[role=grid] | Seven columns: .dow heads, then a button per day. |
aria-current="date" | button | Today. |
aria-selected="true" | button | A chosen date, filled with the accent. |
data-range | button | start, mid or end of a range. |
data-out | span | A day from the next or previous month. |
.trange | div | Time range picker: .trange-abs on the left, .trange-rel list of .mi on the right. |
- Show the time zone wherever a time is entered.
- Disable dates that cannot work instead of rejecting them after.
- A text field next to the calendar for people who know the date.
- A calendar for a date of birth. Use three fields.
- Ranges without their length.
- Weeks starting on Sunday in one place and Monday in another.
Accessibility
- Use one tab stop for the grid and arrow keys to move between days; Page Up and Page Down change month. Each day button has a full label like "16 October 2026".
- Announce the chosen range in a live region, and keep the footer text in sync.