Range Time Picker
Range Time Picker widgets are Input Fields that let the user enter one or more time ranges in a single compact field: they can type the ranges using their keyboard or open a drop-down popover with two scrollable time lists, one for the start time and one for the end time, to select them visually. Each completed range is displayed as a pill that can be removed, or edited in place with allowEdit. The end list stays disabled until a start time is chosen, then only offers slots strictly after it. For a keyboard-only variant use the Range Time Input, and for a single (non-range) time use the Time Picker.
The widget emits an array of { start, end } ISO time strings (HH:mm:ss) on change — pair it with the { format: 'time' } validator.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('timeRanges', { label: 'Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "timeRanges", "label": "Time Ranges" } ]}Use these props to customize your Range Time Picker widget.
| prop | type | description |
|---|---|---|
allowCustomTime | boolean | Allow typing a time; when false (default) values come only from the lists |
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 |
dayPeriodAriaLabel | string | Aria label for the AM/PM segment input. Defaults to AM/PM |
disabledRangeMessage | string | Localizable | Error shown when a selected time falls inside a disabled range |
disabledRanges | array | Array of { start, end } time ranges (both ends inclusive) that render disabled |
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}. |
endTimeAriaLabel | string | Aria label for the end time field group. Defaults to End time |
endTimeLabel | string | Localizable | Visible heading above the end time list. Defaults to End time |
height | number | Height of the time list viewport in pixels |
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 time behind; the value goes to null so validators report it |
itemHeight | number | Height of each time list option in pixels |
maxTime | string | Latest allowed time (ISO time, inclusive) |
maxTimeMessage | string | Localizable | Error shown when a typed time is after maxTime |
minTime | string | Earliest allowed time (ISO time, inclusive) |
minTimeMessage | string | Localizable | Error shown when a typed time is before minTime |
minuteAriaLabel | string | Aria label for the minute segment input. Defaults to Minute |
minuteStep | number | Granularity of the generated time options |
noAvailableTimesMessage | string | Localizable | Message shown when the bounds yield no selectable times. Defaults to No available times |
rangeOrderMessage | string | Localizable | Error shown when the end time is not strictly after the start time |
removePillAriaLabel | string | Aria label for the remove pill action. Defaults to Remove time |
separator | string | The separator between start and end time fields. Defaults to - |
startTimeAriaLabel | string | Aria label for the start time field group. Defaults to Start time |
startTimeLabel | string | Localizable | Visible heading above the start time list. Defaults to Start time |
toggleAriaLabel | string | Aria label for the popup toggle button. Defaults to Show time list |
Use the property hint to add a description or validation instructions.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('timeRanges', { hint: 'Pick one or more time ranges from the lists.', label: 'Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "timeRanges", "label": "Time Ranges", "props": { "hint": "Pick one or more time ranges from the lists." } } ]}Use the property icon to add an icon, which appears next to the toggle chevron. The value of icon represents a set of CSS classes separated by spaces.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('timeRanges', { icon: 'schedule', label: 'Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "timeRanges", "label": "Time Ranges", "props": { "icon": "schedule" } } ]}Min and Max Times
Section titled “Min and Max Times”Use minTime and maxTime (ISO times, both inclusive) to bound the generated options, and minuteStep to set their granularity. A typed time outside the boundaries raises an input error; customize (and localize) the messages with minTimeMessage and maxTimeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('officeHours', { hint: 'Choose ranges between 06:00 and 22:00', hourFormat: '24', minTime: '06:00:00', maxTime: '22:00:00', minuteStep: 30, minTimeMessage: 'No times before 06:00', maxTimeMessage: 'No times after 22:00', label: 'Office Hours', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "officeHours", "label": "Office Hours", "props": { "hint": "Choose ranges between 06:00 and 22:00", "hourFormat": "24", "minTime": "06:00:00", "maxTime": "22:00:00", "minuteStep": 30, "minTimeMessage": "No times before 06:00", "maxTimeMessage": "No times after 22:00" } } ]}Disabled Ranges
Section titled “Disabled Ranges”Use disabledRanges to grey out options inside { start, end } time ranges (both ends inclusive) in both lists. A selection that overlaps a disabled range raises an input error whose message you can customize (and localize) with disabledRangeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('shifts', { hint: 'The lunch break between 13:00 and 14:00 is unavailable', hourFormat: '24', minuteStep: 30, disabledRanges: [ { start: '13:00:00', end: '14:00:00', }, ], disabledRangeMessage: 'Time is within a disabled range.', label: 'Shifts', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "shifts", "label": "Shifts", "props": { "hint": "The lunch break between 13:00 and 14:00 is unavailable", "hourFormat": "24", "minuteStep": 30, "disabledRanges": [ { "start": "13:00:00", "end": "14:00:00" } ], "disabledRangeMessage": "Time is within a disabled range." } } ]}Allow Custom Time
Section titled “Allow Custom Time”Use the property allowCustomTime to let the user type any time directly in the input, in addition to picking from the lists. Typed times are still validated against minTime, maxTime and disabledRanges.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('timeRanges', { hint: 'Type a time or pick one from the lists.', hourFormat: '24', minuteStep: 30, allowCustomTime: true, label: 'Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "timeRanges", "label": "Time Ranges", "props": { "hint": "Type a time or pick one from the lists.", "hourFormat": "24", "minuteStep": 30, "allowCustomTime": true } } ]}Time Labels
Section titled “Time Labels”Use the properties startTimeLabel and endTimeLabel to customize the headings shown above the two lists. Both are localizable and default to Start time and End time.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('timeRanges', { hourFormat: '24', minuteStep: 30, startTimeLabel: 'Time In', endTimeLabel: 'Time Out', label: 'Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "timeRanges", "label": "Time Ranges", "props": { "hourFormat": "24", "minuteStep": 30, "startTimeLabel": "Time In", "endTimeLabel": "Time Out" } } ]}Aria Labels
Section titled “Aria Labels”Use the properties removePillAriaLabel, startTimeAriaLabel and endTimeAriaLabel to customize the accessibility labels for screen readers. These properties are localizable.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.rangeTimePicker('timeRanges', { removePillAriaLabel: 'Delete range', startTimeAriaLabel: 'From time', endTimeAriaLabel: 'To time', label: 'Time Ranges', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "timeRanges", "label": "Time Ranges", "props": { "removePillAriaLabel": "Delete range", "startTimeAriaLabel": "From time", "endTimeAriaLabel": "To 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.
During an edit, list picks reshape the range without committing; Enter in the segments or the pill’s check icon confirms the replacement. With allowCustomTime off, ArrowUp and ArrowDown in a segment step that endpoint through the list’s enabled slots and Enter confirms, so the whole edit can be done from the keyboard.
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.rangeTimePicker('editableSlots', { allowEdit: true, allowCustomTime: true, hourFormat: '24', minuteStep: 30, separator: 'to', label: 'Slots', defaultValue: [ { start: '09:00:00', end: '12:00:00', }, { start: '14:00:00', end: '17:00:00', }, ], }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "rangeTimePicker", "path": "editableSlots", "label": "Slots", "defaultValue": [ { "start": "09:00:00", "end": "12:00:00" }, { "start": "14:00:00", "end": "17:00:00" } ], "props": { "allowEdit": true, "allowCustomTime": true, "hourFormat": "24", "minuteStep": 30, "separator": "to" } } ]}Styling
Section titled “Styling”Range Time Pickers can be styled as explained in the Styling Guide. The typed field and the popover time lists 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 Time Picker popover positioning and toggle, plus those inherited from its sub-widgets.
| CSS Variable | Description |
|---|---|
--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-intent-primary | Pill and selected time option background color |
--gui-intent-primary-hover | Background color for time options on hover |
--gui-intent-primary-text | Text color for the selected and hovered time options |
--gui-color-white | Pill text and remove button color |
--gui-border-default | Pills wrapper border and time list container border color |
--gui-bg-default | Time list popover background color |
--gui-bg-disabled | Background color for disabled time options |
--gui-text-default | Input text color and active piece (hour/minute) underline color |
--gui-text-muted | Text color of the “no available times” message |
--gui-radius-md | Pill and time list popover border radius |
--gui-radius-full | Pill count button and remove button border radius |
--gui-time-list-height | Height of the time list viewport |
--gui-time-list-item-height | Height of each time list option row |
--gui-time-list-columns | Number of columns in each time list |
--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 Time Picker Widget in case you want to use your CSS styles.
<gui-range-time-picker-input class="gui-range-time-picker gui-field"> <gui-range-time-picker allow-custom-time="" allow-edit=""> <span class="gui-label" id="fieldUid_label"><span class="gui-label__text">Slots</span></span> <div class="gui-widget"> <gui-range-time id="time-input" class="gui-field"> <div class="gui-widget"> <div role="group" class="gui-widget-input gui-parts-ring gui-range-time-input" aria-label="Time range input"> <gui-pills class="gui-range-time-input__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 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:00 - 12:00" aria-description="Edit range 09:00 - 12:00. Remove time" > <span class="gui-pills__pill-text">09:00 - 12:00</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="14:00 - 17:00" aria-description="Edit range 14:00 - 17:00. Remove time" > <span class="gui-pills__pill-text">14:00 - 17:00</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 time ranges" aria-expanded="false" aria-controls="fieldUid_pills_dropdown" > 2 </button> </div> </gui-pills> <div class="gui-range-time-input__inputs"> <div class="gui-parts gui-range-time-input__field" role="group" aria-label="Start time"> <div dir="ltr" class="gui-parts__time-cluster gui-range-time-input__time-cluster"> <div class="gui-parts__touch-target gui-range-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-time-input__part" data-type="hour" data-group="start" maxlength="2" placeholder="hh" tabindex="0" aria-label="Hour" aria-valuemin="0" aria-valuemax="23" /> <div class="gui-parts__visual-underline gui-range-time-input__visual-underline"></div> </div> <span class="gui-range-time-input__separator">:</span> <div class="gui-parts__touch-target gui-range-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-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-time-input__visual-underline"></div> </div> </div> </div> <span class="gui-range-time-input__separator">to</span> <div class="gui-parts gui-range-time-input__field" role="group" aria-label="End time"> <div dir="ltr" class="gui-parts__time-cluster gui-range-time-input__time-cluster"> <div class="gui-parts__touch-target gui-range-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-time-input__part" data-type="hour" data-group="end" maxlength="2" placeholder="hh" tabindex="-1" aria-label="Hour" aria-valuemin="0" aria-valuemax="23" /> <div class="gui-parts__visual-underline gui-range-time-input__visual-underline"></div> </div> <span class="gui-range-time-input__separator">:</span> <div class="gui-parts__touch-target gui-range-time-input__touch-target"> <input type="text" inputmode="numeric" role="spinbutton" autocomplete="off" class="gui-parts__part gui-range-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-time-input__visual-underline"></div> </div> </div> </div> </div> </div> <div class="gui-visually-hidden" aria-live="polite"></div> </div> </gui-range-time> <button type="button" class="gui-range-time-picker__arrow" aria-haspopup="dialog" aria-label="Show time list" aria-expanded="false" aria-controls="fieldUid_popup" > <span class="gui-caret" aria-hidden="true"></span> </button> </div> <ul class="gui-validator gui-validator--empty" id="fieldUid_errors" role="alert"></ul> </gui-range-time-picker></gui-range-time-picker-input>