Calendar
A grid-backed calendar for single-date and range selection with keyboard navigation and selection modes.
Features
- Multiple selection modes
- Grid-backed structure
- Controlled or uncontrolled
- Extensible header
- Rich date state
- Keyboard navigation
Installation
npm install @ariaui/calendar
Examples
Each playground is a self-contained example: single date, range, manual grid composition, dual-range two-pane layout, and month/year Select controls in the header.
Styling uses semantic tokens (border-border, bg-card, text-foreground, etc.) and lucide-react icons. Snippets for the manual grid interpolate the same class strings as the live preview.
Single date
One selected day with header navigation and manual-grid styling.
No preview output yet.
Range
Start and end dates with in-range highlighting.
No preview output yet.
Manual grid
Compose Calendar.Row and Calendar.Cell with Grid.Head and Grid.Body for full control over the matrix.
No preview output yet.
Dual range
Two-month layout with a shared range selection model.
No preview output yet.
Month and year selectors
Replace static month and year labels with Select-driven controls wired to calendar context.
No preview output yet.
Anatomy
import * as Calendar from "@ariaui/calendar";
export default function Example() {
return (
<Calendar.Root>
<Calendar.Header>
<Calendar.HeaderPrevious />
<Calendar.HeaderMonth />
<Calendar.HeaderYear />
<Calendar.MonthSelect />
<Calendar.YearSelect />
<Calendar.HeaderNext />
</Calendar.Header>
<Calendar.Body>
<Calendar.Head>
<Calendar.Row>
<Calendar.DayHeader />
</Calendar.Row>
</Calendar.Head>
<Calendar.Rows>
<Calendar.Row>
<Calendar.Cell />
</Calendar.Row>
</Calendar.Rows>
</Calendar.Body>
</Calendar.Root>
);
}
API Reference
Root
Container that owns all calendar state — selected dates, visible month, and selection mode. Provides context to all child parts. Renders a `<div>`.
| Prop | Type | Default |
|---|---|---|
daysInWeek* | Record<string, string> | — |
mode | "single" | "range" | "dual-range" | "single" |
defaultDates | Date[] | [] |
selectedDates | Date[] | — |
onValueChange | (dates: Date[]) => void | — |
visibleMonth | Date | — |
onVisibleMonthChange | (date: Date) => void | — |
| Attribute | Values |
|---|---|
| [data-slot] | "calendar-root" |
Header
Layout container for calendar header controls. When `children` is omitted, renders built-in navigation buttons and month/year labels. Renders a `<div>`.
| Attribute | Values |
|---|---|
| [data-slot] | "calendar-header" |
Body
Renders the weekday header row and the grid-backed date cells for the visible month. In `dual-range` mode, renders both month panes automatically. Renders a `<div>`.
| Attribute | Values |
|---|---|
| [data-slot] | "calendar-body" |
Cell
Individual date cell. Wraps the shared Grid.Cell primitive and layers date-specific selection and range state on top. Renders a `<td>`.
| Prop | Type | Default |
|---|---|---|
date* | Date | — |
isOutsideMonth | boolean | false |
| Attribute | Values |
|---|---|
| [data-slot] | "calendar-cell" |
| [data-selected] | Present when the date is selected |
| [data-today] | Present when the date is today |
| [data-range-start] | Present when the date is the range start endpoint |
| [data-range-end] | Present when the date is the range end endpoint |
| [data-in-range] | Present when the date falls within the committed range |
Keyboard
| Shortcut | Action |
|---|---|
| → | Move focus to the next day. Crossing a week or month boundary updates the visible month. |
| ← | Move focus to the previous day. Crossing a week or month boundary updates the visible month. |
| ↓ | Move focus to the same day of the next week. |
| ↑ | Move focus to the same day of the previous week. |
| Home | Move focus to the first day of the current week. |
| End | Move focus to the last day of the current week. |
| Enter/Space | Select the focused date. |
| PageDown | Move the calendar forward one month. |
| PageUp | Move the calendar back one month. |
| Shift+PageDown | Move the calendar forward one year. |
| Shift+PageUp | Move the calendar back one year. |
Grid cell focus automatically updates the visible month when navigating across boundaries, and day-number fallback is applied when jumping to a month that lacks the target day.
Accessibility
The Calendar component implements the WAI-ARIA Date Picker Dialog pattern. Date cells use a grid structure with proper ARIA attributes:
[aria-selected="true"]marks the selected date(s)[aria-disabled="true"]marks dates outside the visible month[data-today]identifies the current date for visual distinction- Range mode exposes
[data-range-start],[data-range-end], and[data-in-range]for styling range spans
Each date cell is keyboard-focusable and selectable via Enter/Space. Month/year navigation is exposed in the header via buttons with accessible labels.