# Calendar
A month grid for picking one date, several dates or a range, with keyboard navigation across months.
```ehtml Example
import { Calendar } from "@elements/ui/calendar";
```
## Parts
| Part | What it is |
|---|---|
| `Calendar` | The template: one grid per shown month, its caption and navigation, and a footer live region. |
| `.calendar-month` | One month: caption and grid. data-month is YYYY-MM. |
| `.calendar-caption` | Previous button, the month label (a polite live region) or the month and year dropdowns, next button. |
| `.calendar-day` | A day cell (role=gridcell) carrying the day's state as data attributes. |
| `.calendar-day-button` | The day's button; the only one in the Tab order is the focus target. |
| `.calendar-week-number` | A week number rowheader (showWeekNumber); its number is a .calendar-week-number-button with weekNumberButtons. |
| `.calendar-nav-button` | Previous and next month, plus .is-previous-year and .is-next-year with yearButtons. |
| `.calendar-help` | A polite live region for the help text once focus rests on a day (help). |
| `.calendar-footer` | A polite live region for the footer text and slot. |
## Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| `mode` | `"single" \| "multiple" \| "range" \| "none"` | `"single"` | What a click selects. none selects nothing; onDayClick still fires. |
| `value` | `ISODate` (two-way) | `""` | The selected date in single mode, "YYYY-MM-DD". |
| `values` | `ISODate[]` (two-way) | `[]` | The selected dates in multiple mode. |
| `from` | `ISODate` (two-way) | `""` | Range start. |
| `to` | `ISODate` (two-way) | `""` | Range end. |
| `month` | `ISODate` (two-way) | `""` | The first month shown (YYYY-MM or any date in it). Unset: defaultMonth, else the selection, else today. |
| `defaultMonth` | `ISODate` | `""` | The first month shown when month is unset. |
| `startMonth` | `ISODate` | `""` | The earliest month the buttons, keyboard and dropdowns reach. |
| `endMonth` | `ISODate` | `""` | The latest month they reach. |
| `fromMonth, toMonth, fromYear, toYear` | `ISODate, ISODate, number, number` | `"", "", 0, 0` | Older names for the bounds: fromMonth and toMonth are startMonth and endMonth, fromYear starts at its January, toYear ends at its December. startMonth and endMonth win. |
| `numberOfMonths` | `number` | `1` | Months shown side by side; they stack on a phone. |
| `pagedNavigation` | `boolean` | `false` | Previous and next move by numberOfMonths. |
| `reverseMonths` | `boolean` | `false` | Shows the months latest first. |
| `disabled` | `Matcher \| Matcher[]` | `false` | Days that cannot be selected or focused: true, a date, dates, {from,to}, {before}, {after}, {before,after}, {dayOfWeek}, or (date) => boolean. |
| `hidden` | `Matcher \| Matcher[]` | `false` | Days not rendered; the keyboard skips them. |
| `modifiers` | `Record` | `{}` | Custom day states, written to data-modifiers on the cell for styling. |
| `required` | `boolean` | `false` | The selection cannot be cleared by clicking it. |
| `min` | `number` | `0` | Multiple: fewest dates. Range: fewest nights. |
| `max` | `number` | `0` | Multiple: most dates (one more restarts). Range: most nights. |
| `excludeDisabled` | `boolean` | `false` | Range: a range over a disabled day restarts at the clicked day. |
| `resetOnSelect` | `boolean` | `false` | Range: the next click after a complete range starts a new one. |
| `showOutsideDays` | `boolean` | `true` | Shows the adjacent months' days, dimmed and selectable. |
| `fixedWeeks` | `boolean` | `false` | Always six weeks, so the height never changes. |
| `showWeekNumber` | `boolean` | `false` | A week number column. |
| `weekNumberButtons` | `boolean` | `false` | Each week number is a button; onWeekNumberClick reports its week. |
| `hideWeekdays` | `boolean` | `false` | Hides the weekday header row. |
| `weekStartsOn` | `number` | `-1` | 0 Sunday to 6 Saturday; -1 follows the locale. |
| `firstWeekContainsDate` | `number` | `0` | Week 1 holds this day of January; 0 follows the locale. |
| `isoWeek` | `boolean` | `false` | ISO 8601 weeks: Monday first, ISO week numbers. |
| `broadcastCalendar` | `boolean` | `false` | US broadcast calendar: Monday first, whole weeks ending on the month's last Sunday. |
| `captionLayout` | `"label" \| "dropdown" \| "dropdown-months" \| "dropdown-years"` | `"label"` | Native month and year selects in the caption. |
| `reverseYears` | `boolean` | `false` | Newest year first in the year dropdown. |
| `dropdownStyle` | `"native" \| "listbox"` | `"native"` | The caption dropdowns as native selects, or as the library's Select (combobox and listbox). |
| `navLayout` | `"around" \| "after"` | `"around"` | Buttons beside the caption, or both after it in the last month. |
| `hideNavigation` | `boolean` | `false` | No previous and next buttons. |
| `yearButtons` | `boolean` | `false` | Previous and next year buttons beside the month buttons (WAI-ARIA date picker dialog). |
| `disableNavigation` | `boolean` | `false` | Buttons and dropdowns disabled; the keyboard stays in the shown months. |
| `animate` | `boolean` | `false` | Slides the weeks on a month change; off under reduced motion. |
| `locale` | `string` | `""` | BCP 47 tag for names, first weekday and week rule (Intl). Empty uses the page's lang. |
| `numerals` | `string` | `""` | Numbering system: latn, arab, deva, thai and others; ethi writes Ge'ez numerals. |
| `calendar` | `string` | `""` | An Intl calendar the grid follows: persian, islamic-umalqura, buddhist, ethiopic, hebrew. Values stay Gregorian ISO dates. |
| `dir` | `"" \| "ltr" \| "rtl"` | `""` | Text direction; the arrows follow it. |
| `today` | `ISODate` | `""` | The date treated as today; the device's date when empty. |
| `timeZone` | `string` | `""` | An IANA time zone ("Pacific/Auckland"): today is the date there. Dates stay plain ISO days. |
| `footer` | `string` | `""` | Text in the footer live region, such as the picked date. |
| `help` | `string` | `""` | Keyboard help announced in a polite live region once focus rests on a day. |
| `label` | `string` | `""` | aria-label on the root. |
| `role` | `"" \| "application" \| "dialog" \| "group"` | `""` | A role for the root. |
| `autoFocus` | `boolean` | `false` | Focuses the focus target when the calendar appears. |
| `labels` | `Partial` | `{}` | Accessible names: nav, previous, next, previousYear, nextYear, monthDropdown, yearDropdown, weekNumberHeader, weekNumber(n), weekday(date, locale), day(date, modifiers, locale), grid(month, locale). |
| `formatters` | `Partial` | `{}` | Display text: caption, day, weekday, weekNumber, weekNumberHeader, monthDropdown, yearDropdown. |
| `dayContent` | `(date, modifiers) => string` | `() => ""` | A second line inside each day button, such as a price; aria-hidden, and an empty string shows none. |
| `class` | `string` | `""` | Classes on the root. Set --calendar-cell-size through one for larger cells. |
| `attrs` | `Record` | `{}` | Other attributes for the root: id, title, data-\*, aria-\* (aria-labelledby). |
| `onValueChange` | `(value: ISODate) => void` | `() => {}` | The calendar changed value. |
| `onValuesChange` | `(values: ISODate[]) => void` | `() => {}` | The calendar changed values. |
| `onRangeChange` | `(range: { from; to }) => void` | `() => {}` | The calendar changed the range. |
| `onSelect` | `(date: ISODate, modifiers: DayModifiers) => void` | `() => {}` | A day was activated in a selection mode, even when the rules left the selection as it was. |
| `onMonthChange` | `(month: ISODate) => void` | `() => {}` | The first shown month changed. |
| `onPrevClick, onNextClick` | `(month: ISODate) => void` | `() => {}` | A navigation button moved the months. |
| `onPrevYearClick, onNextYearClick` | `(month: ISODate) => void` | `() => {}` | A year button moved the months (yearButtons). |
| `onWeekNumberClick` | `(week: number, dates: ISODate[], e: MouseEvent) => void` | `() => {}` | A week number button was clicked (weekNumberButtons): its number and its seven dates. |
| `onDayClick` | `(date, modifiers, e: MouseEvent) => void` | `() => {}` | A day was clicked. e.preventDefault() cancels the selection. |
| `onDayKeyDown` | `(date, modifiers, e: KeyboardEvent) => void` | `() => {}` | A key on a day. e.preventDefault() cancels the calendar's handling. |
| `onDayFocus, onDayBlur` | `(date, modifiers, e: FocusEvent) => void` | `() => {}` | A day gained or lost focus. |
| `onDayMouseEnter, onDayMouseLeave` | `(date, modifiers, e: PointerEvent) => void` | `() => {}` | The pointer entered or left a day. |
## Slots
| Slot | Content |
|---|---|
| `footer` | Content under the months, inside the polite live region. |
## Keyboard
| Keys | Action |
|---|---|
| `ArrowLeft / ArrowRight` | Previous / next day (flipped under rtl). |
| `ArrowUp / ArrowDown` | Same day of the previous / next week. |
| `Shift + ArrowLeft / ArrowRight` | Same day of the previous / next month. |
| `Shift + ArrowUp / ArrowDown` | Same day of the previous / next year. |
| `PageUp / PageDown` | Same day of the previous / next month; a missing day lands on the month's last day. |
| `Shift + PageUp / PageDown` | Same day of the previous / next year. |
| `Home / End` | First / last day of the week. |
| `Enter / Space` | Selects the focused day. |
| `Tab` | Moves between the navigation, the dropdowns and the one focusable day. |
## Data attributes
| Attribute | Values | On | Description |
|---|---|---|---|
| `data-mode` | the mode | root | |
| `data-animate` | `"before" \| "after"` | root | during a month animation |
| `data-month` | YYYY-MM | .calendar-month | |
| `data-day` | YYYY-MM-DD | .calendar-day | |
| `data-selected` | present | .calendar-day | |
| `data-today` | present | .calendar-day | |
| `data-outside` | present | .calendar-day | another month's day |
| `data-disabled` | present | .calendar-day | |
| `data-hidden` | present | .calendar-day | an empty cell |
| `data-focused` | present | .calendar-day | |
| `data-range-start, data-range-middle, data-range-end` | present | .calendar-day | |
| `data-modifiers` | space-separated names | .calendar-day | style with [data-modifiers~="booked"] |
## Accessibility
- Each month is a role=grid named by its month and year; aria-multiselectable in multiple and range modes.
- Weekday headers are columnheaders named with the full weekday; week numbers are rowheaders named "Week 44".
- Days are gridcells with aria-selected on the selection; each button is named like "Today, Thursday, October 29, 2026, selected".
- One Tab stop across all shown months: the focused day, else the selection, else today, else the first enabled day.
- The month label is a polite live region, so a month change is announced; the footer is a polite live region for selection feedback.
- Disabled days are disabled buttons with aria-disabled on the cell and are skipped by the keyboard.
## See also
- [DatePicker](date-picker): A button or text field that picks one date from a Calendar in a popover.
- [DateRangePicker](date-range-picker): A button that picks a start and end date from a two-month range Calendar in a popover.
- [TimePicker](time-picker): A time of day in the browser's own time field, styled as a control, with steps, limits and suggestions.