Calendar
Accessible, unstyled calendar compound primitive with a timezone-free date model.
|
|
| Package |
@vizejs/ui/calendar |
| Maturity |
stable |
| Own the source |
vize lib pull calendar |
| Requires |
context, controllable-state, id, locale |
| Aliases |
calendar, date grid, month view, mini calendar, day picker |
| Covers |
WAI-ARIA APG date picker dialog grid, React Aria Calendar, Reka UI Calendar, Radix-style calendar composition |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Calendar, CalendarRoot, CalendarGrid, CalendarMultipleRoot, MultipleCalendar, CalendarHeading, CalendarMonthSelect, CalendarNext, CalendarPrev, CalendarYearSelect } from "@vizejs/ui/calendar";
Or copy the source into your project with vize lib pull calendar (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Single-date calendar bound with v-model, a fixed SSR-safe today, and a bookable range. -->
<script setup lang="ts">
import { ref } from "vue";
import {
CalendarGrid,
CalendarHeading,
CalendarNext,
CalendarPrev,
CalendarRoot,
createPlainDate,
formatIsoDate,
type PlainDate,
} from "@vizejs/ui/calendar";
const today = createPlainDate(2026, 10, 1);
const selected = ref<PlainDate | null>(createPlainDate(2026, 10, 24));
const min = createPlainDate(2026, 10, 1);
const max = createPlainDate(2026, 12, 31);
</script>
<template>
<div>
<CalendarRoot v-model="selected" :today :min :max locale="en-US" name="event-date">
<CalendarPrev />
<CalendarHeading />
<CalendarNext />
<CalendarGrid />
</CalendarRoot>
<output>Selected: {{ selected ? formatIsoDate(selected) : "none" }}</output>
</div>
</template>
API
Calendar
Source: src/families/date-time/calendar/calendar-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
PlainDate | null | undefined |
undefined |
Controlled selected date; undefined selects uncontrolled mode and null clears. |
defaultValue |
PlainDate | null | undefined |
null |
Initial uncontrolled selected date. |
focusedDate |
PlainDate | null | undefined |
undefined |
Controlled keyboard focus date; also decides which months are visible. |
min |
PlainDate | null | undefined |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null | undefined |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only, for example japanese. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
dir |
CalendarDirection | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat | undefined |
"short" |
Weekday column label width. |
numberOfMonths |
number | undefined |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean | undefined |
false |
Move month controls by numberOfMonths instead of one month. |
fixedWeeks |
boolean | undefined |
false |
Always render six week rows per month. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark today. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
name |
string | undefined |
undefined |
Hidden input name that submits the ISO date with forms. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the calendar group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the calendar group; defaults to the heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the calendar group. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: PlainDate | null] |
Fired when the calendar requests a new controlled value. |
change |
[value: PlainDate | null, previous: PlainDate | null, nativeEvent: Event | null] |
Fired after a distinct selection with the previous value and triggering event. |
select |
[value: PlainDate, nativeEvent: Event] |
Fired for every user activation of a selectable date, even when the value is unchanged. |
update:focusedDate |
[date: PlainDate] |
Fired when keyboard or navigation moves the focus date. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Calendar composition. Receives months, weekdays, heading, and state; defaults to a header plus one grid per month. |
Exposed
| Member |
Type |
Description |
value |
— |
|
setValue |
— |
|
CalendarRoot
Source: src/families/date-time/calendar/calendar-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
PlainDate | null | undefined |
undefined |
Controlled selected date; undefined selects uncontrolled mode and null clears. |
defaultValue |
PlainDate | null | undefined |
null |
Initial uncontrolled selected date. |
focusedDate |
PlainDate | null | undefined |
undefined |
Controlled keyboard focus date; also decides which months are visible. |
min |
PlainDate | null | undefined |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null | undefined |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only, for example japanese. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
dir |
CalendarDirection | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat | undefined |
"short" |
Weekday column label width. |
numberOfMonths |
number | undefined |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean | undefined |
false |
Move month controls by numberOfMonths instead of one month. |
fixedWeeks |
boolean | undefined |
false |
Always render six week rows per month. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark today. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
name |
string | undefined |
undefined |
Hidden input name that submits the ISO date with forms. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the calendar group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the calendar group; defaults to the heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the calendar group. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: PlainDate | null] |
Fired when the calendar requests a new controlled value. |
change |
[value: PlainDate | null, previous: PlainDate | null, nativeEvent: Event | null] |
Fired after a distinct selection with the previous value and triggering event. |
select |
[value: PlainDate, nativeEvent: Event] |
Fired for every user activation of a selectable date, even when the value is unchanged. |
update:focusedDate |
[date: PlainDate] |
Fired when keyboard or navigation moves the focus date. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Calendar composition. Receives months, weekdays, heading, and state; defaults to a header plus one grid per month. |
Exposed
| Member |
Type |
Description |
mode |
CalendarSelectionMode |
Selection model of the root. |
months |
readonly CalendarMonthState[] |
Visible month grids; empty while pending. |
weekdays |
readonly CalendarWeekdayLabel[] |
Localized weekday labels in column order. |
heading |
string |
Localized heading for the visible months. |
focusedDate |
PlainDate | null |
Keyboard focus target, or null while pending. |
today |
PlainDate | null |
Resolved current date, or null before a clock is available. |
locale |
string |
Resolved BCP 47 locale. |
direction |
CalendarDirection |
Resolved text direction. |
weekStartsOn |
Weekday |
Resolved first day of week. |
disabled |
boolean |
Whether every control is disabled. |
readOnly |
boolean |
Whether selection is blocked while navigation works. |
pending |
boolean |
Whether no date is known yet: no value, focused date, today, or now was supplied, so the grid waits for the client clock after mount. |
state |
CalendarState |
Stable root state token. |
root |
HTMLDivElement | null |
Rendered root element. |
focus |
(options?: FocusOptions) => boolean |
Focus the roving day button; returns whether focus moved. |
setFocusedDate |
(date: PlainDate) => void |
Move the keyboard focus target (and visible months) to a date, clamped to min/max. |
setVisibleMonth |
(month: PlainYearMonth) => void |
Show a month as the first visible month. |
navigate |
(unit: CalendarNavigationUnit, direction: -1 | 1) => boolean |
Navigate by one page of months or years; returns whether the view moved. |
value |
PlainDate | null |
Current selected date. |
setValue |
(value: PlainDate | null) => boolean |
Request a new value; returns whether it differs. |
CalendarGrid
Source: src/families/date-time/calendar/calendar-grid.vue
Props
| Prop |
Type |
Default |
Description |
monthIndex |
number |
0 |
Zero-based index of the visible month this grid renders. |
showWeekNumbers |
boolean |
false |
Render a leading row header with the ISO 8601 week number of each row. |
weekNumberLabel |
string |
"Wk" |
Visible week-number column header. |
weekNumberHeader |
string |
"Week" |
Full week-number column header used as abbr. |
Slots
| Slot |
Slot props |
Description |
weekday |
CalendarWeekdaySlotState |
Weekday column header content. Receives the weekday, its labels, and column index. |
weekNumber |
CalendarWeekNumberSlotState |
Week-number row header content. Receives the ISO week and the row's days. |
day |
CalendarDayState |
Day button content. Receives the complete day state; defaults to the localized day number. |
CalendarMultipleRoot
Source: src/families/date-time/calendar/calendar-multiple-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
readonly PlainDate[] | undefined |
undefined |
Controlled selected dates; undefined selects uncontrolled mode. |
defaultValue |
readonly PlainDate[] | undefined |
[] |
Initial uncontrolled selected dates. |
maxSelections |
number | undefined |
undefined |
Largest number of selected dates; activating another unselected date is ignored. |
focusedDate |
PlainDate | null | undefined |
undefined |
Controlled keyboard focus date; also decides which months are visible. |
min |
PlainDate | null | undefined |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null | undefined |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only, for example japanese. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
dir |
CalendarDirection | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat | undefined |
"short" |
Weekday column label width. |
numberOfMonths |
number | undefined |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean | undefined |
false |
Move month controls by numberOfMonths instead of one month. |
fixedWeeks |
boolean | undefined |
false |
Always render six week rows per month. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark today. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
name |
string | undefined |
undefined |
Hidden input name; one input per selected ISO date is submitted. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the calendar group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the calendar group; defaults to the heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the calendar group. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: readonly PlainDate[]] |
Fired when the calendar requests new controlled dates (sorted, unique). |
change |
[value: readonly PlainDate[], previous: readonly PlainDate[], nativeEvent: Event | null] |
Fired after a distinct selection with the previous dates and triggering event. |
toggle |
[date: PlainDate, selected: boolean, nativeEvent: Event] |
Fired after a user activation toggles a date, with its new selected state. |
update:focusedDate |
[date: PlainDate] |
Fired when keyboard or navigation moves the focus date. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Calendar composition. Receives months, weekdays, heading, and state; defaults to a header plus one grid per month. |
Exposed
| Member |
Type |
Description |
mode |
CalendarSelectionMode |
Selection model of the root. |
months |
readonly CalendarMonthState[] |
Visible month grids; empty while pending. |
weekdays |
readonly CalendarWeekdayLabel[] |
Localized weekday labels in column order. |
heading |
string |
Localized heading for the visible months. |
focusedDate |
PlainDate | null |
Keyboard focus target, or null while pending. |
today |
PlainDate | null |
Resolved current date, or null before a clock is available. |
locale |
string |
Resolved BCP 47 locale. |
direction |
CalendarDirection |
Resolved text direction. |
weekStartsOn |
Weekday |
Resolved first day of week. |
disabled |
boolean |
Whether every control is disabled. |
readOnly |
boolean |
Whether selection is blocked while navigation works. |
pending |
boolean |
Whether no date is known yet: no value, focused date, today, or now was supplied, so the grid waits for the client clock after mount. |
state |
CalendarState |
Stable root state token. |
root |
HTMLDivElement | null |
Rendered root element. |
focus |
(options?: FocusOptions) => boolean |
Focus the roving day button; returns whether focus moved. |
setFocusedDate |
(date: PlainDate) => void |
Move the keyboard focus target (and visible months) to a date, clamped to min/max. |
setVisibleMonth |
(month: PlainYearMonth) => void |
Show a month as the first visible month. |
navigate |
(unit: CalendarNavigationUnit, direction: -1 | 1) => boolean |
Navigate by one page of months or years; returns whether the view moved. |
value |
readonly PlainDate[] |
Selected dates, sorted and unique. |
setValue |
(value: readonly PlainDate[]) => boolean |
Replace the selection; returns whether it differs. |
toggle |
(date: PlainDate) => boolean |
Toggle one date; returns whether the selection changed. |
MultipleCalendar
Source: src/families/date-time/calendar/calendar-multiple-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null | undefined |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
readonly PlainDate[] | undefined |
undefined |
Controlled selected dates; undefined selects uncontrolled mode. |
defaultValue |
readonly PlainDate[] | undefined |
[] |
Initial uncontrolled selected dates. |
maxSelections |
number | undefined |
undefined |
Largest number of selected dates; activating another unselected date is ignored. |
focusedDate |
PlainDate | null | undefined |
undefined |
Controlled keyboard focus date; also decides which months are visible. |
min |
PlainDate | null | undefined |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null | undefined |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that stay focusable but cannot be selected. |
locale |
string | undefined |
undefined |
BCP 47 locale; defaults to the nearest LocaleProvider. |
calendar |
string | undefined |
undefined |
Intl calendar used for display labels only, for example japanese. |
numberingSystem |
string | undefined |
undefined |
Intl numbering system for labels. |
dir |
CalendarDirection | undefined |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
weekStartsOn |
Weekday | undefined |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat | undefined |
"short" |
Weekday column label width. |
numberOfMonths |
number | undefined |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean | undefined |
false |
Move month controls by numberOfMonths instead of one month. |
fixedWeeks |
boolean | undefined |
false |
Always render six week rows per month. |
today |
PlainDate | null | undefined |
undefined |
Explicit current date; the SSR-safe way to mark today. |
now |
DateTimeNow | undefined |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string | undefined |
undefined |
IANA time zone used with now and the post-mount host clock. |
disabled |
boolean | undefined |
false |
Disable navigation, focus, and selection. |
readOnly |
boolean | undefined |
false |
Allow navigation while blocking selection. |
name |
string | undefined |
undefined |
Hidden input name; one input per selected ISO date is submitted. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the calendar group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the calendar group; defaults to the heading. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the calendar group. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: readonly PlainDate[]] |
Fired when the calendar requests new controlled dates (sorted, unique). |
change |
[value: readonly PlainDate[], previous: readonly PlainDate[], nativeEvent: Event | null] |
Fired after a distinct selection with the previous dates and triggering event. |
toggle |
[date: PlainDate, selected: boolean, nativeEvent: Event] |
Fired after a user activation toggles a date, with its new selected state. |
update:focusedDate |
[date: PlainDate] |
Fired when keyboard or navigation moves the focus date. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Calendar composition. Receives months, weekdays, heading, and state; defaults to a header plus one grid per month. |
Exposed
| Member |
Type |
Description |
value |
— |
|
setValue |
— |
|
toggle |
— |
|
CalendarHeading
Source: src/families/date-time/calendar/calendar-heading.vue
Slots
| Slot |
Slot props |
Description |
default |
CalendarHeadingSlotState |
Heading content. Receives the localized heading and visible months. |
CalendarMonthSelect
Source: src/families/date-time/calendar/calendar-month-select.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Month" |
Accessible name of the native select. |
Events
| Event |
Payload |
Description |
change |
[month: number, nativeEvent: Event] |
Fired after the user picks a month, with the ISO month number. |
CalendarNext
Source: src/families/date-time/calendar/calendar-next.vue
Props
| Prop |
Type |
Default |
Description |
unit |
CalendarNavigationUnit |
"month" |
Whether the control pages by month (or numberOfMonths when paged) or by year. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Next month" or "Next year". |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before navigation. Call preventDefault() to keep the view unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarNavigationSlotState |
Control content. Receives the unit and whether navigation is possible. |
CalendarPrev
Source: src/families/date-time/calendar/calendar-prev.vue
Props
| Prop |
Type |
Default |
Description |
unit |
CalendarNavigationUnit |
"month" |
Whether the control pages by month (or numberOfMonths when paged) or by year. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Previous month" or "Previous year". |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before navigation. Call preventDefault() to keep the view unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarNavigationSlotState |
Control content. Receives the unit and whether navigation is possible. |
CalendarYearSelect
Source: src/families/date-time/calendar/calendar-year-select.vue
Props
| Prop |
Type |
Default |
Description |
from |
number |
undefined |
First listed year; defaults to the min year or ten years before the view. |
to |
number |
undefined |
Last listed year; defaults to the max year or ten years after the view. |
ariaLabel |
string |
"Year" |
Accessible name of the native select. |
Events
| Event |
Payload |
Description |
change |
[year: number, nativeEvent: Event] |
Fired after the user picks a year. |
Behavior
Normative state x input -> outcome table for the Calendar compound
(@vizejs/ui/calendar): calendar-root.vue, calendar-grid.vue,
calendar-heading.vue, calendar-prev.vue, calendar-next.vue,
calendar-month-select.vue, calendar-year-select.vue, and calendar-multiple-root.vue, plus the
timezone-free date model in plain-date.ts. Every row is proven by the named
mounted-DOM, SSR, or unit test; a row without a passing test is a contract
violation.
| # |
State |
Input |
Outcome |
Proven by |
| C1 |
known date |
render |
root role="group" labelled by the heading; each month is a <table role="grid"> named by its month; weekday headers carry abbr; one roving tabindex=0 day; today has aria-current="date"; outside days are inert |
renders an APG date grid with locale weekdays, roving focus, and today |
| C2 |
uncontrolled |
day activation |
emits update:modelValue then change(value, previous, event); selection, data-*, and the hidden ISO input update |
uncontrolled selection emits update and change with the previous date |
| C3 |
controlled |
day activation |
emits the request; rendered selection follows modelValue until the parent accepts it |
controlled value wins until the parent accepts the request |
| C4 |
focused day |
Arrow / Home / End / PageUp / PageDown |
±1 day, ±1 week, week start/end, ±1 month, Shift ±1 year; focus follows into new months; update:focusedDate fires; Tab is not intercepted |
keyboard moves focus by day, week, week edge, month, and year across months |
| C5 |
RTL / locale |
render / ArrowLeft / ArrowRight |
dir flips horizontal arrows; week start comes from CLDR region data (or -u-fw-, or weekStartsOn); labels come from Intl |
RTL flips horizontal arrows and locale week start follows the region |
| C6 |
min / max / predicate |
render / keyboard / activation |
out-of-range days are disabled and focus clamps to the window; unavailable days stay focusable with aria-disabled and never select; prev/next disable at the bounds |
min, max, and unavailable dates constrain focus, navigation, and selection |
| C7 |
multiple months |
prev / next / keyboard |
numberOfMonths grids render side by side; paged navigation moves by the page; moving focus past the window scrolls it by one month; moving back inside does not scroll |
previous and next controls page by month or year and keep focus date in view |
| C8 |
month / year selects |
native change |
the chosen month or year becomes the first visible month; focus date keeps its day clamped to the month; out-of-range months are disabled options |
month and year selects jump the view and respect bounds |
| C9 |
disabled / read-only |
render / activation |
disabled removes every day and control from interaction; read-only keeps navigation and focus but blocks selection with aria-readonly/aria-disabled |
disabled and read-only calendars keep availability semantics |
| C10 |
no value, no clock |
mount |
the grid is pending until mount, then reads the host clock in timeZone, the nearest LocaleProvider zone, or the host zone |
without today or now the calendar is pending until mount reads the injected time zone clock |
| C11 |
LocaleProvider |
render |
locale, direction, display calendar (japanese, buddhist, …), and numbering system are inherited |
LocaleProvider supplies locale, direction, calendar display, and numbering |
| C12 |
imperative |
expose |
focus(), setValue(), setFocusedDate(), setVisibleMonth(), navigate() plus normalized slot state |
exposes focus, setValue, setFocusedDate, setVisibleMonth, and navigate |
| C13 |
custom composition |
default slot |
typed CalendarSlotState drives consumer-rendered grids; fixedWeeks renders six rows; day/weekday slots replace cell content |
custom composition receives typed slot state and renders fixed weeks |
| C14 |
SSR, explicit today |
isolated requests / hydration |
markup is byte-identical, selected <option>s carry selected, and hydration reuses the root with no warnings |
renders byte-identical calendar markup across isolated SSR requests, hydrates a calendar with an explicit today without mismatches |
| C15 |
SSR, no clock |
isolated requests / hydration |
the server renders a deterministic data-state="pending" shell without days; hydration is silent and the client fills the grid after mount |
without today or now SSR renders a pending shell and the client fills it after mount |
| C16 |
SSR, injected now |
server + client |
the injected instant resolves in timeZone identically on both sides |
an injected now renders the same today on server and client |
| D1 |
date model |
arithmetic |
epoch-day math matches UTC across eras and leap rules; month/year arithmetic clamps days |
epoch-day arithmetic matches UTC Date math across eras and leap rules, month, year, and week arithmetic clamps to real calendar days |
| D2 |
date model |
adapters |
Temporal-like records are copied; ISO strings round-trip; Date adapters never shift days; instants resolve per IANA zone |
validation rejects impossible dates and copies Temporal-like records, ISO strings round-trip including extended years, Date adapters read local or UTC fields and never shift days, instants resolve to calendar dates per IANA time zone |
| D3 |
date model |
ranges / locale data |
ranges are ordered; week start and formatters are deterministic |
ranges are ordered, normalized, and compared by day, week start follows CLDR regions, the fw extension, and explicit overrides, formatters render UTC-anchored labels with calendar and numbering overrides |
Multiple selection (calendar-multiple-root.vue)
| # |
State |
Input |
Outcome |
Proven by |
| M1 |
uncontrolled |
day activation |
toggles the date; the value stays sorted and unique; grids are aria-multiselectable; name submits one input per date |
multiple mode toggles dates, keeps them sorted, and submits one input per date |
| M2 |
any |
activation / maxSelections |
emits update:modelValue, change, then toggle(date, selected); unavailable dates and additions beyond maxSelections are ignored |
multiple mode emits update, change, and toggle and honors maxSelections |
| M3 |
controlled |
keyboard / expose / prop |
Enter toggles the focused date; toggle and setValue are exposed; an external value outside the view scrolls it into view |
controlled multiple selection, keyboard toggling, and the exposed API |
| M4 |
SSR |
isolated requests / hydration |
byte-identical markup and silent hydration |
renders byte-identical multiple-date calendar markup across isolated SSR requests, hydrates multiple-date calendar markup without mismatches |
CalendarGrid also accepts showWeekNumbers (with weekNumberLabel,
weekNumberHeader, and a weekNumber slot) to render a leading
<th scope="row"> holding each row's ISO 8601 week number; see
week-picker.behavior.md row W1.
SSR determinism
"Today" is never read from the wall clock during server rendering or
hydration. Resolution order:
today — an explicit PlainDate; deterministic everywhere.
now — an injected clock (for example the request timestamp) resolved in
timeZone, else the nearest LocaleProvider time zone, else UTC. Inject
the same instant on the server and the client.
- Otherwise the calendar renders a
pending shell (data-state="pending",
empty grid body) until mount, then reads Date.now() once in timeZone,
the provided locale zone, or the host zone.
A modelValue, defaultValue, or focusedDate also decides which month is
visible, so a calendar with a value never waits for the clock. Week start uses
an embedded CLDR region table instead of Intl.Locale#getWeekInfo() so the
server and every browser agree. All labels format UTC midnight with
timeZone: "UTC".
Date model
PlainDate is { year, month, day } in the ISO calendar. Temporal.PlainDate
(ISO calendar) satisfies it structurally; call Temporal.PlainDate.from(value)
to convert back. Adapters: parseIsoDate / formatIsoDate, fromLocalDate /
toLocalDate, fromUtcDate / toUtcDate, and fromEpochMilliseconds(ms, timeZone). Non-Gregorian calendars are supported for display only
(calendar prop or LocaleProvider): grid months stay ISO months, which is
exact for gregory, iso8601, japanese, roc, and buddhist.
Public props (CalendarRoot)
| Prop |
Type |
Default |
Contract |
modelValue / defaultValue |
PlainDate | null |
undefined |
Controlled / uncontrolled selection. |
focusedDate |
PlainDate | null |
undefined |
Controlled roving focus date; decides the visible months. |
min / max |
PlainDate | null |
undefined |
Inclusive selectable window; swapped when reversed. |
isDateUnavailable |
(date) => boolean |
undefined |
Focusable but unselectable dates. |
locale / dir |
string / "ltr" | "rtl" |
provider |
Defaults to the nearest LocaleProvider. |
calendar / numberingSystem |
string |
provider |
Display-only Intl overrides. |
weekStartsOn |
0–6 |
locale |
First column; 0 is Sunday. |
weekdayFormat |
"narrow" | "short" | "long" |
"short" |
Column label width. |
numberOfMonths |
number |
1 |
Consecutive months, clamped to 1–12. |
pagedNavigation |
boolean |
false |
Month controls move by the page. |
fixedWeeks |
boolean |
false |
Always six rows. |
today / now / timeZone |
see SSR determinism |
undefined |
Clock injection. |
disabled / readOnly |
boolean |
false |
Availability. |
name |
string |
undefined |
Hidden input submitting YYYY-MM-DD. |
Extension hooks
| Hook |
Values |
| parts |
root, header, heading, prev, next, month-select, month-option, year-select, year-option, grid, grid-head, weekdays, weekday, grid-body, week, cell, day |
| root data |
data-vize-ui="calendar", data-mode, data-state, data-dir, data-months, data-value, data-disabled, data-readonly, data-pending |
| cell / day data |
data-date, data-state, data-weekday, data-today, data-focused, data-outside-month, data-selected, data-in-range, data-range-start, data-range-end, data-preview, data-disabled, data-unavailable |