Date-time calendar
<gui-date-time-calendar> is a Calendar with a
Time picker on top, always visible, to pick a date and a
time. Its value is a local ISO date-time, such as 2026-10-05T18:45:00. For a field that opens
it in a popup, use the Date-time picker.
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimeCalendarBasic() { return <GuiDateTimeCalendar label="Pickup" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-time-calendar';
@Component({ selector: 'app-date-time-calendar-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-date-time-calendar label="Pickup"></gui-date-time-calendar>`,})export class DateTimeCalendarBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';</script>
<template> <gui-date-time-calendar label="Pickup"></gui-date-time-calendar></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
@customElement('date-time-calendar-basic')export class DateTimeCalendarBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-calendar label="Pickup"></gui-date-time-calendar> `; }}<gui-date-time-calendar label="Pickup"></gui-date-time-calendar>The element’s entry point is @golemui/gui-components/date-time-calendar, which registers
<gui-date-time-calendar> and the <gui-time-picker> inside it. To install the package and load
the stylesheet, see Installation.
The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.
import { useState } from 'react';import { GuiDateTimeCalendar } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PickupAtField() { const [pickupAt, setPickupAt] = useState('');
return ( <GuiDateTimeCalendar label="Pickup" name="pickupAt" value={pickupAt} onGuiChange={(event) => setPickupAt(event.detail.value ?? '')} /> );}Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, and the stylesheet to styles in angular.json.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { GuiEventMap, GuiDateTimeCalendarEvents } from '@golemui/gui-components';import '@golemui/gui-components/date-time-calendar';
type ChangeEvent = GuiEventMap<typeof GuiDateTimeCalendarEvents>['gui-change'];
@Component({ selector: 'app-pickup-at-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-calendar label="Pickup" name="pickupAt" [value]="pickupAt" (gui-change)="onChange($event)" ></gui-date-time-calendar> `,})export class PickupAtFieldComponent { pickupAt = '';
onChange(event: Event) { this.pickupAt = (event as ChangeEvent).detail.value ?? ''; }}"styles": ["node_modules/@golemui/gui-components/lib/styles/index.css"]Tell Vue's compiler that gui-* tags are custom elements, and add the typings once for typed props and @gui-* handlers.
<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
const pickupAt = ref('');</script>
<template> <gui-date-time-calendar label="Pickup" name="pickupAt" :value="pickupAt" @gui-change="pickupAt = $event.detail.value ?? ''" /></template>vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') } },});import type {} from '@golemui/gui-components/vue';The page's stylesheet styles the gui-* elements, so it only reaches them outside a shadow root. Render the host component into the light DOM too, as below, or load the stylesheet into its shadow root.
import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
@customElement('pickup-at-field')export class PickupAtField extends LitElement { @state() private pickupAt = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-calendar label="Pickup" name="pickupAt" .value=${this.pickupAt} @gui-change=${(event: CustomEvent) => (this.pickupAt = event.detail.value)} ></gui-date-time-calendar> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-date-time-calendar label="Pickup" name="pickupAt"></gui-date-time-calendar>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-date-time-calendar');
element.addEventListener('gui-change', (event) => { console.log(event.detail.value);});Properties
| Property | Type | Default | Description |
|---|---|---|---|
showHint
attribute show-hint | boolean | true | Whether the element renders its hint. Elements that embed it turn it off and show the hint themselves: aria-describedby still points at the hint by its id. |
localeId
attribute locale-id | string | – | BCP 47 locale for formatting and parsing, such as en-US or es. |
prevMonthIcon
attribute prev-month-icon | string | '' | Icon class name of the previous-month button. |
nextMonthIcon
attribute next-month-icon | string | '' | Icon class name of the next-month button. |
prevMonthAriaLabel
attribute prev-month-aria-label | string | – | Accessible name of the previous-month button. An empty value keeps the default. |
nextMonthAriaLabel
attribute next-month-aria-label | string | – | Accessible name of the next-month button. An empty value keeps the default. |
selectYearAriaLabel
attribute select-year-aria-label | string | – | Accessible name of the button that opens the year grid. An empty value keeps the default. |
yearGridAriaLabel
attribute year-grid-aria-label | string | – | Accessible name of the year grid. An empty value keeps the default. |
dayFormat
attribute day-format | 'numeric' | '2-digit' | 'numeric' | How day numbers are written. |
weekdayFormat
attribute weekday-format | 'short' | 'long' | 'narrow' | 'narrow' | How weekday names are written in the header. |
monthFormat
attribute month-format | 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' | 'long' | How the month is written in the header. |
minDate
attribute min-date | string | – | Earliest selectable date, as an ISO date (YYYY-MM-DD). |
maxDate
attribute max-date | string | – | Latest selectable date, as an ISO date (YYYY-MM-DD). |
disabledRanges
attribute disabled-ranges | DateRange[] | – | Dates that cannot be picked, as { start, end } ISO date ranges. |
numberOfMonths
attribute number-of-months | number | 1 | Number of months shown side by side. |
value | string | – | The date and time, as an ISO date-time (YYYY-MM-DDTHH:mm:ss). |
hourFormat
attribute hour-format | HourFormat | – | 12- or 24-hour clock. Defaults to the locale's. |
minuteStep
attribute minute-step | number | – | Minutes between the times offered in the list. |
minTime
attribute min-time | string | – | Earliest selectable time, as an ISO time (HH:mm:ss). |
maxTime
attribute max-time | string | – | Latest selectable time, as an ISO time (HH:mm:ss). |
disabledTimeRanges
attribute disabled-time-ranges | DisabledTimeRange[] | – | Times that cannot be picked, optionally only on a date or on some weekdays. |
minTimeMessage
attribute min-time-message | string | – | Error for a time before minTime. |
maxTimeMessage
attribute max-time-message | string | – | Error for a time after maxTime. |
disabledTimeRangeMessage
attribute disabled-time-range-message | string | – | Error for a time inside disabledTimeRanges. |
noAvailableTimesMessage
attribute no-available-times-message | string | – | Text shown when no time can be picked. |
allowCustomTime
attribute allow-custom-time | boolean | false | Allows typing any time, not only picking one from the list. |
timeLabel
attribute time-label | string | – | Label of the time. An empty value keeps the default. |
incompleteMessage
attribute incomplete-message | string | – | Error when focus leaves a partly filled value. |
uid | string | – | The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration. |
Form control properties
Every field has these, from the base class of the form controls.
| Property | Type | Default | Description |
|---|---|---|---|
name | string | – | The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute. |
label | string | – | The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds. |
hint | string | – | Help text shown under the label and announced as the control's description. |
errors | string[] | [] | Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false. |
touched | boolean | – | Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true. |
required | boolean | false | The control needs a value: it is marked as required and, with a name, blocks submission. |
readOnly
attribute readonly | boolean | – | The value can be read and focused but not changed. |
disabled | boolean | – | Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's. |
form read-only, no attribute | HTMLFormElement | null | – | The <form> the element belongs to, if any. |
validity read-only, no attribute | ValidityState | – | The element's validity, like a native control's. |
validationMessage read-only, no attribute | string | – | The message the browser shows for an invalid value. |
Methods
| Method | Description |
|---|---|
checkValidity(): boolean | – |
reportValidity(): boolean | – |
Events
Every event bubbles and is composed, so you can listen on any ancestor.
| Event | Description |
|---|---|
gui-input | The user changed the value. detail.value is the new value. |
gui-change | The user picked a time for the chosen day. detail.value is the date-time. |
gui-blur | Focus left the control. |
gui-input-error | The element rejected what the user entered, such as an impossible date or a value out of bounds. detail.message is the error; show it through errors. |
gui-parts-change | The typed parts changed before they form a complete value, for a host that mirrors them. |
CSS custom properties
| Property | Default | Description |
|---|---|---|
--gui-calendar-width | – | Width of one month. |
--gui-calendar-day-button-size | – | Size of each day. |
--gui-calendar-change-month-button-width | – | Width of the previous- and next-month buttons. |
--gui-calendar-change-month-button-height | – | Height of the previous- and next-month buttons. |
--gui-calendar-year-button-width | – | Width of each year in the year grid. |
--gui-calendar-year-button-height | – | Height of each year in the year grid. |
--gui-calendar-year-grid-height | – | Height of the year grid. |
--gui-calendar-time-grid-height | – | Height of the time grid. |
--gui-calendar-time-button-height | – | Height of each time in the grid. |
Examples
Section titled “Examples”Value and events
Section titled “Value and events”value takes a local ISO date-time, YYYY-MM-DDTHH:mm:ss, with no time zone, and the calendar
shows its month. The value needs a day and a time. The time picker’s list opens in place of the
days when its field gets focus, or from its button, and closes when the user picks a time.
- Picking a time, with a day picked, fires
gui-inputand thengui-change. - Picking another day, with a time picked, keeps the time and fires
gui-inputonly. - A day without a time, or a time without a day, sets no value yet.
Value: 2026-11-18T11:00:00
import { useState } from 'react';import { GuiDateTimeCalendar } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimeCalendarValue() { const [value, setValue] = useState<string | undefined>('2026-11-18T11:00:00'); const [lastEvent, setLastEvent] = useState<'gui-input' | 'gui-change'>();
return ( <> <GuiDateTimeCalendar id="date-time-calendar-tour" label="Museum tour" value={value} onGuiInput={(event) => { setValue(event.detail.value); setLastEvent('gui-input'); }} onGuiChange={(event) => { setValue(event.detail.value); setLastEvent('gui-change'); }} /> <p role="status"> {lastEvent ? `${lastEvent}: ${value}` : `Value: ${value}`} </p> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiDateTimeCalendarEvents, GuiEventMap,} from '@golemui/gui-components';import '@golemui/gui-components/date-time-calendar';
type DateTimeCalendarEvent<K extends keyof typeof GuiDateTimeCalendarEvents> = GuiEventMap<typeof GuiDateTimeCalendarEvents>[K];
@Component({ selector: 'app-date-time-calendar-value', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-calendar id="date-time-calendar-tour" label="Museum tour" value="2026-11-18T11:00:00" (gui-input)="onInput($event)" (gui-change)="onChange($event)" ></gui-date-time-calendar> <p id="date-time-calendar-tour-value" role="status">{{ status() }}</p> `,})export class DateTimeCalendarValueComponent { status = signal('Value: 2026-11-18T11:00:00');
onInput(event: Event) { const { value } = (event as DateTimeCalendarEvent<'gui-input'>).detail; this.status.set(`gui-input: ${value}`); }
onChange(event: Event) { const { value } = (event as DateTimeCalendarEvent<'gui-change'>).detail; this.status.set(`gui-change: ${value}`); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
const status = ref('Value: 2026-11-18T11:00:00');</script>
<template> <gui-date-time-calendar id="date-time-calendar-tour" label="Museum tour" value="2026-11-18T11:00:00" @gui-input="status = `gui-input: ${$event.detail.value}`" @gui-change="status = `gui-change: ${$event.detail.value}`" ></gui-date-time-calendar> <p id="date-time-calendar-tour-value" role="status">{{ status }}</p></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiDateTimeCalendarEvents, GuiEventMap,} from '@golemui/gui-components';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
type DateTimeCalendarEvent<K extends keyof typeof GuiDateTimeCalendarEvents> = GuiEventMap<typeof GuiDateTimeCalendarEvents>[K];
@customElement('date-time-calendar-value')export class DateTimeCalendarValue extends LitElement { @state() private status = 'Value: 2026-11-18T11:00:00';
protected override createRenderRoot() { return this; }
private onInput(event: DateTimeCalendarEvent<'gui-input'>) { this.status = `gui-input: ${event.detail.value}`; }
private onChange(event: DateTimeCalendarEvent<'gui-change'>) { this.status = `gui-change: ${event.detail.value}`; }
override render() { return html` <gui-date-time-calendar id="date-time-calendar-tour" label="Museum tour" value="2026-11-18T11:00:00" @gui-input=${this.onInput} @gui-change=${this.onChange} ></gui-date-time-calendar> <p role="status">${this.status}</p> `; }}<gui-date-time-calendar id="date-time-calendar-tour" label="Museum tour" value="2026-11-18T11:00:00"></gui-date-time-calendar><p id="date-time-calendar-tour-value" role="status">Value: 2026-11-18T11:00:00</p>
<script type="module"> const tour = document.querySelector('#date-time-calendar-tour'); const status = document.querySelector('#date-time-calendar-tour-value');
tour.addEventListener('gui-input', (event) => { status.textContent = `gui-input: ${event.detail.value}`; }); tour.addEventListener('gui-change', (event) => { status.textContent = `gui-change: ${event.detail.value}`; });</script>The time picker takes minute-step, the minutes between its times, 30 by default, and
min-time and max-time, inclusive ISO times that apply on every day. time-label is its label:
Time by default. allow-custom-time lets the user type any time in it, as in the
Time picker. locale-id and hour-format
set the clock, and locale-id the names of the calendar, as on the
Calendar.
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimeCalendarTimes() { return ( <GuiDateTimeCalendar label="Tasting" timeLabel="Seating" localeId="en-GB" minuteStep={15} minTime="18:00:00" maxTime="21:00:00" value="2026-12-14T19:30:00" /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-time-calendar';
@Component({ selector: 'app-date-time-calendar-times', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-calendar label="Tasting" time-label="Seating" locale-id="en-GB" [minuteStep]="15" min-time="18:00:00" max-time="21:00:00" value="2026-12-14T19:30:00" ></gui-date-time-calendar> `,})export class DateTimeCalendarTimesComponent {}<script setup lang="ts">import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';</script>
<template> <gui-date-time-calendar label="Tasting" time-label="Seating" locale-id="en-GB" :minute-step="15" min-time="18:00:00" max-time="21:00:00" value="2026-12-14T19:30:00" ></gui-date-time-calendar></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
@customElement('date-time-calendar-times')export class DateTimeCalendarTimes extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-calendar label="Tasting" time-label="Seating" locale-id="en-GB" minute-step="15" min-time="18:00:00" max-time="21:00:00" value="2026-12-14T19:30:00" ></gui-date-time-calendar> `; }}<gui-date-time-calendar label="Tasting" time-label="Seating" locale-id="en-GB" minute-step="15" min-time="18:00:00" max-time="21:00:00" value="2026-12-14T19:30:00"></gui-date-time-calendar>Disabled dates and times
Section titled “Disabled dates and times”min-date, max-date and disabled-ranges disable days, as on the
Calendar. disabled-time-ranges
takes a JSON list of { "start", "end" } ISO times, both ends included, that can’t be picked.
Scope an entry to some days with weekdays, a list of day numbers from 0 for Sunday to 6 for
Saturday, or to one day with date, an ISO date. The time list follows the picked day: pick a
Friday below, or December 16.
import { GuiDateTimeCalendar } from '@golemui/gui-components/react';import type { DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/index.css';
const afternoonsOff: DisabledTimeRange[] = [ { start: '12:00:00', end: '17:00:00', weekdays: [5] }, { start: '12:00:00', end: '17:00:00', date: '2026-12-16' },];
export function DateTimeCalendarDisabled() { return ( <GuiDateTimeCalendar label="Workshop slot" hint="Mornings only on Fridays and on December 16." minuteStep={60} minTime="09:00:00" maxTime="17:00:00" value="2026-12-14T10:00:00" disabledTimeRanges={afternoonsOff} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/date-time-calendar';
@Component({ selector: 'app-date-time-calendar-disabled', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-calendar label="Workshop slot" hint="Mornings only on Fridays and on December 16." [minuteStep]="60" min-time="09:00:00" max-time="17:00:00" value="2026-12-14T10:00:00" [disabledTimeRanges]="afternoons" ></gui-date-time-calendar> `,})export class DateTimeCalendarDisabledComponent { afternoons: DisabledTimeRange[] = [ { start: '12:00:00', end: '17:00:00', weekdays: [5] }, { start: '12:00:00', end: '17:00:00', date: '2026-12-16' }, ];}<script setup lang="ts">import type { DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
const disabledTimeRanges: DisabledTimeRange[] = [ { start: '12:00:00', end: '17:00:00', weekdays: [5] }, { start: '12:00:00', end: '17:00:00', date: '2026-12-16' },];</script>
<template> <gui-date-time-calendar label="Workshop slot" hint="Mornings only on Fridays and on December 16." :minute-step="60" min-time="09:00:00" max-time="17:00:00" value="2026-12-14T10:00:00" :disabledTimeRanges.prop="disabledTimeRanges" ></gui-date-time-calendar></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import type { DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
const afternoonsOff: DisabledTimeRange[] = [ { start: '12:00:00', end: '17:00:00', weekdays: [5] }, { start: '12:00:00', end: '17:00:00', date: '2026-12-16' },];
@customElement('date-time-calendar-disabled')export class DateTimeCalendarDisabled extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-calendar label="Workshop slot" hint="Mornings only on Fridays and on December 16." minute-step="60" min-time="09:00:00" max-time="17:00:00" value="2026-12-14T10:00:00" .disabledTimeRanges=${afternoonsOff} ></gui-date-time-calendar> `; }}<gui-date-time-calendar label="Workshop slot" hint="Mornings only on Fridays and on December 16." minute-step="60" min-time="09:00:00" max-time="17:00:00" value="2026-12-14T10:00:00" disabled-time-ranges='[ { "start": "12:00:00", "end": "17:00:00", "weekdays": [5] }, { "start": "12:00:00", "end": "17:00:00", "date": "2026-12-16" } ]'></gui-date-time-calendar>Replace the English messages with min-time-message, max-time-message,
disabled-time-range-message, incomplete-message and no-available-times-message, or translate
them for every element as in i18n. number-of-months, the
calendar formats and the month button icons work as on the
Calendar.
In a form
Section titled “In a form”With a name, <gui-date-time-calendar> submits its ISO date-time under that name, such as
deliverAt=2026-11-18T14:00:00, and nothing until both a day and a time are picked. required
blocks the submit until then. A disabled calendar can’t be used and isn’t submitted; a
readonly one shows its value and is submitted, but can’t be changed.
import { useState } from 'react';import { GuiButton, GuiDateTimeCalendar } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimeCalendarNativeForm() { const [result, setResult] = useState('');
return ( <form id="date-time-calendar-form" onSubmit={(event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(event.currentTarget)); setResult(`Submitted ${JSON.stringify(data)}`); }} > <GuiDateTimeCalendar label="Delivery" name="deliverAt" minuteStep={60} minTime="08:00:00" maxTime="20:00:00" required /> <p> <GuiButton type="submit" label="Confirm delivery" /> </p> <p role="status">{result}</p> </form> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/date-time-calendar';
@Component({ selector: 'app-date-time-calendar-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="date-time-calendar-form" #form (submit)="onSubmit($event, form)"> <gui-date-time-calendar label="Delivery" name="deliverAt" [minuteStep]="60" min-time="08:00:00" max-time="20:00:00" required ></gui-date-time-calendar> <p> <gui-button type="submit" label="Confirm delivery"></gui-button> </p> <p id="date-time-calendar-form-result" role="status">{{ result() }}</p> </form> `,})export class DateTimeCalendarNativeFormComponent { result = signal('');
onSubmit(event: Event, form: HTMLFormElement) { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); this.result.set(`Submitted ${JSON.stringify(data)}`); }}<script setup lang="ts">import { ref, useTemplateRef } from 'vue';import '@golemui/gui-components/button';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
const form = useTemplateRef('form');const result = ref('');
function onSubmit() { if (!form.value) return; const data = Object.fromEntries(new FormData(form.value)); result.value = `Submitted ${JSON.stringify(data)}`;}</script>
<template> <form id="date-time-calendar-form" ref="form" @submit.prevent="onSubmit"> <gui-date-time-calendar label="Delivery" name="deliverAt" :minute-step="60" min-time="08:00:00" max-time="20:00:00" :required="true" ></gui-date-time-calendar> <p> <gui-button type="submit" label="Confirm delivery"></gui-button> </p> <p id="date-time-calendar-form-result" role="status">{{ result }}</p> </form></template>import { LitElement, html } from 'lit';import { customElement, query, state } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/date-time-calendar';import '@golemui/gui-components/index.css';
@customElement('date-time-calendar-native-form')export class DateTimeCalendarNativeForm extends LitElement { @query('form') private form!: HTMLFormElement; @state() private data?: Record<string, FormDataEntryValue>;
protected override createRenderRoot() { return this; }
private onSubmit(event: SubmitEvent) { event.preventDefault(); this.data = Object.fromEntries(new FormData(this.form)); }
override render() { return html` <form id="date-time-calendar-form" @submit=${this.onSubmit}> <gui-date-time-calendar label="Delivery" name="deliverAt" minute-step="60" min-time="08:00:00" max-time="20:00:00" required ></gui-date-time-calendar> <p> <gui-button type="submit" label="Confirm delivery"></gui-button> </p> <p role="status"> ${this.data && `Submitted ${JSON.stringify(this.data)}`} </p> </form> `; }}<form id="date-time-calendar-form"> <gui-date-time-calendar label="Delivery" name="deliverAt" minute-step="60" min-time="08:00:00" max-time="20:00:00" required ></gui-date-time-calendar> <p> <gui-button type="submit" label="Confirm delivery"></gui-button> </p> <p id="date-time-calendar-form-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#date-time-calendar-form');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#date-time-calendar-form-result').textContent = `Submitted ${JSON.stringify(data)}`; });</script>How the elements submit, validate and reset is in Native forms.
Styling
Section titled “Styling”Anatomy
Section titled “Anatomy”<gui-date-time-calendar> renders into the light DOM, so you can style every part with plain CSS.
It is the Calendar’s markup, with a row for a
Time picker under the first month’s header. Its
list is a grid of four columns, and while it is open it takes the place of the days. Here in
en-US:
<gui-date-time-calendar class="gui-field"> <label class="gui-label" for="pickup" id="pickup_calendar_label"> <span class="gui-label__text">Pickup</span> </label> <div class="gui-widget"> <div class="gui-calendar-input" role="group" id="pickup_calendar" aria-labelledby="pickup_calendar_label" > <div class="gui-visually-hidden" aria-live="polite">October 2026</div> <div class="gui-calendar__container"> <!-- The previous-month button --> <div class="gui-calendar__months-grid"> <div class="gui-calendar__panel"> <header class="gui-calendar__header"> <!-- The month name and the year button --> </header> <div class="gui-calendar__time-input-row"> <gui-time-picker class="gui-time-picker gui-field"> <span class="gui-label" id="pickup-time_label"> <span class="gui-label__text">Time</span> </span> <div class="gui-widget"> <!-- The time field and its button, as in gui-time-picker --> <div class="gui-picker__panel" hidden> <gui-time-list role="dialog" id="pickup-time_popup" aria-labelledby="pickup-time_label" hidden > <div class="gui-time-list__viewport gui-time-list__viewport--grid" role="grid" aria-label="Time" > <div role="row" class="gui-time-list__row"> <button type="button" role="gridcell" class="gui-time-list__option" tabindex="-1" data-value="00:00:00" aria-selected="false" aria-disabled="false" > 12:00 AM </button> <!-- Three more times --> </div> <!-- The other rows --> </div> </gui-time-list> </div> </div> </gui-time-picker> </div> <div class="gui-calendar__days-grid" role="grid" aria-label="October 2026" > <!-- The weekdays and the days, as in gui-calendar --> </div> </div> </div> <!-- The next-month button --> </div> </div> </div></gui-date-time-calendar>| Class | Part |
|---|---|
gui-calendar__time-input-row | The row of the time picker, under the first month’s header. |
gui-time-picker | The time picker in that row. |
The other classes are those of the Calendar and the Time picker.
Tokens
Section titled “Tokens”The calendar reads the tokens of the Calendar, and the time picker those of the Time picker. Two more size its grid of times:
| Token | What it styles |
|---|---|
--gui-calendar-time-grid-height | The height of the grid of times, which scrolls. |
--gui-calendar-time-button-height | The height of each time in the grid. |
/* A taller grid of times, with roomier times. */gui-date-time-calendar { --gui-calendar-time-grid-height: 280px; --gui-calendar-time-button-height: 40px;}To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.
Accessibility
Section titled “Accessibility”- Name. The
labelnames the calendar’s group. The days, the month buttons, the year button and the year grid are named as on the Calendar, with the same*-aria-labelprops. - Time. The time picker is labelled
Time, which names its field and its list. Change it withtime-label, or translate it for every element under the keytime. Its button and parts are named as on the Time picker. - List. The times are a
gridof four columns, each time a grid cell witharia-selected.
| Key | Action |
|---|---|
| Tab | Moves between the month buttons, the year button, the time field, its button and the days grid. |
| Keys in the days grid and the year grid | As on the Calendar. |
| Keys in the time field and the list | As on the Time picker. |
| Escape in the list | Closes the list, shows the days again and returns focus to the time field. |
What every element shares, such as translating the browser messages, is in Accessibility.