Range Date Time Input
Range Date Time Input widgets are Input Fields that allow the user to enter multiple date and time ranges using segmented keyboard inputs. Each endpoint is a full instant (a date and a time together), and each completed range is displayed as a pill that can be removed, or edited in place with allowEdit. The date and time segments follow the locale’s order, and whether the time renders in 12-hour or 24-hour mode follows the user’s locale too.
The widget emits an array of { start, end } objects, each holding a local ISO date-time string (YYYY-MM-DDTHH:mm:ss). To capture a single date and time, use the Date Time Input instead; to capture time-only ranges, use the Range Time Input.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { label: 'Date-Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges" } ]}A range is committed once both endpoints are complete; a backward selection (end before start) reorders itself instead of erroring. Typed values clamp at their limits and ArrowUp/ArrowDown increment the focused segment. If a completed endpoint does not exist (e.g. February 30) or falls outside the allowed bounds, the widget surfaces an error instead of adding the range.
Use these props to customize your Range Date Time Input widget.
| prop | type | description |
|---|---|---|
allowEdit | boolean | Enables selecting and editing pills in place. See Editing Pills. Defaults to false |
cancelEditLabel | string | Localizable | Tooltip of the pill’s cancel icon while editing. Defaults to Cancel |
confirmEditLabel | string | Localizable | Tooltip of the pill’s confirm icon while editing. Defaults to Confirm |
dayAriaLabel | string | Aria label for the day segment input. Defaults to Day |
dayPeriodAriaLabel | string | Aria label for the AM/PM segment input. Defaults to AM/PM |
editAriaLabel | string | Localizable | Edit hint joined into a selectable pill’s aria-description; {label} is replaced with the range’s label. Defaults to Edit range {label} |
editCancelledMessage | string | Localizable | Screen-reader announcement when an edit is cancelled. Defaults to Edit cancelled. |
editCommittedMessage | string | Localizable | Screen-reader announcement when an edit commits; {label} carries the resulting range. Defaults to Range updated to {label}. |
editLabel | string | Localizable | Tooltip of the pill’s edit icon. Defaults to Edit |
editStartedMessage | string | Localizable | Screen-reader announcement when an edit starts; {label} carries the range. Defaults to Editing range {label}. |
endDateTimeAriaLabel | string | Aria label for the end date-time field group. Defaults to End date and time |
hint | string | A description to display below the input |
hourAriaLabel | string | Aria label for the hour segment input. Defaults to Hour |
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 |
incompleteMessage | string | Localizable | Error shown when focus leaves the widget with a partially filled date-time behind; the value goes to null so validators report it |
invalidDateMessage | string | Localizable | Error shown when a typed date does not exist, e.g. February 30 |
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 |
minuteAriaLabel | string | Aria label for the minute segment input. Defaults to Minute |
minuteStep | number | Granularity of the minute segment |
monthAriaLabel | string | Aria label for the month segment input. Defaults to Month |
removePillAriaLabel | string | Aria label for the remove pill action. Defaults to Remove date and time |
separator | string | The separator between start and end fields. Defaults to - |
startDateTimeAriaLabel | string | Aria label for the start date-time field group. Defaults to Start date and time |
yearAriaLabel | string | Aria label for the year segment input. Defaults to Year |
Use the property hint to add a description or validation instructions.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { hint: 'Enter one or more date and time ranges', label: 'Date-Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges", "props": { "hint": "Enter one or more date and time ranges" } } ]}Use the property icon to add an icon to the widget. The value of icon represents a set of CSS classes separated by spaces. The icon is displayed inside the input border.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { icon: 'schedule', label: 'Date-Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges", "props": { "icon": "schedule" } } ]}Separator
Section titled “Separator”Use the property separator to customize the text displayed between the start and end fields. By default the separator is -.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { separator: 'to', label: 'Date-Time Ranges', defaultValue: [ { start: '2026-11-22T09:00:00', end: '2026-11-22T11:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges", "defaultValue": [ { "start": "2026-11-22T09:00:00", "end": "2026-11-22T11:00:00" } ], "props": { "separator": "to" } } ]}Hour Format
Section titled “Hour Format”By default the 12-hour/24-hour layout comes from the locale’s hour cycle. Use the property hourFormat ('12' or '24') to force one of them.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { hourFormat: '24', label: 'Date-Time Ranges', defaultValue: [ { start: '2026-11-22T09:00:00', end: '2026-11-22T11:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges", "defaultValue": [ { "start": "2026-11-22T09:00:00", "end": "2026-11-22T11:00:00" } ], "props": { "hourFormat": "24" } } ]}Minute Step
Section titled “Minute Step”Use the property minuteStep to control the ArrowUp/ArrowDown increment of the minute segment, useful for appointment-style forms where times snap to a grid (e.g. every 15 minutes). Typing a value is not restricted by the step. The default is 1.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { minuteStep: 15, label: 'Date-Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges", "props": { "minuteStep": 15 } } ]}Min and Max Date-Times
Section titled “Min and Max Date-Times”Use the properties minDateTime and maxDateTime (ISO date-times, both inclusive) to specify the boundaries for accepted instants. Each endpoint is bounded as a whole instant rather than by independent date and time axes. A completed endpoint outside the boundaries raises an input error instead of adding the range; customize (and localize) the messages with minDateTimeMessage and maxDateTimeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { hint: 'Slots between Jan 1st and Dec 31st, 2026', minDateTime: '2026-01-01T09:00:00', maxDateTime: '2026-12-31T18:00:00', minDateTimeMessage: 'No slots available before Jan 1st, 2026 at 9:00.', maxDateTimeMessage: 'No slots available after Dec 31st, 2026 at 18:00.', label: 'Available Slots', defaultValue: [ { start: '2026-11-22T09:00:00', end: '2026-11-22T11:00:00', }, { start: '2026-11-23T14:00:00', end: '2026-11-24T09:30:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Available Slots", "defaultValue": [ { "start": "2026-11-22T09:00:00", "end": "2026-11-22T11:00:00" }, { "start": "2026-11-23T14:00:00", "end": "2026-11-24T09:30:00" } ], "props": { "hint": "Slots between Jan 1st and Dec 31st, 2026", "minDateTime": "2026-01-01T09:00:00", "maxDateTime": "2026-12-31T18:00:00", "minDateTimeMessage": "No slots available before Jan 1st, 2026 at 9:00.", "maxDateTimeMessage": "No slots available after Dec 31st, 2026 at 18:00." } } ]}Aria Labels
Section titled “Aria Labels”Use the properties removePillAriaLabel, startDateTimeAriaLabel and endDateTimeAriaLabel to customize the accessibility labels for screen readers. These properties are localizable.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('dateTimeRanges', { removePillAriaLabel: 'Delete range', startDateTimeAriaLabel: 'From date and time', endDateTimeAriaLabel: 'To date and time', label: 'Date-Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "dateTimeRanges", "label": "Date-Time Ranges", "props": { "removePillAriaLabel": "Delete range", "startDateTimeAriaLabel": "From date and time", "endDateTimeAriaLabel": "To date and time" } } ]}Editing Pills
Section titled “Editing Pills”Use the property allowEdit to let the user change a committed range in place instead of removing it and typing 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 segments and moves focus to the first start segment. While you type, the pill’s label previews the new value. Enter in the segments or 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.
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.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeDateTimeInput('editableMeetings', { allowEdit: true, separator: 'to', label: 'Meetings', defaultValue: [ { start: '2026-09-07T09:00:00', end: '2026-09-07T11:00:00', }, { start: '2026-09-08T14:00:00', end: '2026-09-09T09:30:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeDateTimeInput", "path": "editableMeetings", "label": "Meetings", "defaultValue": [ { "start": "2026-09-07T09:00:00", "end": "2026-09-07T11:00:00" }, { "start": "2026-09-08T14:00:00", "end": "2026-09-09T09:30:00" } ], "props": { "allowEdit": true, "separator": "to" } } ]}Styling
Section titled “Styling”Range Date Time Inputs can be styled as explained in the Styling Guide.
CSS Variables
Section titled “CSS Variables”Following you will find a list with the CSS Variables and a quick description of what you will style.
| CSS Variable | Description |
|---|---|
--gui-intent-error | Border and focus shadow color when invalid |
--gui-border-focus | Border color when a pill or segment is focused |
--gui-shadow-focus | Box shadow when a pill or segment is focused |
--gui-intent-primary | Pill background color |
--gui-color-white | Pill text and remove button color |
--gui-border-default | Pills wrapper border and separator border color |
--gui-bg-default | Scroll shadow gradient color and dropdown background color |
--gui-text-default | Active piece (date/time segment) visual underline color |
--gui-radius-md | Pill and dropdown border radius |
--gui-radius-full | Pill count button and remove button border radius |
--gui-pill-height | Pill height. 24px, 32px under pointer: coarse |
--gui-pill-action-size | Size of the edit, confirm, cancel and remove icons. 16px, 24px under pointer: coarse |
--gui-pill-action-hit | Hit area of each pill icon. 24px, 32px under pointer: coarse |
--gui-pill-font-size | Pill label font size. var(--gui-font-xs), var(--gui-font-sm) under pointer: coarse |
Anatomy
Section titled “Anatomy”This is the anatomy of the Range Date Time Input Widget in case you want to use your CSS styles.
<gui-range-date-time-input class="gui-range-date-time gui-field"> <gui-range-date-time allow-edit="" class="gui-field"> <span class="gui-label" id="fieldUid_label"><span class="gui-label__text">Meetings</span></span> <div class="gui-widget"> <div role="group" class="gui-widget-input gui-parts-ring gui-range-date-time-input" aria-label="Meetings"> <gui-pills class="gui-range-date-time-input__pills"> <div class="gui-pills__strip-wrapper gui-pills--end-shadow gui-pills--start-shadow"> <div class="gui-pills__strip" role="toolbar" tabindex="-1" aria-label="Selected date-time ranges"> <span class="gui-sentinel gui-sentinel__start"></span> <button type="button" class="gui-pills__pill gui-pills__pill--clickable" tabindex="-1" aria-pressed="false" aria-label="09/07/2026, 09:00 AM - 09/07/2026, 11:00 AM" aria-description="Edit range 09/07/2026, 09:00 AM - 09/07/2026, 11:00 AM. Remove date-time" > <span class="gui-pills__pill-text">09/07/2026, 09:00 AM - 09/07/2026, 11:00 AM</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="-1" aria-pressed="false" aria-label="09/08/2026, 02:00 PM - 09/09/2026, 09:30 AM" aria-description="Edit range 09/08/2026, 02:00 PM - 09/09/2026, 09:30 AM. Remove date-time" > <span class="gui-pills__pill-text">09/08/2026, 02:00 PM - 09/09/2026, 09:30 AM</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> <div class="gui-pills__compact"> <button type="button" aria-haspopup="true" class="gui-pills__count" aria-label="2 date-time ranges" aria-expanded="false" aria-controls="fieldUid_pills_dropdown" > 2 </button> </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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="month" data-group="start" maxlength="2" placeholder="mm" tabindex="0" aria-label="Month" aria-valuemin="1" aria-valuemax="12" /> <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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="day" data-group="start" maxlength="2" placeholder="dd" tabindex="-1" aria-label="Day" aria-valuemin="1" aria-valuemax="31" /> <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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part gui-parts__year gui-range-date-time-input__year" data-type="year" data-group="start" maxlength="4" placeholder="yyyy" tabindex="-1" aria-label="Year" aria-valuemin="1900" aria-valuemax="9999" /> <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 dir="ltr" class="gui-parts__time-cluster gui-range-date-time-input__time-cluster"> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="hour" data-group="start" maxlength="2" placeholder="hh" tabindex="-1" aria-label="Hour" aria-valuemin="1" aria-valuemax="12" /> <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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="minute" data-group="start" maxlength="2" placeholder="mm" tabindex="-1" aria-label="Minute" aria-valuemin="0" aria-valuemax="59" /> <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__touch-target gui-range-date-time-input__touch-target"> <button type="button" class="gui-parts__part gui-parts__dayperiod gui-range-date-time-input__part gui-range-date-time-input__dayperiod" data-type="dayPeriod" data-group="start" tabindex="-1" aria-label="AM/PM" > AM </button> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> </div> <span class="gui-range-date-time-input__separator">to</span> <div class="gui-parts gui-range-date-time-input__field" role="group" aria-label="End date-time"> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="month" data-group="end" maxlength="2" placeholder="mm" tabindex="-1" aria-label="Month" aria-valuemin="1" aria-valuemax="12" /> <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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="day" data-group="end" maxlength="2" placeholder="dd" tabindex="-1" aria-label="Day" aria-valuemin="1" aria-valuemax="31" /> <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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part gui-parts__year gui-range-date-time-input__year" data-type="year" data-group="end" maxlength="4" placeholder="yyyy" tabindex="-1" aria-label="Year" aria-valuemin="1900" aria-valuemax="9999" /> <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 dir="ltr" class="gui-parts__time-cluster gui-range-date-time-input__time-cluster"> <div class="gui-parts__touch-target gui-range-date-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="hour" data-group="end" maxlength="2" placeholder="hh" tabindex="-1" aria-label="Hour" aria-valuemin="1" aria-valuemax="12" /> <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" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-date-time-input__part" data-type="minute" data-group="end" maxlength="2" placeholder="mm" tabindex="-1" aria-label="Minute" aria-valuemin="0" aria-valuemax="59" /> <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__touch-target gui-range-date-time-input__touch-target"> <button type="button" class="gui-parts__part gui-parts__dayperiod gui-range-date-time-input__part gui-range-date-time-input__dayperiod" data-type="dayPeriod" data-group="end" tabindex="-1" aria-label="AM/PM" > AM </button> <div class="gui-parts__visual-underline gui-range-date-time-input__visual-underline"></div> </div> </div> </div> </div> <div class="gui-visually-hidden" aria-live="polite"></div> </div> </gui-range-date-time></gui-range-date-time-input>