Month Grid Calendar
A full month laid out as an accessible table with prev/next navigation, a today ring and a selectable day.
3 frameworksIntermediate
A single day's hours as rows, each event dropped into the hour it starts - no overlap, no overflow.
<!--
An agenda, not an absolute-positioned timeline: each hour is a row, each event
sits in the row it starts in. That trades pixel-accurate durations for a
layout that never overlaps or overflows at 320px, and it reads top-to-bottom
in the DOM in the order the day happens.
-->
<div class="w-full max-w-md rounded-2xl border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-gray-900">
<h2 class="mb-3 text-sm font-semibold text-gray-900 dark:text-gray-100">Friday, July 17</h2>
<ul class="space-y-1">
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">8 AM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">9 AM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
<div class="rounded-lg border-l-4 px-3 py-1.5 text-xs border-blue-500 bg-blue-50 text-blue-900 dark:border-blue-400 dark:bg-blue-950 dark:text-blue-100">
<p class="font-semibold">Team standup</p>
<p class="opacity-80">09:00–09:45</p>
</div>
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">10 AM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">11 AM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
<div class="rounded-lg border-l-4 px-3 py-1.5 text-xs border-violet-500 bg-violet-50 text-violet-900 dark:border-violet-400 dark:bg-violet-950 dark:text-violet-100">
<p class="font-semibold">Design review</p>
<p class="opacity-80">11:00–12:00</p>
</div>
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">12 PM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">1 PM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">2 PM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
<div class="rounded-lg border-l-4 px-3 py-1.5 text-xs border-emerald-500 bg-emerald-50 text-emerald-900 dark:border-emerald-400 dark:bg-emerald-950 dark:text-emerald-100">
<p class="font-semibold">1:1 with Sam</p>
<p class="opacity-80">14:30–15:30</p>
</div>
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">3 PM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">4 PM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
<li class="flex gap-3">
<span class="w-12 shrink-0 pt-0.5 text-right text-xs tabular-nums text-gray-400 dark:text-gray-500">5 PM</span>
<div class="min-w-0 flex-1 border-t border-gray-100 pt-1 dark:border-gray-800">
</div>
</li>
</ul>
</div>| Prop | Type | Default | Description |
|---|---|---|---|
heading | string | 'Friday, July 17' | Heading |
fromHour | number | 8 | From hour |
toHour | number | 17 | To hour |
events | ScheduleEvent[] | - | Events |
className | string | - | Additional classes merged onto the root element. |
An agenda rather than an absolute-positioned timeline, which trades pixel-exact durations for a layout that never collides or overflows at 320px. Pass `events` with `start`/`end`/`title`/`tone`, and bound the visible window with `fromHour`/`toHour`.