macbetter

Text and fields

Calendar

Pick a date or a range from a month that stays on screen.

November 2026

SunMonTueWedThuFriSat
1234567
891011121314
15161718192021
22232425262728
2930
import { Calendar } from "macbetter/calendar"

export default function Example() {
  return <Calendar aria-label="Delivery date" defaultValue="2026-11-20" />
}

When to use it

Use a calendar when the month itself helps: choosing a delivery day, seeing which days are free, picking a reporting period. It stays on the page; for a field that opens a calendar only when asked, use a Date Picker.

Today is marked in accent text. The chosen day fills with the accent. Days that can't be picked are dimmed, and stay focusable so keyboard users hear why.

Usage

import { Calendar } from "macbetter/calendar"

Values are ISO strings like "2026-11-20". With no children, Calendar draws its header and a grid for each month. To change one part, write the parts out: CalendarHeader, CalendarHeading, CalendarPrevious, CalendarNext and CalendarGrid, which draws its own weekday row and days.

Ranges and two months

mode="range" picks a start and an end. Moving the pointer previews the range, and the days between the two circles join into one continuous band that rounds off where a week wraps. months={2} shows two months side by side.

November – December 2026

SunMonTueWedThuFriSat
1234567
891011121314
15161718192021
22232425262728
2930
SunMonTueWedThuFriSat
12345
6789101112
13141516171819
20212223242526
2728293031
import { Calendar } from "macbetter/calendar"

export default function Example() {
  return (
    <Calendar
      aria-label="Report period"
      mode="range"
      months={2}
      defaultValue={["2026-11-09", "2026-11-19"]}
    />
  )
}

Compact

size="sm" is the dense month of a macOS popover or sidebar, with single-letter weekdays. unavailable takes dates, ranges, weekdays or a function.

November 2026

SMTWTFS
1234567
891011121314
15161718192021
22232425262728
2930
import { Calendar } from "macbetter/calendar"

export default function Example() {
  return <Calendar size="sm" aria-label="Go to date" defaultValue="2026-11-18" unavailable={{ weekday: [0, 6] }} />
}

Platform behavior

  • macOS. 13-point days in 26-point circles, a 13-point semibold month title, and small gray chevrons that highlight under the pointer.
  • iPadOS and iOS. 20-point days in 40-point circles, a 17-point semibold title, uppercase weekday abbreviations, and accent chevrons with 44-point touch targets, as in the iOS graphical date picker.
  • Every platform. Pressing a day gives a small, instant squeeze. Days from neighboring months are hidden unless you pass showOutsideDays.

Accessibility

  • The month is a grid named by its title, and the title is announced as it changes.
  • Arrow keys move by day and week, Home and End to the week's ends, Page Up and Page Down by month. Enter or Space picks the focused day; Escape cancels an unfinished range.
  • Previous and Next are named "Previous month" and "Next month"; pass aria-label in another language.

API

Calendar

PropTypeDefaultDescription
size"sm" | "md""md"Compact popover month, or the full graphical picker.
mode"single" | "multiple" | "range""single"One date, any number, or a start and an end.
valuestring | string[] | [string, string] | null—The selection, for a controlled calendar.
defaultValuestring | string[] | [string, string] | null—The selection it starts with.
onValueChange(value, details) => void—Runs when a day is picked, or a range's end.
monthsnumber1How many months show side by side.
minstring—The earliest date that can be picked.
maxstring—The latest date that can be picked.
unavailableDateMatchers—Dates that can't be picked.
weekdayFormat"narrow" | "short" | "long""short", or "narrow" at smHow weekday headers are written.
showOutsideDaysbooleanfalseShows the neighboring months' days at the edges.
todaystring—Today's date, when it isn't the device's.

CalendarHeader takes the props of a div. CalendarGrid takes an offset for the month it shows. CalendarDay draws its number in a circle unless it has children.

Data attributes

AttributeOnValues
data-sizeCalendarsm, md
data-selectedDayPresent on picked days; in a range, every day from start to end
data-todayDayPresent on today
data-range-start, data-range-middle, data-range-endDayWhere the day sits in a range
data-previewDayPresent on days of a range being picked
data-unavailable, data-disabledDayCan't be picked

CSS variables

VariablemacOSiOSWhat it sets
--mb-calendar-cell-width32px46pxColumn width
--mb-calendar-cell-height30px44pxRow height
--mb-calendar-circle26px40pxDay circle
--mb-calendar-day-font13px20pxDay numbers
--mb-calendar-bandAccent tintAccent tintA range's band, set on the root