Month Calendar
A theme-portable month event-grid (scheduler) — a 6×7 day-cell grid with weekday header, month navigation, a view-mode segmented control, and per-cell event chips carrying a status dot and series tint.
Preview
July 2026
Installation
npx visor add --block month-calendarThis copies files into your project:
blocks/month-calendar/month-calendar.tsx— the block componentblocks/month-calendar/month-calendar.module.css— the styles
The block depends only on @phosphor-icons/react (nav arrows) and the shared utils helper — it builds its own grid, so there are no component registry dependencies.
Usage
'use client';
import * as React from 'react';
import {
MonthCalendar,
type MonthCalendarEvent,
} from '@/blocks/month-calendar/month-calendar';
const events: MonthCalendarEvent[] = [
{ id: '1', date: new Date(2026, 6, 4), title: 'Launch party', status: 'success', series: 1 },
{ id: '2', date: new Date(2026, 6, 4), title: 'Standup', status: 'info', series: 2 },
{ id: '3', date: new Date(2026, 6, 20), title: 'Payment due', status: 'danger' },
];
export function Schedule() {
const [month, setMonth] = React.useState(new Date());
const [selected, setSelected] = React.useState<Date | undefined>(undefined);
const [view, setView] = React.useState('month');
return (
<MonthCalendar
month={month}
onMonthChange={setMonth}
events={events}
today={new Date()}
selectedDate={selected}
onSelectDate={setSelected}
onEventSelect={(event) => console.log('open', event.id)}
view={view}
onViewChange={setView}
/>
);
}Anatomy
The block is a single column with three regions:
- Header — prev/next month arrows around a localized
Month Yearlabel, plus a view-mode segmented control (Month/Week/Dayby default). The arrows are disabled when noonMonthChangehandler is supplied. - Weekday header — seven localized short weekday names, honoring
weekStartsOn(Sunday or Monday). - Day grid — a fixed 6×7 (42-cell) month grid. Each cell shows its day number and stacks its event chips; days outside the displayed month are dimmed.
Event chips
Each event renders as a chip with two token-driven signals:
- Status dot — a leading dot colored by
status(success/warning/danger/info, or a neutral default). Dots bind to the--surface-{success,warning,error,info}-defaulttokens. - Series tint — an optional
seriesindex (1–5) that washes the chip background and adds a leading accent bar, keyed to the theme's--chart-1…5ramp. Use it to color-code a recurring series or a resource lane.
When a day holds more events than maxChipsPerDay (default 3), the extras collapse into a +N more row.
Interaction
The block is fully controlled and SSR-safe — it never reads the system clock itself.
month+onMonthChange— drive the displayed month. The handler receives the first day of the target month.onSelectDate— when supplied, day cells become buttons and report the clicked date; the active day is highlighted viaselectedDate.onEventSelect— when supplied, chips become buttons and report the clicked event.today— passnew Date()from a client boundary to highlight the current day. Omit it to keep server and client render identical (no hydration mismatch).view+onViewChange— drive the segmented control. The block always renders the month grid; swap in your own week/day surface based on the active view.
Accessibility
- Month-nav arrows carry
aria-label="Previous month"/"Next month". - The view-mode control is a labelled
role="group"; each segment reportsaria-pressed. - Selectable day cells expose a full
aria-label(e.g."July 4, 2026, 2 events") and reportaria-pressedfor the selected day. - Status dots are decorative (
aria-hidden) — the chip's text label is the accessible name.
Props
See MonthCalendarProps in blocks/month-calendar/month-calendar.tsx for the full list. In summary:
- Month —
month(required),onMonthChange,weekStartsOn,locale. - Events —
events,maxChipsPerDay,onEventSelect. - Selection —
selectedDate,onSelectDate,today. - View mode —
viewOptions,view,onViewChange.
MonthCalendarEvent is { id, date, title, status?, series? }, where status is "default" | "success" | "warning" | "danger" | "info" and series is 1 | 2 | 3 | 4 | 5.
About Blocks
Blocks are complete UI patterns made up of multiple Visor components. Unlike individual components, blocks represent larger, composed sections of UI — such as admin shells, login forms, or dashboard panels.
Blocks are copy-and-own, just like components. Install them into your project and customize freely.