Skip to main content
PROYECTOVIVIANA

RangeCalendar

A range calendar displays a grid of days and allows users to select a contiguous range of dates by clicking a start date and then an end date. It is useful for booking flows, report filtering, and any scenario that requires a date span.

Import

import { RangeCalendar } from '@proyecto-viviana/solid-spectrum';
import {
  CalendarDateClass as CalendarDate,
  type DateValue,
  type RangeValue,
} from '@proyecto-viviana/solid-stately';

Basic Range Selection

Click a date to set the range start, then click another date to set the range end. The days between start and end are highlighted. The selected range is displayed below the calendar.

Trip dates, November 2026

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
1
2
3
4
5

Selected range: None

const [range, setRange] = createSignal<RangeValue<DateValue> | null>(null);

<RangeCalendar
  aria-label="Trip dates"
  value={range()}
  onChange={setRange}
/>

<p>Selected: {formatRange(range())}</p>

With Min/Max Constraints

Restrict the selectable range to a specific window. Dates outside the min/max bounds are disabled and navigation beyond those months is prevented.

2026 date range, November 2026

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
1
2
3
4
5

Only dates in 2026 are selectable

const minDate = new CalendarDate(2026, 1, 1);
const maxDate = new CalendarDate(2026, 12, 31);

<RangeCalendar
  aria-label="2026 date range"
  minValue={minDate}
  maxValue={maxDate}
/>

Unavailable Dates

Use isDateUnavailable to block specific dates from being included in a range. Users cannot select a range that spans across unavailable dates.

Booking dates, March 2026

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
1
2
3
4

Mar 25-27 are unavailable

const isDateUnavailable = (date: DateValue) =>
  unavailableDates.some(
    (d) => d.year === date.year && d.month === date.month && d.day === date.day
  );

<RangeCalendar
  aria-label="Booking dates"
  isDateUnavailable={isDateUnavailable}
  defaultValue={{ start: new CalendarDate(2026, 3, 1), end: new CalendarDate(2026, 3, 5) }}
/>

Disabled State

A disabled range calendar is non-interactive. All cells are muted and navigation buttons are inoperative.

Disabled calendar, November 2026

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
1
2
3
4
5
<RangeCalendar
  aria-label="Disabled calendar"
  isDisabled
/>

Props

PropTypeDefaultDescription
valueRangeValue<DateValue> | null—The currently selected date range (controlled)
onChange(range: RangeValue<DateValue>) => void—Handler called when the selected range changes
defaultValueRangeValue<DateValue>—The default selected range (uncontrolled)
minValueDateValue—The minimum selectable date
maxValueDateValue—The maximum selectable date
isDisabledbooleanfalseWhether the range calendar is disabled
isDateUnavailable(date: DateValue) => boolean—Callback that returns whether a specific date is unavailable for selection
aria-labelstring—Accessible label for the range calendar
size'sm' | 'md' | 'lg''md'The visual size of the range calendar
localestring—The locale to use for formatting month and day names

Accessibility

  • Uses a grid role with gridcell elements for each day
  • Full keyboard navigation: Arrow keys move between days, Page Up/Down change months
  • Range start and end are visually distinct with rounded corners at the endpoints
  • Days within the range use aria-selected="true" for screen reader announcement
  • Unavailable dates are announced as disabled via aria-disabled
  • Previous/next month buttons include descriptive aria-label attributes
  • Today's date is visually indicated with a ring highlight