Skip to content

Calendar ​

A calendar is the month grid on its own. Most of the time you want it inside a DatePicker, which pairs it with a field that can be typed into; use it directly where the calendar is the page — a booking screen, a schedule.

ts
import {
  Calendar,
  CalendarCell,
  CalendarGrid,
  CalendarGridBody,
  CalendarGridHeader,
  CalendarHeader,
  CalendarHeading,
  CalendarNavButton,
} from "ropav";

The parts split the grid from its chrome: CalendarHeader holds the heading and the two CalendarNavButtons (which take slot="previous" and slot="next"), and CalendarGrid holds the weekday row and the days. CalendarGridBody hands each date to its slot, so a cell can be rendered however the app needs.

Event date, October 2026

27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
calendar-basic.vue
vue
<script setup lang="ts">
import {
  Calendar,
  CalendarCell,
  CalendarGrid,
  CalendarGridBody,
  CalendarGridHeader,
  CalendarHeader,
  CalendarHeaderCell,
  CalendarHeading,
  CalendarNavButton,
} from "ropav";
</script>

<template>
  <Calendar aria-label="Event date">
    <CalendarHeader>
      <CalendarNavButton slot="previous" />
      <CalendarHeading />
      <CalendarNavButton slot="next" />
    </CalendarHeader>

    <CalendarGrid>
      <CalendarGridHeader>
        <template #default="{ day }">
          <CalendarHeaderCell>{{ day }}</CalendarHeaderCell>
        </template>
      </CalendarGridHeader>
      <CalendarGridBody>
        <template #default="{ date }">
          <CalendarCell :date />
        </template>
      </CalendarGridBody>
    </CalendarGrid>
  </Calendar>
</template>

What can be chosen ​

min-value and max-value bound the range. is-date-unavailable rules out individual dates inside it — weekends, days already booked — which is a different thing from disabling the calendar: the grid still works, those days just cannot be taken.

selection-mode takes one date or several. For a start and an end that belong together, use a RangeCalendar rather than multiple selection.

How much is on screen ​

visible-duration decides how many months show at once and is also what paging moves by, so a two-month calendar steps two months. page-behavior chooses between moving a whole page and moving one unit. weeks-in-month fixes the number of rows, which stops the grid changing height as the reader pages through months of different shapes.

first-day-of-week overrides the locale, and create-calendar swaps the calendar system — injectable so a build can ship fewer of them than the full set.

Accessibility ​

  • The grid is a real table with a role="grid", so the arrow keys move by day and week the way a reader expects, and each cell announces its date and its state.
  • Give it an aria-label. "Event date" tells a reader what the grid is for; without one they get a grid of numbers and have to work it out.
  • An unavailable date is announced as such rather than simply not responding, which is the difference between "you cannot pick that" and a control that seems broken.
  • The heading is what tells a reader which month they have paged into. Keep it, even when the months are obvious on screen.

API ​

Calendar

PropTypeDefaultDescription
ariaDescribedbystring—
ariaDetailsstring—
ariaLabelstring—
ariaLabelledbystring—
autoFocusboolean—Whether the calendar takes focus as soon as it mounts.
createCalendar((identifier: CalendarIdentifier) => Calendar)—Builds the calendar system for an identifier. Injectable so a build can ship fewer of them.
defaultFocusedValueDateValue | null—
defaultValueCalendarValue—
defaultYearPickerOpenbooleanfalse
firstDayOfWeek"fri" | "mon" | "sat" | "sun" | "thu" | "tue" | "wed"—The day a week starts on, when it should not follow the locale.
focusedValueDateValue | null—
idstring—
isDateUnavailable((date: DateValue) => boolean)—Rules a date out even though it is inside the range.
isDisabledboolean—
isInvalidboolean—
isReadOnlyboolean—
isYearPickerOpenboolean—Whether the year picker is open.
maxValueDateValue | null—
minValueDateValue | null—
pageBehavior"single" | "visible""visible"
selectionAlignment"center" | "end" | "start""center"
selectionMode"single" | "multiple""single"Whether the calendar takes one date or several.
valueCalendarValue—
visibleDurationDateDuration{months: 1}How much is on screen at once, which is also what paging moves by.
weeksInMonthnumber—A fixed number of week rows, so a month grid does not change height while paging.

The default slot receives:

Slot propType
isDisabledboolean
isInvalidboolean
stateAnyCalendarState
EventPayload
update:focusedValueCalendarDate
update:valueCalendarValue
update:yearPickerOpenboolean

CalendarCell

PropTypeDefaultDescription
date*CalendarDate—The date this cell stands for.

The default slot receives:

Slot propType
dateCalendarDate
formattedDatestring
isDisabledboolean
isFocusedboolean
isFocusVisibleboolean
isHoveredboolean
isInvalidboolean
isOutsideMonthboolean
isOutsideVisibleRangeboolean
isPressedboolean
isSelectedboolean
isTodayboolean
isUnavailableboolean

CalendarCellIndicator

Slots: default.

CalendarGrid

PropTypeDefaultDescription
offsetDateDuration—How far past the first visible date this grid starts, for a multi-month calendar.
weekdayStyle"long" | "narrow" | "short""short"How wide the weekday names above the grid are written.

Slots: default.

CalendarGridBody

The default slot receives:

Slot propType
dateCalendarDate

CalendarGridHeader

The default slot receives:

Slot propType
daystring

CalendarHeader

Slots: default.

CalendarHeaderCell

Slots: default.

CalendarHeading

PropTypeDefaultDescription
formatCalendarHeadingFormatOptions—Overrides the parts the heading is written from.
offsetDateDuration—How far past the first visible date this heading describes.

CalendarNavButton

PropTypeDefaultDescription
slot"next" | "previous""next"Which direction this button pages.

Slots: default.