Skip to content
Forms, switch product

Range Calendar

Range Calendar widgets are Input Fields that allow the user to visualize and select an inclusive date range (start date and end date). Each completed range is displayed as a pill that can be removed, or edited in place with allowEdit.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('vacationDates'),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeCalendar",
"path": "vacationDates"
}
]
}

Use these props to customize your Range Calendar widget. Range Calendars share all properties with standard Calendars.

proptypedescription
allowEditbooleanEnables selecting and editing pills in place. See Editing Pills. Defaults to false
cancelEditLabelstring | LocalizableTooltip of the pill’s cancel icon while editing. Defaults to Cancel
confirmEditLabelstring | LocalizableTooltip of the pill’s confirm icon while editing. Defaults to Confirm
disabledDateRangeMessagestring | LocalizableError surfaced when a selected range spans one or more disabled days. Defaults to Invalid date: date is within a disabled range.
disabledRangesarrayArray of objects ({ start: string, end?: string }) defining disabled date ranges
editAriaLabelstring | LocalizableEdit hint joined into a selectable pill’s aria-description; {label} is replaced with the range’s label. Defaults to Edit range {label}
editCancelledMessagestring | LocalizableScreen-reader announcement when an edit is cancelled. Defaults to Edit cancelled.
editCommittedMessagestring | LocalizableScreen-reader announcement when an edit commits; {label} carries the resulting range. Defaults to Range updated to {label}.
editLabelstring | LocalizableTooltip of the pill’s edit icon. Defaults to Edit
editStartedMessagestring | LocalizableScreen-reader announcement when an edit starts; {label} carries the range. Defaults to Editing range {label}.
hintstringA description to display below the range calendar
maxDatestringMaximum allowable date in ISO format
minDatestringMinimum allowable date in ISO format
nextMonthAriaLabelstringAccessible label for the next month navigation button
numberOfMonthsnumberNumber of months to display simultaneously. Defaults to 2 for range calendars
prevMonthAriaLabelstringAccessible label for the previous month navigation button
removePillAriaLabelstringAccessible label for the remove button on date range pills
selectYearAriaLabelstringAccessible label for the year-selector toggle button in the calendar header; the current year is appended. Defaults to Select year
yearGridAriaLabelstringAccessible label for the year-selection grid. Defaults to Year selection

Use the property hint to add a description to the date range selection.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('vacationDates', {
hint: 'Please select the start and end dates for your vacation',
label: 'Vacation Dates',
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeCalendar",
"path": "vacationDates",
"props": {
"hint": "Please select the start and end dates for your vacation"
},
"label": "Vacation Dates"
}
]
}

Use the properties minDate and maxDate to specify the boundaries for selectable dates.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('vacationDates', {
minDate: '2024-06-01T00:00:00.000Z',
maxDate: '2024-08-31T23:59:59.999Z',
defaultValue: [
{
start: '2024-06-21',
},
],
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeCalendar",
"path": "vacationDates",
"defaultValue": [
{
"start": "2024-06-21"
}
],
"props": {
"minDate": "2024-06-01T00:00:00.000Z",
"maxDate": "2024-08-31T23:59:59.999Z"
}
}
]
}

Use the property numberOfMonths to display multiple months side-by-side to ease selection of larger spans.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('vacationDates', {
numberOfMonths: 2,
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeCalendar",
"path": "vacationDates",
"props": {
"numberOfMonths": 2
}
}
]
}

Use the property disabledRanges to define dates which cannot be highlighted as part of a range. A selected range that spans one or more disabled days is rejected as a whole (no pill is added) and an error is thrown; customize (and localize) the error shown in that case with disabledDateRangeMessage.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('vacationDates', {
disabledRanges: [
{
start: '2024-07-04T00:00:00.000Z',
end: '2024-07-07T23:59:59.999Z',
},
],
disabledDateRangeMessage: 'Selection can\'t include blocked days',
defaultValue: [
{
start: '2024-07-21',
end: '2024-07-25',
},
],
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeCalendar",
"path": "vacationDates",
"defaultValue": [
{
"start": "2024-07-21",
"end": "2024-07-25"
}
],
"props": {
"disabledRanges": [
{
"start": "2024-07-04T00:00:00.000Z",
"end": "2024-07-07T23:59:59.999Z"
}
],
"disabledDateRangeMessage": "Selection can't include blocked days"
}
}
]
}

Use prevMonthIcon, nextMonthIcon, prevMonthAriaLabel and nextMonthAriaLabel to customize the navigation buttons.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('dates', {
prevMonthIcon: 'chevron_left',
nextMonthIcon: 'chevron_right',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
label: 'Custom Icons Range Calendar',
uid: 'range-calendar-icons',
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"uid": "range-calendar-icons",
"kind": "input",
"type": "rangeCalendar",
"path": "dates",
"label": "Custom Icons Range Calendar",
"props": {
"prevMonthIcon": "chevron_left",
"nextMonthIcon": "chevron_right",
"prevMonthAriaLabel": "Go to previous month",
"nextMonthAriaLabel": "Go to next month"
}
}
]
}

Use prevMonthAriaLabel, nextMonthAriaLabel and removePillAriaLabel to provide accessible labels for the navigation buttons and the remove button on date range pills.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('dates', {
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
removePillAriaLabel: 'Remove date range',
label: 'Range Calendar with Aria Labels',
uid: 'range-calendar-aria-labels',
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"uid": "range-calendar-aria-labels",
"kind": "input",
"type": "rangeCalendar",
"path": "dates",
"label": "Range Calendar with Aria Labels",
"props": {
"prevMonthAriaLabel": "Go to previous month",
"nextMonthAriaLabel": "Go to next month",
"removePillAriaLabel": "Remove date range"
}
}
]
}

Use the property allowEdit to let the user change a committed range in place instead of removing it and picking it again. Click a pill, or arrow onto it, to select it; the selection reveals a pencil icon next to the remove icon. The pencil, or F2 or E on the focused pill, loads the range back into the day grid. While you re-pick, the pill’s label previews the new value. The pill’s check icon confirms the replacement; Escape or the cross icon cancels it and keeps the pill selected. A confirmed range that overlaps a neighbour merges into it, and confirming an unchanged range is treated as a cancel. Selecting is not a commit: the form value only changes on confirm.

Here the calendar itself is the edit surface. Selecting a pill dims the other committed ranges in the day grid, and editing parks the pill’s span as the in-progress selection: day picks reshape it without committing until the pill’s check icon confirms. Leaving the widget with a complete, changed span also commits it. Escape is layered: the first press cancels the edit, the second clears the selection.

On touch devices, where the pills collapse into the count bubble, the pencil is always visible inside the pills dropdown, and the pills and their icons grow to the pointer: coarse sizes listed under CSS Variables.

The tooltips and screen-reader announcements of the flow are localizable through editLabel, confirmEditLabel, cancelEditLabel, editAriaLabel, editStartedMessage, editCommittedMessage and editCancelledMessage. A {label} token in editAriaLabel, editStartedMessage and editCommittedMessage is replaced with the range’s display label by the widget itself, single braces on purpose, so the form’s {{ }} localization interpolation never consumes it.

range-calendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeCalendar('editableVacations', {
allowEdit: true,
label: 'Vacations',
defaultValue: [
{
start: '2026-09-07',
end: '2026-09-11',
},
{
start: '2026-09-21',
end: '2026-09-25',
},
],
}),
];
range-calendar.json
{
"$schema": "https://golemui.com/schemas/form.schema.json",
"form": [
{
"kind": "input",
"type": "rangeCalendar",
"path": "editableVacations",
"label": "Vacations",
"defaultValue": [
{
"start": "2026-09-07",
"end": "2026-09-11"
},
{
"start": "2026-09-21",
"end": "2026-09-25"
}
],
"props": {
"allowEdit": true
}
}
]
}

Range Calendars can be styled as explained in the Styling Guide. They inherit all Calendar widget styles along with specialized range highlighting rules.

Following you will find a list with the CSS Variables and a quick description of what you will style.

CSS VariableDescription
--gui-radius-mdCalendar container base radius
--gui-radius-rangeBorder radius applied to the start and end of a selected range (calculated as double radius-md)
--gui-bg-defaultCalendar container background color
--gui-border-defaultDefault border color and cell hover border color
--gui-space-3Internal padding
--gui-calendar-change-month-button-widthWidth of the next/prev buttons
--gui-calendar-change-month-button-heightHeight of the next/prev buttons
--gui-text-defaultDefault text and focus border color
--gui-text-secondaryDay button hover background color
--gui-intent-primary-active”Today” border highlight color
--gui-intent-primarySelected range and selected day background color
--gui-bg-disabledDisabled day background color
--gui-pill-heightPill height. 24px, 32px under pointer: coarse
--gui-pill-action-sizeSize of the edit, confirm, cancel and remove icons. 16px, 24px under pointer: coarse
--gui-pill-action-hitHit area of each pill icon. 24px, 32px under pointer: coarse
--gui-pill-font-sizePill label font size. var(--gui-font-xs), var(--gui-font-sm) under pointer: coarse

This is the anatomy of the Range Calendar Widget in case you want to use your CSS styles.

range-calendar-anatomy.html
<gui-range-calendar-input class="gui-calendar gui-field">
<gui-range-calendar allow-edit="" class="gui-field">
<label class="gui-label" for="fieldUid" id="fieldUid_calendar_label"><span class="gui-label__text">Vacations</span></label>
<div class="gui-widget">
<div class="gui-calendar-input" role="group" id="fieldUid_calendar" aria-labelledby="fieldUid_calendar_label">
<div class="gui-visually-hidden" aria-live="polite"></div>
<div class="gui-visually-hidden" aria-live="polite"></div>
<gui-pills class="gui-range-calendar__pills">
<div class="gui-pills__strip-wrapper gui-pills--start-shadow gui-pills--end-shadow">
<div class="gui-pills__strip" role="toolbar" tabindex="-1" aria-label="Selected date ranges">
<span class="gui-sentinel gui-sentinel__start"></span>
<button
type="button"
class="gui-pills__pill gui-pills__pill--clickable"
tabindex="0"
aria-pressed="false"
aria-label="09/07/2026 - 09/11/2026"
aria-description="Edit range 09/07/2026 - 09/11/2026. Remove date"
>
<span class="gui-pills__pill-text">09/07/2026 - 09/11/2026</span>
<span aria-hidden="true" class="gui-pills__pill-action gui-pills__pill-action--edit" title="Edit">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 256 256"
fill="currentColor"
aria-hidden="true"
>
<path d="M229.66,58.34l-32-…"></path>
</svg>
</span>
<span class="gui-pills__pill-remove" aria-hidden="true">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 256 256"
fill="currentColor"
aria-hidden="true"
>
<path d="M165.66,101.66,139…"></path>
</svg>
</span>
</button>
<button
type="button"
class="gui-pills__pill gui-pills__pill--clickable"
tabindex="0"
aria-pressed="false"
aria-label="09/21/2026 - 09/25/2026"
aria-description="Edit range 09/21/2026 - 09/25/2026. Remove date"
>
<span class="gui-pills__pill-text">09/21/2026 - 09/25/2026</span>
<span aria-hidden="true" class="gui-pills__pill-action gui-pills__pill-action--edit" title="Edit">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 256 256"
fill="currentColor"
aria-hidden="true"
>
<path d="M229.66,58.34l-32-…"></path>
</svg>
</span>
<span class="gui-pills__pill-remove" aria-hidden="true">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 256 256"
fill="currentColor"
aria-hidden="true"
>
<path d="M165.66,101.66,139…"></path>
</svg>
</span>
</button>
<span class="gui-sentinel gui-sentinel__end"></span>
</div>
</div>
</gui-pills>
<div class="gui-calendar__container">
<button type="button" class="gui-button gui-calendar__month-button gui-calendar__month-button--prev" aria-label="Previous month">
<span class="gui-caret gui-caret--left" aria-hidden="true"></span>
</button>
<div class="gui-calendar__months-grid">
<div class="gui-calendar__panel">
<header class="gui-calendar__header">
<h2>
<span class="gui-calendar__month-name">September</span>
<button type="button" class="gui-calendar__year-selector" aria-expanded="false" aria-label="Select year, 2026">
<span class="gui-calendar__year-value">2026</span>
<span class="gui-calendar__year-arrow" aria-hidden="true">
<span class="gui-caret" aria-hidden="true"></span>
</span>
</button>
</h2>
</header>
<div class="gui-calendar__days-grid" role="grid" aria-label="September 2026">
<div role="row" class="gui-calendar__rows">
<span class="gui-calendar__weekday" role="columnheader">S</span>
<span class="gui-calendar__weekday" role="columnheader">M</span>
<span class="gui-calendar__weekday" role="columnheader">T</span>
<!-- More span.gui-calendar__weekday -->
</div>
<div role="row" class="gui-calendar__rows">
<button
type="button"
role="gridcell"
class="gui-calendar__day-button other-month"
tabindex="-1"
disabled=""
aria-label="Sunday, August 30, 2026"
data-date="2026-08-30"
aria-selected="false"
>
30
</button>
<button
type="button"
role="gridcell"
class="gui-calendar__day-button other-month"
tabindex="-1"
disabled=""
aria-label="Monday, August 31, 2026"
data-date="2026-08-31"
aria-selected="false"
>
31
</button>
<button
type="button"
role="gridcell"
class="gui-calendar__day-button"
tabindex="-1"
aria-label="Tuesday, September 1, 2026"
data-date="2026-09-01"
aria-selected="false"
>
1
</button>
<!-- More button.gui-calendar__day-button -->
</div>
<div role="row" class="gui-calendar__rows">
<button
type="button"
role="gridcell"
class="gui-calendar__day-button"
tabindex="-1"
aria-label="Sunday, September 6, 2026"
data-date="2026-09-06"
aria-selected="false"
>
6
</button>
<button
type="button"
role="gridcell"
class="gui-calendar__day-button range-start"
tabindex="0"
aria-label="Monday, September 7, 2026"
data-date="2026-09-07"
aria-selected="true"
>
7
</button>
<button
type="button"
role="gridcell"
class="gui-calendar__day-button in-range"
tabindex="-1"
aria-label="Tuesday, September 8, 2026"
data-date="2026-09-08"
aria-selected="true"
>
8
</button>
<!-- More button.gui-calendar__day-button -->
</div>
<!-- More div.gui-calendar__rows -->
</div>
</div>
</div>
<button type="button" class="gui-button gui-calendar__month-button gui-calendar__month-button--next" aria-label="Next month">
<span class="gui-caret gui-caret--right" aria-hidden="true"></span>
</button>
</div>
</div>
</div>
</gui-range-calendar>
</gui-range-calendar-input>