Skip to content

Components · Date, time and scheduling

MiniMonth

A keyboard-navigable month grid (APG date-picker grid) with min/max, disabled days, week numbers and event dots.

@rookcom/monarch-react/schedulingComponent

Sidebar calendar with events

November 2026
MonTueWedThuFriSatSun

Usage examples

Bookable days only, week numbers

US locale (week starts on Sunday)

Purpose

Shows one month (or several side by side) as a grid of days the user can move through with the keyboard and pick from. It is the calendar inside DatePicker and DateRangePicker, and works on its own as a sidebar calendar for jumping to a date. Import from @rookcom/monarch-react/scheduling.

Use when

  • A sidebar or panel calendar that jumps a view to a date and marks days with events.
  • Building a custom picker that needs the same grid and keyboard model as DatePicker.
  • Showing availability for booking, with unavailable days blocked.

Avoid when

  • The user needs to type or submit a date in a form: use DatePicker.
  • Picking a period: use DateRangePicker.
  • Showing events with titles and times across a month: use CalendarMonthView.

Design principles

  • Dates are ISO YYYY-MM-DD strings throughout, so a day never shifts with the time zone.
  • One Tab stop for the whole grid; arrow keys, Home/End and PageUp/PageDown move inside it, as in the WAI-ARIA APG date-picker dialog.
  • Selection, today and unavailable days each have a non-colour cue: a filled day, a ring and bold numeral, and a line-through (out-of-bounds days are muted and announced as unavailable).
  • Locale decides names, numerals and the first day of the week; a prop can override the week start.
  • Products own meaning: availability, events and holidays come in through isDateDisabled and getDayInfo.

API reference

import { MiniMonth } from '@rookcom/monarch-react/scheduling';
ExportKindRenderingRoot class
MiniMonthcomponentclient.mn-mini-month

The ref goes to the root <div>; other props go there too.

range and rangePreview turn the grid into a range display for DateRangePicker; onValueChange then reports each activated day.

Generated from the TypeScript declarations of packages/react/src/components/scheduling/mini-month.

Properties

value/onValueChange (controlled) or defaultValue; month/onMonthChange (controlled) or defaultMonth for the visible month.

today defaults to the device date and refreshes at midnight. During server rendering and hydration the UTC date is used, then the device date; pass today to fix it (a workspace time zone, tests).

Pass locale when server rendering, so the server and the browser format the same names.

MiniMonth

A month calendar grid with full keyboard support, following the WAI-ARIA APG date-picker grid: arrow keys move by day and week, Home and End to the week's ends, PageUp and PageDown by month (with Shift, by year), Enter or Space selects. One roving Tab stop; `aria-selected` on the chosen days and `aria-current="date"` on today. The foundation of DatePicker and DateRangePicker, and usable on its own as a sidebar calendar.

PropTypeDescription
childrenReactNode
classNamestring
defaultMonthstringAny date in the initially visible month (uncontrolled). Default: the value, else today.
defaultValuestring | nullInitially selected date (uncontrolled).
fixedWeeksbooleanAlways six week rows, so the height never changes between months. Default true.
focusOnMountbooleanFocus the current day on mount (when opened in a popover).
footerReactNodeContent under the grid, such as a "Today" button.
getDayInfo((date: IsoDate) => MiniMonthDayInfo | undefined)Dots and an accessible description for a day. Called for every visible day, so keep it cheap.
idstring
isDateDisabled((date: IsoDate) => boolean)Marks extra dates unavailable (weekends, holidays, booked days). They stay focusable but cannot be chosen.
labelsPartial<MiniMonthLabels>Visible and accessible text, for translation.
localestringBCP 47 locale for names, numerals and the default week start. Default: the runtime's. Pass it for server rendering.
maxstringLatest selectable date. Navigation stops at its month.
minstringEarliest selectable date. Navigation stops at its month.
monthstringAny date in the first visible month (controlled).
numberOfMonthsnumberConsecutive months shown side by side. Default 1.
onDayHover((date: IsoDate | null) => void)Called when the pointer enters a day or focus moves to one, and with `null` when it leaves the grid.
onMonthChange((month: IsoDate) => void)Called with the first day of the newly visible month.
onValueChange((date: IsoDate) => void)Called with the date the user activates (click, Enter or Space). Not called for disabled dates.
rangeMiniMonthRange | nullHighlights a range instead of a single value: the start and end are selected, the days between are marked in range. For range pickers.
rangePreviewMiniMonthRange | nullA provisional range drawn without selecting it, for example while hovering an end date.
showOutsideDaysbooleanShow the neighbouring months' days, dimmed and inert. Default true (single month only).
showWeekNumbersbooleanShow ISO 8601 week numbers in a leading column.
todaystringToday's date. Default: the device's date, updated at midnight. Pass it to use a workspace time zone.
valuestring | nullSelected date (controlled), `YYYY-MM-DD`. `null` for none.
weekStartsOnWeekdayFirst day of the week, 0 (Sunday) to 6 (Saturday). Default: from the locale.

Variants

PropValuesDescription
numberOfMonths'1''2''…'Consecutive months side by side; focus moves across them. Navigation buttons sit on the outer edges.
showWeekNumbers'true''false'A leading column of ISO 8601 week numbers (row headers).
showOutsideDays'true''false'Dimmed, inert days of the neighbouring months (single month only). Default true.
fixedWeeks'true''false'Always six rows, so the height never jumps. Default true.

States

StateExposed asBehaviour
Selectedaria-selected="true" on the cell, data-selected on the dayFilled with the accent colour.
Todayaria-current="date", data-todayA ring and a bold numeral.
Outside min/maxaria-disabled="true", data-disabledMuted. Still focusable up to the bound so keyboard users keep their place, but cannot be chosen; navigation stops at the bound’s month.
Unavailablearia-disabled="true", data-disabled, data-unavailableBlocked by isDateDisabled inside the bounds: muted and struck through, so it is not shown by colour alone. Focusable, cannot be chosen.
In range / range endsdata-in-range, data-range-start, data-range-endA tinted band between the ends, which are filled.
Previewdata-previewA provisional range (hover or keyboard focus while picking an end).
Focus–Focus ring on the focused day.

Interaction behaviour

  • Clicking a day, or pressing Enter or Space on it, selects it (unless unavailable).
  • Moving focus past the visible month changes the month; the previous and next buttons do too, and stop at min and max.
  • The month title is a polite live region, so month changes are announced.
  • Left and Right follow the text direction (reversed in right-to-left layouts), and the chevrons mirror.
  • Pointer hover and keyboard focus both report the day through onDayHover.

Keyboard support

KeysAction
TabEnters the grid at the selected day, else today, else the first available day.
← / →Previous / next day.
↑ / ↓Same day of the previous / next week.
Home / EndFirst / last day of the week.
Page Up / Page DownSame day of the previous / next month (clamped to the month’s length).
Shift + Page Up / Page DownSame day of the previous / next year.
Enter / SpaceSelect the focused day.

Focus never moves past min or max: keys clamp to the bound.

Accessibility

Role
grid (one per month), with column headers, gridcells and a button per day
Accessible name
Each grid is labelled by its month title; each day by its full date ("Wednesday 4 November 2026") plus the getDayInfo description.
  • Follows the WAI-ARIA APG date-picker grid: roving tabindex, aria-selected on cells, aria-current="date" on today.
  • Weekday headers carry the full name in abbr; week numbers are row headers named "Week 45".
  • Event dots are decorative (aria-hidden); their meaning must be in description.
  • Outside days are hidden from assistive technology and cannot be focused.
  • Day cells are 36px, 44px on touch screens that have the room.

Responsive behaviour

  • One month fits a 320px screen, also with week numbers (cells shrink slightly under 352px).
  • With several months the months wrap onto new lines when the container is too narrow.
  • Touch screens get 44px day and navigation targets from 384px up.

Theme support

  • Every colour is a Monarch token: accent for selection, accent-subtle for ranges, status colours for dots, so all themes and more-contrast modes apply.
  • Forced-colours mode outlines the selection and the range, and draws dots in the text colour.
  • Transitions are removed under prefers-reduced-motion: reduce.

Do and don't

Do

  • Pass locale (and today if you use a workspace time zone) so server and browser agree.
  • Describe what dots mean in getDayInfo().description.
  • Use isDateDisabled for unavailable days rather than hiding them.

Don't

  • Do not convert ISO dates to Date in local time to compare or format them; use the strings.
  • Do not put a MiniMonth in a form as the only way to enter a date: use DatePicker, which also accepts typing.
  • Do not show more than three dots per day; summarise in the description instead.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsNone

Date maths and formatting come from the internal Intl-based helpers (no date library).

Testing coverage

  • Labelled grid per month with full weekday names in abbr; axe clean.
  • aria-selected on the selection, aria-current="date" on today; one roving Tab stop.
  • Arrow keys, Home/End, PageUp/PageDown and Shift+PageUp/PageDown, clamping the day and crossing months.
  • Click, Enter and Space select; controlled value and controlled month.
  • min/max clamp keys and disable navigation; isDateDisabled days stay focusable but cannot be chosen.
  • Week start from the locale or the prop; ISO week numbers as row headers.
  • Event dots (max three) and their description in the accessible name.
  • Range and preview highlighting; onDayHover on hover and leave.
  • Two months with focus moving between them; RTL reverses Left and Right; focusOnMount.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/mini-month; Cross-browser axe: /components/mini-month

Tests: packages/react/src/components/scheduling/mini-month