The month grid on its own: a single date or a range, one or two months, with month and year pickers.
Calendar is the grid DateField and DateRangeFilter open in their popovers, usable on its own when a date picker should stay on the page (a booking panel, a report range). It takes a single value with onChange, or a range with onRangeChange, where the first pick starts the range, the second ends it and hovering previews it.
min, max and isDateDisabled bound what can be picked; disabled days stay focusable so keyboard users can pass over them. numberOfMonths={2} shows two months side by side; captionLayout="dropdown" adds Month and Year pickers for dates years away. Dates are ISO strings (2026-10-14), never Date objects, so time zones never shift a day.
Usage
import { Calendar, type DateRange, type IsoDate } from "@/components/ui";
<Calendar value={date} onChange={setDate} />Examples
Single date
October 2026
Range, two months
October 2026
November 2026
Month and year pickers
June 1990
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
Calendar
| Prop | Type | Default | Description |
|---|---|---|---|
| isDateDisabled | (date: IsoDate) => boolean | Days that cannot be picked. Still focusable, so keyboard users can pass over them. | |
| min | IsoDate | ||
| max | IsoDate | ||
| defaultMonth | IsoDate | Month shown first when there is no value. Defaults to today's month. | |
| autoFocus | boolean | Moves focus into the grid on mount (used by DateField's popup). | |
| numberOfMonths | 1 | 2 | 1 | 2 | 1 | Months side by side. Navigation moves them together. |
| captionLayout | "label" | "dropdown" | "label" | "dropdown" | "label" | label: the month and year as a heading, with month and year arrows. dropdown: Month ▾ and Year ▾ buttons that open quick pickers, for dates years away such as a date of birth (a sign-up form). Month arrows stay. Two-month calendars always use labels. |
| fromYear | number | First and last years the Year picker lists (dropdown only). Default to min's and max's years, else 100 years back and 10 on. They also bound the arrows and keyboard paging, so the grid never shows a year the picker lacks. | |
| toYear | number | ||
| className | string | ||
| valueRequired | IsoDate | null | never | ||
| onChangeRequired | (value: IsoDate) => void | never | ||
| range | never | DateRange | Range mode. After a start is picked, hovering or arrowing previews the range. | |
| onRangeChange | never | (range: DateRange) => void |
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
This component has no tokens of its own; it reads the system tokens below.
System tokens it reads
| Token | Default | Live value |
|---|---|---|
| --brand | tokens.css | … |
| --brand-gradient | tokens.css | … |
| --dur | tokens.css | … |
| --dur-fast | tokens.css | … |
| --ease | tokens.css | … |
| --focus-ring | tokens.css | … |
| --font-body | tokens.css | … |
| --glow-sm | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-brand | tokens.css | … |
| --ink-disabled | tokens.css | … |
| --ink-heading | tokens.css | … |
| --ink-inverse | tokens.css | … |
| --ink-muted | tokens.css | … |
| --line | tokens.css | … |
| --line-brand | tokens.css | … |
| --line-brand-strong | tokens.css | … |
| --r-xs | tokens.css | … |
| --rgb-brand-deep | tokens.css | … |
| --text-micro | tokens.css | … |
| --wash-active | tokens.css | … |
| --wash-hover | tokens.css | … |
| --wash-strong | tokens.css | … |
| --wash-subtle | tokens.css | … |
Accessibility
- The days are a grid: arrows move by day and week, PageUp and PageDown by month (Shift for a year), Home and End to the week's ends, Enter or Space picks.
- Each day is named by its full date, and the picked day carries aria-selected, so the choice is never shown by colour alone.
Do and don't
Do
- Use DateField when people may know the date and want to type it.
- Bound the grid with min and max.
Don't
- Don't force a calendar for a date of birth; let people type it.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open calendar.mdRelated: date-field, date-range-filter