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.

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.

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.

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.

<RangeCalendar
  aria-label="Disabled calendar"
  isDisabled
/>

Props

PropTypeDefaultDescription
valueRangeValue<DateValue> | nullThe currently selected date range (controlled)
onChange(range: RangeValue<DateValue>) => voidHandler called when the selected range changes
defaultValueRangeValue<DateValue>The default selected range (uncontrolled)
minValueDateValueThe minimum selectable date
maxValueDateValueThe maximum selectable date
isDisabledbooleanfalseWhether the range calendar is disabled
isDateUnavailable(date: DateValue) => booleanCallback that returns whether a specific date is unavailable for selection
aria-labelstringAccessible label for the range calendar
size'sm' | 'md' | 'lg''md'The visual size of the range calendar
localestringThe 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