# 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.