Skip to content

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. 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.

range-time-picker.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeTimePicker('timeRanges', {
label: 'Time Ranges',
}),
];

Use these props to customize your Range Time Picker widget.

proptypedescription
allowCustomTimebooleanAllow typing a time; when false (default) values come only from the lists
disabledRangeMessagestring | LocalizableError shown when a selected time falls inside a disabled range
disabledRangesarrayArray of { start, end } time ranges (both ends inclusive) that render disabled
endTimeAriaLabelstringAria label for the end time field group. Defaults to End time
endTimeLabelstring | LocalizableVisible heading above the end time list. Defaults to End time
heightnumberHeight of the time list viewport in pixels
hintstringA description to display below the input
hourFormat'12' | '24'Force 12- or 24-hour entry. Defaults to the user’s locale
iconstringA css class to display an icon inside the input
itemHeightnumberHeight of each time list option in pixels
maxTimestringLatest allowed time (ISO time, inclusive)
maxTimeMessagestring | LocalizableError shown when a typed time is after maxTime
minTimestringEarliest allowed time (ISO time, inclusive)
minTimeMessagestring | LocalizableError shown when a typed time is before minTime
minuteStepnumberGranularity of the generated time options
noAvailableTimesMessagestring | LocalizableMessage shown when the bounds yield no selectable times. Defaults to No available times
rangeOrderMessagestring | LocalizableError shown when the end time is not strictly after the start time
removePillAriaLabelstringAria label for the remove pill action. Defaults to Remove time
separatorstringThe separator between start and end time fields. Defaults to -
startTimeAriaLabelstringAria label for the start time field group. Defaults to Start time
startTimeLabelstring | LocalizableVisible heading above the start time list. Defaults to Start time

Use the property hint to add a description or validation instructions.

range-time-picker.ts
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',
}),
];

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.

range-time-picker.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeTimePicker('timeRanges', {
icon: 'schedule',
label: 'Time Ranges',
}),
];

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.

range-time-picker.ts
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',
}),
];

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.

range-time-picker.ts
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',
}),
];

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.

range-time-picker.ts
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',
}),
];

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.

range-time-picker.ts
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',
}),
];

Use the properties removePillAriaLabel, startTimeAriaLabel and endTimeAriaLabel to customize the accessibility labels for screen readers. These properties are localizable.

range-time-picker.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.rangeTimePicker('timeRanges', {
removePillAriaLabel: 'Delete range',
startTimeAriaLabel: 'From time',
endTimeAriaLabel: 'To time',
label: 'Time Ranges',
}),
];

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.

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 VariableDescription
--gui-intent-errorBorder and focus shadow color when invalid
--gui-border-focusBorder color when a pill or input is focused
--gui-shadow-focusBox shadow when a pill or input is focused
--gui-intent-primaryPill and selected time option background color
--gui-intent-primary-hoverBackground color for time options on hover
--gui-intent-primary-textText color for the selected and hovered time options
--gui-color-whitePill text and remove button color
--gui-border-defaultPills wrapper border and time list container border color
--gui-bg-defaultTime list popover background color
--gui-bg-disabledBackground color for disabled time options
--gui-text-defaultInput text color and active piece (hour/minute) underline color
--gui-text-mutedText color of the “no available times” message
--gui-radius-mdPill and time list popover border radius
--gui-radius-fullPill count button and remove button border radius
--gui-time-list-heightHeight of the time list viewport
--gui-time-list-item-heightHeight of each time list option row
--gui-time-list-columnsNumber of columns in each time list

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

range-time-picker-anatomy.html
<div class="gui-form">
<gui-range-time-picker>
<label for="fieldUid">
Label
<div class="gui-widget-hint" id="fieldUid_hint">Hint</div>
</label>
<div role="button" tabindex="-1" class="gui-widget" aria-expanded="true">
<!-- The typing surface is a Range Time Input; readonly unless allowCustomTime -->
<gui-range-time id="time-input" class="gui-field">
<div class="gui-widget">
<div class="gui-widget-input gui-parts-ring gui-range-time-input gui-range-time-input--icon" role="group" aria-label="Time range input">
<span class="gui-widget-icon schedule" data-icon="schedule"></span>
<gui-pills class="gui-range-time-input__pills">
<div class="gui-pills__strip-wrapper">
<div class="gui-pills__strip" role="list" tabindex="-1">
<span class="gui-sentinel gui-sentinel__start"></span>
<div class="gui-pills__pill gui-pills__pill--clickable" role="listitem" data-key="09:00:00/12:00:00" tabindex="0" aria-label="Remove time: 09:00 - 12:00">
<span class="gui-pills__pill-text">09:00 - 12:00</span>
<button type="button" class="gui-pills__pill-remove" tabindex="-1" aria-hidden="true">&times;</button>
</div>
<span class="gui-sentinel gui-sentinel__end"></span>
</div>
</div>
<!-- On overflow the strip collapses into a count button + dropdown -->
<div class="gui-pills__compact">
<button type="button" class="gui-pills__count" aria-label="1 time ranges">1</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 class="gui-range-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-time-input__part" data-type="hour" data-group="start" maxlength="2" placeholder="hh" tabindex="0">
<div class="gui-range-time-input__visual-underline"></div>
</div>
<span class="gui-range-time-input__separator">:</span>
<div class="gui-range-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-time-input__part" data-type="minute" data-group="start" maxlength="2" placeholder="mm" tabindex="-1">
<div class="gui-range-time-input__visual-underline"></div>
</div>
</div>
<span class="gui-range-time-input__separator">-</span>
<div class="gui-parts gui-range-time-input__field" role="group" aria-label="End time">
<div class="gui-range-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-time-input__part" data-type="hour" data-group="end" maxlength="2" placeholder="hh" tabindex="-1">
<div class="gui-range-time-input__visual-underline"></div>
</div>
<span class="gui-range-time-input__separator">:</span>
<div class="gui-range-time-input__touch-target">
<input type="text" inputmode="numeric" class="gui-range-time-input__part" data-type="minute" data-group="end" maxlength="2" placeholder="mm" tabindex="-1">
<div class="gui-range-time-input__visual-underline"></div>
</div>
</div>
</div>
</div>
</div>
</gui-range-time>
<span class="gui-range-time-picker__arrow">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path></svg>
</span>
<!-- Drop-down: two time lists, one per range endpoint -->
<div class="gui-range-time-picker__panel" id="list-panel" role="group">
<div class="gui-range-time-picker__column">
<span class="gui-range-time-picker__column-label">Start time</span>
<gui-time-list class="gui-range-time-picker__list">
<div class="gui-time-list__viewport" role="listbox" style="--gui-time-list-height: 300px; --gui-time-list-item-height: 40px; --gui-time-list-columns: 1;">
<button type="button" role="option" class="gui-time-list__option gui-time-list__option--selected" tabindex="0" data-value="09:00:00" aria-selected="true" aria-disabled="false">09:00</button>
<button type="button" role="option" class="gui-time-list__option" tabindex="-1" data-value="13:00:00" aria-selected="false" aria-disabled="true" disabled>13:00</button>
</div>
</gui-time-list>
</div>
<div class="gui-range-time-picker__column">
<span class="gui-range-time-picker__column-label">End time</span>
<!-- Disabled until a start time is picked; its floor is one step after start -->
<gui-time-list class="gui-range-time-picker__list">
<div class="gui-time-list__viewport" role="listbox" style="--gui-time-list-height: 300px; --gui-time-list-item-height: 40px; --gui-time-list-columns: 1;">
<button type="button" role="option" class="gui-time-list__option gui-time-list__option--selected" tabindex="0" data-value="12:00:00" aria-selected="true" aria-disabled="false">12:00</button>
</div>
<!-- When the bounds yield no slots, the viewport is replaced by: -->
<!-- <div class="gui-time-list__empty">No available times</div> -->
</gui-time-list>
</div>
</div>
</div>
</gui-range-time-picker>
</div>