Date Time Calendar
Date Time Calendar widgets are Input Fields that let the user select a date and a time from a single interactive calendar. The user picks a day from the grid and a time from a segmented input with a drop-down of slots; the slots are generated from minTime to maxTime (inclusive) stepping minuteStep minutes.
The widget emits a local ISO date-time string (YYYY-MM-DDTHH:mm:ss) only when both the day and the time are selected, pair it with the { format: 'date-time' } validator. Picking a different day clears the time and resets the value to null, and time selection stays disabled until a day is selected. For a compact single-field variant that opens as a popover, use the Date Time Picker.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { minTime: '09:00:00', maxTime: '18:00:00', minuteStep: 30, label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "minTime": "09:00:00", "maxTime": "18:00:00", "minuteStep": 30 } } ]}Use these props to customize your Date Time Calendar widget. It accepts date props (for the day grid) and time props (for the slots), all listed below. With numberOfMonths greater than 1, the time row renders on the first panel only.
| prop | type | description |
|---|---|---|
allowCustomTime | boolean | Allows typing a time in the input; when false (default) times come only from the slot grid |
disabledRanges | array | Array of { start: string, end?: string } ISO date ranges whose days render disabled |
disabledTimeRangeMessage | string | Localizable | Error message shown when a typed time falls inside a disabled time range |
disabledTimeRanges | array | { start, end, date?, weekdays? } ISO time ranges whose slots render disabled. See below |
hint | string | A description to display below the calendar |
hourFormat | '12' | '24' | Forces 12-hour or 24-hour slot labels and typing mode; when omitted the locale decides |
maxDate | string | Latest selectable date (ISO date, inclusive); later calendar days render disabled |
maxTime | string | Last selectable slot (ISO time, inclusive). Defaults to 23:59:59 |
maxTimeMessage | string | Localizable | Error message shown when a typed time is after maxTime |
minDate | string | Earliest selectable date (ISO date, inclusive); earlier calendar days render disabled |
minTime | string | First selectable slot (ISO time, inclusive). Defaults to 00:00:00 |
minTimeMessage | string | Localizable | Error message shown when a typed time is before minTime |
minuteStep | number | Minutes between generated slots, also the arrow-key minute increment when typing. Defaults to 30 |
nextMonthAriaLabel | string | ARIA label for the next month button |
nextMonthIcon | string | Custom CSS class for the “next month” button icon |
noAvailableTimesMessage | string | Localizable | Shown in the slot grid when the bounds yield no slots. Defaults to No available times |
numberOfMonths | number | Number of months to display side-by-side. Defaults to 1 |
prevMonthAriaLabel | string | ARIA label for the previous month button |
prevMonthIcon | string | Custom CSS class for the “previous month” button icon |
Use the property hint to add a description.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { hint: 'Pick a day first, then a time slot', minTime: '09:00:00', maxTime: '18:00:00', minuteStep: 30, label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "hint": "Pick a day first, then a time slot", "minTime": "09:00:00", "maxTime": "18:00:00", "minuteStep": 30 } } ]}Custom Icons
Section titled “Custom Icons”Use prevMonthIcon, nextMonthIcon, prevMonthAriaLabel and nextMonthAriaLabel to customize the calendar’s month-navigation buttons, exactly as in the Calendar.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { prevMonthIcon: 'chevron_left', nextMonthIcon: 'chevron_right', prevMonthAriaLabel: 'Go to previous month', nextMonthAriaLabel: 'Go to next month', label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "prevMonthIcon": "chevron_left", "nextMonthIcon": "chevron_right", "prevMonthAriaLabel": "Go to previous month", "nextMonthAriaLabel": "Go to next month" } } ]}Number of Months
Section titled “Number of Months”Use the property numberOfMonths to display multiple months side-by-side. The time input and slot grid render on the first panel only.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { hint: 'Two months side by side; the time row shows on the first panel only', numberOfMonths: 2, minTime: '09:00:00', maxTime: '18:00:00', minuteStep: 30, label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "hint": "Two months side by side; the time row shows on the first panel only", "numberOfMonths": 2, "minTime": "09:00:00", "maxTime": "18:00:00", "minuteStep": 30 } } ]}Custom Time
Section titled “Custom Time”Use the property allowCustomTime to let the user type any time directly in the time field, in addition to picking a slot. Typed times are still validated against minTime and maxTime; customize (and localize) the messages with minTimeMessage and maxTimeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { hint: 'Type any time or pick a 30m slot', allowCustomTime: true, minTime: '09:00:00', maxTime: '18:00:00', minuteStep: 30, minTimeMessage: 'Office not open until 9:00', maxTimeMessage: 'Office closed from 18:00', label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "hint": "Type any time or pick a 30m slot", "allowCustomTime": true, "minTime": "09:00:00", "maxTime": "18:00:00", "minuteStep": 30, "minTimeMessage": "Office not open until 9:00", "maxTimeMessage": "Office closed from 18:00" } } ]}Hour Format
Section titled “Hour Format”Use the property hourFormat to force '12'-hour (AM/PM) or '24'-hour slot labels and typing mode. When omitted, the user’s locale decides.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { hint: '12-hour slot labels and typing (AM/PM)', hourFormat: '12', minTime: '09:00:00', maxTime: '18:00:00', minuteStep: 30, label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "hint": "12-hour slot labels and typing (AM/PM)", "hourFormat": "12", "minTime": "09:00:00", "maxTime": "18:00:00", "minuteStep": 30 } } ]}Disabled Date and Time Ranges
Section titled “Disabled Date and Time Ranges”The date boundaries minDate, maxDate and disabledRanges grey out days. On top of that, disabledTimeRanges greys out time slots, and each entry can be scoped to specific days with date (an ISO date) or weekdays (numbers as returned by Date.prototype.getDay(): 0 = Sunday … 6 = Saturday). An unscoped entry applies every day. Because allowCustomTime is on here, typing a time inside a disabled range raises an input error whose message you set with disabledTimeRangeMessage.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { hint: 'Weekday lunch break disabled; type a time to see the messages', allowCustomTime: true, minTime: '09:00:00', maxTime: '18:00:00', minuteStep: 30, minDate: '2026-02-01', maxDate: '2026-07-31', disabledRanges: [ { start: '2026-02-09', end: '2026-02-10', }, { start: '2026-02-17', }, ], disabledTimeRanges: [ { start: '13:00:00', end: '14:00:00', weekdays: [1, 2, 3, 4, 5], }, { start: '09:00:00', end: '10:30:00', date: '2026-02-13', }, ], disabledTimeRangeMessage: 'This time is not available', label: 'Appointment', defaultValue: '2026-02-16T09:30:00', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "defaultValue": "2026-02-16T09:30:00", "props": { "hint": "Weekday lunch break disabled; type a time to see the messages", "allowCustomTime": true, "minTime": "09:00:00", "maxTime": "18:00:00", "minuteStep": 30, "minDate": "2026-02-01", "maxDate": "2026-07-31", "disabledRanges": [ { "start": "2026-02-09", "end": "2026-02-10" }, { "start": "2026-02-17" } ], "disabledTimeRanges": [ { "start": "13:00:00", "end": "14:00:00", "weekdays": [ 1, 2, 3, 4, 5 ] }, { "start": "09:00:00", "end": "10:30:00", "date": "2026-02-13" } ], "disabledTimeRangeMessage": "This time is not available" } } ]}No Available Times
Section titled “No Available Times”When minTime and maxTime generate no slots for the chosen day, the grid shows an empty-state message. Use noAvailableTimesMessage to customize (and localize) it (it defaults to No available times).
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.dateTimeCalendar('appointmentAt', { hint: 'These bounds generate no slots, so the grid shows the empty-state message', minTime: '18:00:00', maxTime: '09:00:00', noAvailableTimesMessage: 'No slots available for this day', label: 'Appointment', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "dateTimeCalendar", "path": "appointmentAt", "label": "Appointment", "props": { "hint": "These bounds generate no slots, so the grid shows the empty-state message", "minTime": "18:00:00", "maxTime": "09:00:00", "noAvailableTimesMessage": "No slots available for this day" } } ]}Styling
Section titled “Styling”Date Time Calendars can be styled as explained in the Styling Guide. They also use the styling variables of the day grid and the time slot grid.
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-calendar-day-button-size | Day button size, which drives the container width |
--gui-calendar-time-grid-height | Maximum height of the time slot grid |
--gui-calendar-time-button-height | Height of each time slot button |
--gui-space-1 | Gap around the time input row and slot grid |
--gui-intent-primary | Selected day and selected slot background color |
--gui-intent-primary-hover | Slot hover background color |
--gui-bg-disabled | Disabled day and slot background color |
--gui-text-muted | Text color of the “no available times” message |
Anatomy
Section titled “Anatomy”This is the anatomy of the Date Time Calendar Widget in case you want to use your CSS styles.
<gui-date-time-calendar> <label for="fieldUid"> Label <div class="gui-calendar__hint" id="fieldUid_hint">Hint</div> </label>
<div class="gui-widget"> <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">March 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>
<!-- The embedded Time Picker; disabled until a day is selected. Its slot grid opens in place of the days grid below. --> <div class="gui-calendar__time-input-row"> <gui-time-picker class="gui-time-picker gui-field"> <!-- See the Time Picker anatomy: gui-time + arrow + gui-time-list (grid mode) --> </gui-time-picker> </div>
<div class="gui-calendar__days-grid" role="grid" aria-label="March 2026"> <div class="gui-calendar__rows"> <div class="gui-calendar__weekday" role="columnheader">Su</div> <div class="gui-calendar__weekday" role="columnheader">Mo</div> <div class="gui-calendar__weekday" role="columnheader">Tu</div> <div class="gui-calendar__weekday" role="columnheader">We</div> <div class="gui-calendar__weekday" role="columnheader">Th</div> <div class="gui-calendar__weekday" role="columnheader">Fr</div> <div class="gui-calendar__weekday" role="columnheader">Sa</div> </div> <div class="gui-calendar__rows"> <button type="button" role="gridcell" class="gui-calendar__day-button other-month" tabindex="-1" disabled aria-selected="false">25</button> <button type="button" role="gridcell" class="gui-calendar__day-button" tabindex="0" aria-selected="false">1</button> </div> </div> </div> </div>
</div> </div> </div></gui-date-time-calendar>