Range Date Time Picker
Range Date Time Picker widgets are Input Fields that let the user enter one or more date-time ranges in a single compact field: they can type the ranges using their keyboard or open a drop-down calendar popover, a multi-month day grid plus a start and end time picker, to select them visually. Clicking a pill or pressing Enter navigates the calendar to that range’s month. For an always-visible inline variant, use the Range Date Time Calendar; for a typed-only field with no popover, use the Range Date Time Input; for a single (non-range) value, use the Date Time Picker.
The widget emits an array of { start, end } objects, each a local ISO date-time string (YYYY-MM-DDTHH:mm:ss).
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { label: 'Booking Slots', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots" } ]}Use these props to customize your Range Date Time Picker widget. It accepts the full set of typed-input props (for the trigger) and calendar props (for the popover and its time pickers), all listed below.
| prop | type | description |
|---|---|---|
allowCustomTime | boolean | Allow typing a time in each picker; when false (default) times come only from the grid |
dayCountAriaLabel | string | Localizable | Aria label template for the per-day count badge; {count} token substituted. Defaults to {count} ranges |
disabledDayCountAriaLabel | string | Localizable | Aria label template for the per-day disabled-spans badge; {count} token substituted. Defaults to {count} disabled ranges |
disabledRangeMessage | string | Localizable | Error shown when a selected range overlaps a disabled span |
disabledRanges | array | Array of { start, end } date-time spans that render disabled (a whole day is 00:00:00–23:59:59) |
endDateTimeAriaLabel | string | Aria label for the end date-time field group |
endTimeLabel | string | Localizable | Visible heading on the end time picker. Defaults to End time |
hint | string | A description to display below the input |
hourFormat | '12' | '24' | Force 12- or 24-hour entry. Defaults to the user’s locale |
icon | string | A css class to display an icon inside the input |
invalidDateMessage | string | Localizable | Error shown when a typed date does not exist |
maxDateTime | string | Latest allowed instant (ISO date-time, inclusive) |
maxDateTimeMessage | string | Localizable | Error shown when a typed date-time is after maxDateTime |
minDateTime | string | Earliest allowed instant (ISO date-time, inclusive) |
minDateTimeMessage | string | Localizable | Error shown when a typed date-time is before minDateTime |
minuteStep | number | Granularity of the time pickers and minute segment |
nextMonthAriaLabel | string | Accessible label for the next month navigation button |
nextMonthIcon | string | Custom CSS class for the next month button |
noAvailableTimesMessage | string | Localizable | Message shown when the bounds yield no selectable times. Defaults to No available times |
numberOfMonths | number | Number of months to display simultaneously in the popover |
prevMonthAriaLabel | string | Accessible label for the previous month navigation button |
prevMonthIcon | string | Custom CSS class for the previous month button |
removePillAriaLabel | string | Aria label for the remove pill action |
separator | string | The separator between start and end fields. Defaults to - |
startDateTimeAriaLabel | string | Aria label for the start date-time field group |
startTimeLabel | string | Localizable | Visible heading on the start time picker. Defaults to Start time |
Use the property hint to add descriptive text.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { hint: 'Pick one or more date-time ranges from the calendar.', minuteStep: 30, label: 'Booking Slots', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots", "props": { "hint": "Pick one or more date-time ranges from the calendar.", "minuteStep": 30 } } ]}Time Labels
Section titled “Time Labels”Use startTimeLabel and endTimeLabel to rename the two time pickers inside the popover (for example Check-in and Check-out). Both are localizable and default to Start time and End time.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('stays', { hint: 'Open the calendar to see the renamed time pickers.', minuteStep: 30, startTimeLabel: 'Check-in', endTimeLabel: 'Check-out', label: 'Stays', defaultValue: [ { start: '2026-02-16T14:00:00', end: '2026-02-18T09:30:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "stays", "label": "Stays", "defaultValue": [ { "start": "2026-02-16T14:00:00", "end": "2026-02-18T09:30:00" } ], "props": { "hint": "Open the calendar to see the renamed time pickers.", "minuteStep": 30, "startTimeLabel": "Check-in", "endTimeLabel": "Check-out" } } ]}Min and Max Date-Times
Section titled “Min and Max Date-Times”Use minDateTime and maxDateTime (ISO date-times, both inclusive) to bound the selectable instant span. Out-of-range days and times render disabled in the popover, and a typed value that violates a boundary raises an input error; customize (and localize) the messages with minDateTimeMessage and maxDateTimeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { hint: 'Book between 2026-02-01 09:00 and 2026-07-31 18:00', minuteStep: 30, minDateTime: '2026-02-01T09:00:00', maxDateTime: '2026-07-31T18:00:00', minDateTimeMessage: 'No bookings before February 2026', maxDateTimeMessage: 'No bookings after July 2026', label: 'Booking Slots', defaultValue: [ { start: '2026-02-13T09:00:00', end: '2026-02-13T11:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots", "defaultValue": [ { "start": "2026-02-13T09:00:00", "end": "2026-02-13T11:00:00" } ], "props": { "hint": "Book between 2026-02-01 09:00 and 2026-07-31 18:00", "minuteStep": 30, "minDateTime": "2026-02-01T09:00:00", "maxDateTime": "2026-07-31T18:00:00", "minDateTimeMessage": "No bookings before February 2026", "maxDateTimeMessage": "No bookings after July 2026" } } ]}Disabled Ranges
Section titled “Disabled Ranges”Use disabledRanges to mark instant spans as unavailable: a whole day is 00:00:00–23:59:59, and a partial-day span disables only the affected time slots on that day. A selected range that overlaps a disabled span raises an input error whose message you can customize (and localize) with disabledRangeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { hint: 'Feb 9-10 are closed, plus a maintenance window on 2026-02-17', minuteStep: 30, disabledRanges: [ { start: '2026-02-09T00:00:00', end: '2026-02-10T23:59:59', }, { start: '2026-02-17T13:00:00', end: '2026-02-17T14:00:00', }, ], disabledRangeMessage: 'This slot overlaps an unavailable period', label: 'Booking Slots', defaultValue: [ { start: '2026-02-13T09:00:00', end: '2026-02-13T11:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots", "defaultValue": [ { "start": "2026-02-13T09:00:00", "end": "2026-02-13T11:00:00" } ], "props": { "hint": "Feb 9-10 are closed, plus a maintenance window on 2026-02-17", "minuteStep": 30, "disabledRanges": [ { "start": "2026-02-09T00:00:00", "end": "2026-02-10T23:59:59" }, { "start": "2026-02-17T13:00:00", "end": "2026-02-17T14:00:00" } ], "disabledRangeMessage": "This slot overlaps an unavailable period" } } ]}Allow Custom Time
Section titled “Allow Custom Time”Use the property allowCustomTime to let the user type any time directly in each time picker, in addition to picking from the grid. When false (the default) times come only from the grid.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { hint: 'Type an exact time in each picker.', minuteStep: 30, allowCustomTime: true, label: 'Booking Slots', defaultValue: [ { start: '2026-02-13T09:00:00', end: '2026-02-13T11:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots", "defaultValue": [ { "start": "2026-02-13T09:00:00", "end": "2026-02-13T11:00:00" } ], "props": { "hint": "Type an exact time in each picker.", "minuteStep": 30, "allowCustomTime": true } } ]}Number of Months
Section titled “Number of Months”Use the property numberOfMonths to display multiple months side-by-side in the popover. The time pickers render below the first panel.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { hint: 'Two months are shown side-by-side in the popover.', minuteStep: 30, numberOfMonths: 2, label: 'Booking Slots', defaultValue: [ { start: '2026-02-16T14:00:00', end: '2026-02-18T09:30:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots", "defaultValue": [ { "start": "2026-02-16T14:00:00", "end": "2026-02-18T09:30:00" } ], "props": { "hint": "Two months are shown side-by-side in the popover.", "minuteStep": 30, "numberOfMonths": 2 } } ]}Day Count Labels
Section titled “Day Count Labels”A day carrying more than one range shows a count badge, and a day with disabled slots shows a disabled badge. Use dayCountAriaLabel and disabledDayCountAriaLabel to customize (and localize) their screen-reader labels; the {count} token is replaced with the number of ranges.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimePicker('bookingSlots', { hint: 'Hover a day badge to read its ranges.', minuteStep: 30, disabledRanges: [ { start: '2026-02-17T13:00:00', end: '2026-02-17T14:00:00', }, ], dayCountAriaLabel: 'Number of slots taken: {count}', disabledDayCountAriaLabel: 'Disabled slots: {count}', label: 'Booking Slots', defaultValue: [ { start: '2026-02-13T09:00:00', end: '2026-02-13T11:00:00', }, { start: '2026-02-13T13:00:00', end: '2026-02-13T15:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimePicker", "path": "bookingSlots", "label": "Booking Slots", "defaultValue": [ { "start": "2026-02-13T09:00:00", "end": "2026-02-13T11:00:00" }, { "start": "2026-02-13T13:00:00", "end": "2026-02-13T15:00:00" } ], "props": { "hint": "Hover a day badge to read its ranges.", "minuteStep": 30, "disabledRanges": [ { "start": "2026-02-17T13:00:00", "end": "2026-02-17T14:00:00" } ], "dayCountAriaLabel": "Number of slots taken: {count}", "disabledDayCountAriaLabel": "Disabled slots: {count}" } } ]}Styling
Section titled “Styling”Range Date Time Pickers can be styled as explained in the Styling Guide. The typed field, the popover calendar, and the embedded time pickers each have their own styling variables.
CSS Variables
Section titled “CSS Variables”Following you will find a list with the CSS Variables specific to the Range Date Time Picker popover positioning and toggle, plus those inherited from its sub-widgets.
| CSS Variable | Description |
|---|---|
--gui-radius-md | Interactive calendar container, day button, pill, and time list radius |
--gui-bg-default | Interactive calendar and time list background color |
--gui-border-default | Interactive calendar border color, hover day border, pills wrapper border |
--gui-space-3 | Internal padding within the calendar view and day buttons |
--gui-text-default | Input text color, day buttons text color, and time slot text color |
--gui-bg-disabled | Disabled day and disabled time slot background color |
--gui-intent-primary | Selected day, pill, and selected time slot background color |
--gui-intent-primary-hover | Today’s day border color and time slot hover background |
--gui-intent-error | Border and focus shadow color when invalid |
--gui-border-focus | Border color when a pill or input is focused |
--gui-shadow-focus | Box shadow when a pill or input is focused |
--gui-time-list-height | Total height of each time picker’s slot list viewport |
--gui-time-list-item-height | Fixed height of each time slot row |
--gui-time-list-columns | Number of columns in the time slot grid |
Anatomy
Section titled “Anatomy”This is the anatomy of the Range Date Time Picker Widget in case you want to use your CSS styles.
<div class="gui-form"> <gui-range-date-time-picker> <label for="fieldUid"> Label <div class="gui-widget-hint" id="fieldUid_hint">Hint</div> </label>
<div class="gui-widget" aria-expanded="true"> <gui-range-date-time id="date-input"> <div class="gui-widget-input gui-parts-ring gui-range-date-time-input gui-range-date-time-input--icon" role="group" aria-label="Booking Slots"> <span class="gui-widget-icon calendar_month" data-icon="calendar_month"></span>
<gui-pills class="gui-range-date-time-input__pills"> <div class="gui-pills" role="list"> <div class="gui-pills__pill" role="listitem" tabindex="0"> <span class="gui-pills__pill-text">02/13/2026 09:00 - 02/13/2026 11:00</span> <button type="button" class="gui-pills__pill-remove" tabindex="-1" aria-label="Remove date-time">×</button> </div> </div> </gui-pills>
<div class="gui-range-date-time-input__inputs"> <div class="gui-parts gui-range-date-time-input__field" role="group" aria-label="Start date-time"> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" class="gui-parts__part gui-range-date-time-input__part" data-type="month" data-group="start" maxlength="2" placeholder="mm" tabindex="0"> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> <span class="gui-range-date-time-input__separator">/</span> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" class="gui-parts__part gui-range-date-time-input__part" data-type="day" data-group="start" maxlength="2" placeholder="dd" tabindex="-1"> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> <span class="gui-range-date-time-input__separator">/</span> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" class="gui-parts__part gui-parts__year gui-range-date-time-input__part gui-range-date-time-input__year" data-type="year" data-group="start" maxlength="4" placeholder="yyyy" tabindex="-1"> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" class="gui-parts__part gui-range-date-time-input__part" data-type="hour" data-group="start" maxlength="2" placeholder="hh" tabindex="-1"> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> <span class="gui-range-date-time-input__separator">:</span> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" class="gui-parts__part gui-range-date-time-input__part" data-type="minute" data-group="start" maxlength="2" placeholder="mm" tabindex="-1"> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> </div>
<span class="gui-range-date-time-input__separator">-</span>
<div class="gui-parts gui-range-date-time-input__field" role="group" aria-label="End date-time"> <!-- End group repeats the same month/day/year hour:minute parts with data-group="end" --> </div> </div> </div> </gui-range-date-time>
<gui-range-date-time-calendar id="calendar-input"> <div class="gui-calendar-input"> <div class="gui-calendar__container"> <div class="gui-calendar__months-grid"> <div> <div class="gui-calendar__header"> <button class="gui-button gui-calendar__month-button gui-calendar__month-button--prev" type="button" aria-label="Previous Month"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path d="..."></path></svg></button> <span aria-live="polite">February 2026</span> <button class="gui-button gui-calendar__month-button gui-calendar__month-button--next" type="button" aria-label="Next Month"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path d="..."></path></svg></button> </div>
<div class="gui-calendar__days-grid" role="grid" aria-label="February 2026"> <div class="gui-calendar__rows"> <div class="gui-calendar__weekday" role="columnheader">Su</div> <div class="gui-calendar__weekday" role="columnheader">Mo</div> <!-- …Tu We Th Fr Sa --> </div> <div class="gui-calendar__rows"> <button type="button" role="gridcell" class="gui-calendar__day-button other-month" tabindex="-1" disabled aria-selected="false">1</button> <!-- A day carrying more than one range shows a count badge; a day with disabled slots shows a disabled badge --> <button type="button" role="gridcell" class="gui-calendar__day-button selected" tabindex="0" data-date="2026-02-13" aria-selected="true">13<span class="gui-range-date-time-calendar__badges"><span class="gui-range-date-time-calendar__day-count" aria-label="2 ranges">2</span></span></button> </div> </div> </div> </div>
<!-- The two embedded Time Pickers; each is disabled until its endpoint day is chosen --> <div class="gui-range-date-time-calendar__time-row"> <div class="gui-range-date-time-calendar__time-column"> <gui-time-picker class="gui-time-picker gui-field gui-range-date-time-calendar__start"> <label class="gui-label">Start time</label> <!-- See the Time Picker anatomy: gui-time + arrow + gui-time-list (grid mode) --> </gui-time-picker> </div> <div class="gui-range-date-time-calendar__time-column"> <gui-time-picker class="gui-time-picker gui-field gui-range-date-time-calendar__end"> <label class="gui-label">End time</label> <!-- See the Time Picker anatomy: gui-time + arrow + gui-time-list (grid mode) --> </gui-time-picker> </div> </div> </div> </div> </gui-range-date-time-calendar> </div> </gui-range-date-time-picker></div>