Date-time picker
<gui-date-time-picker> is a date and time field with a popup. The user types the date and time
part by part, as in a Date-time input, or picks them from a
Date-time calendar: a calendar with a time picker
on top. Its value is a local ISO date-time, such as 2026-10-05T18:45:00.
import { GuiDateTimePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimePickerBasic() { return <GuiDateTimePicker label="Appointment" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-time-picker';
@Component({ selector: 'app-date-time-picker-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-date-time-picker label="Appointment"></gui-date-time-picker>`,})export class DateTimePickerBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';</script>
<template> <gui-date-time-picker label="Appointment"></gui-date-time-picker></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
@customElement('date-time-picker-basic')export class DateTimePickerBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-picker label="Appointment"></gui-date-time-picker> `; }}<gui-date-time-picker label="Appointment"></gui-date-time-picker>The element’s entry point is @golemui/gui-components/date-time-picker, which registers
<gui-date-time-picker> and the elements it is made of: <gui-date-time>,
<gui-date-time-calendar> and its <gui-time-picker>. 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 { GuiDateTimePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function AppointmentField() { const [appointment, setAppointment] = useState('');
return ( <GuiDateTimePicker label="Appointment" name="appointment" value={appointment} onGuiChange={(event) => setAppointment(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, GuiDateTimePickerEvents } from '@golemui/gui-components';import '@golemui/gui-components/date-time-picker';
type ChangeEvent = GuiEventMap<typeof GuiDateTimePickerEvents>['gui-change'];
@Component({ selector: 'app-appointment-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-picker label="Appointment" name="appointment" [value]="appointment" (gui-change)="onChange($event)" ></gui-date-time-picker> `,})export class AppointmentFieldComponent { appointment = '';
onChange(event: Event) { this.appointment = (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-picker';import '@golemui/gui-components/index.css';
const appointment = ref('');</script>
<template> <gui-date-time-picker label="Appointment" name="appointment" :value="appointment" @gui-change="appointment = $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-picker';import '@golemui/gui-components/index.css';
@customElement('appointment-field')export class AppointmentField extends LitElement { @state() private appointment = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-picker label="Appointment" name="appointment" .value=${this.appointment} @gui-change=${(event: CustomEvent) => (this.appointment = event.detail.value)} ></gui-date-time-picker> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-date-time-picker label="Appointment" name="appointment"></gui-date-time-picker>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-date-time-picker');
element.addEventListener('gui-change', (event) => { console.log(event.detail.value);});Properties
| Property | Type | Default | Description |
|---|---|---|---|
icon | string | '' | Icon class name shown inside the control, for example from an icon font. |
toggleAriaLabel
attribute toggle-aria-label | string | – | Accessible name of the button that opens the popup. An empty value keeps the default. |
dayAriaLabel
attribute day-aria-label | string | – | Accessible name of the day part. An empty value keeps the default. |
monthAriaLabel
attribute month-aria-label | string | – | Accessible name of the month part. An empty value keeps the default. |
yearAriaLabel
attribute year-aria-label | string | – | Accessible name of the year part. An empty value keeps the default. |
hourAriaLabel
attribute hour-aria-label | string | – | Accessible name of the hour part. An empty value keeps the default. |
minuteAriaLabel
attribute minute-aria-label | string | – | Accessible name of the minute part. An empty value keeps the default. |
dayPeriodAriaLabel
attribute day-period-aria-label | string | – | Accessible name of the AM/PM part. An empty value keeps the default. |
showErrors
attribute show-errors | boolean | true | Whether the element renders its own error list. Elements that embed it turn it off and show the errors themselves. |
localeId
attribute locale-id | string | – | BCP 47 locale for formatting and parsing, such as en-US or es. |
value | string | – | The date and time, as an ISO date-time (YYYY-MM-DDTHH:mm:ss). |
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' | – | How day numbers are written. |
weekdayFormat
attribute weekday-format | 'short' | 'long' | 'narrow' | – | How weekday names are written in the header. |
monthFormat
attribute month-format | 'numeric' | '2-digit' | 'long' | 'short' | 'narrow' | – | 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 | – | Number of months shown side by side. |
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. |
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 in the calendar. An empty value keeps the default. |
invalidDateMessage
attribute invalid-date-message | string | – | Error for a complete but impossible date, such as February 31. |
minDateMessage
attribute min-date-message | string | – | Error for a date before minDate. |
maxDateMessage
attribute max-date-message | string | – | Error for a date after maxDate. |
disabledDateRangeMessage
attribute disabled-date-range-message | string | – | Error for a date inside disabledRanges. |
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. |
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 committed the value. detail.value is the committed value. |
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. |
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”Picking a date and a time
Section titled “Picking a date and a time”value takes a local ISO date-time, YYYY-MM-DDTHH:mm:ss, with no time zone. The popup opens
when a part of the field gets focus, or from the button at the end of the field, on the month of
the value.
The value needs a day and a time. Pick a day, then a time: the time picker at the top of the popup opens its list in place of the days, and picking a time sets the value, closes the popup and returns focus to the field. Once there is a value, picking another day keeps its time and changes the value at once, with the popup still open. Escape, a click outside or moving focus out of the element closes the popup too.
Each change of the value fires gui-input and then gui-change, with the date-time in
event.detail.value.
Value: 2026-11-30T10:00:00
import { useState } from 'react';import { GuiDateTimePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimePickerValue() { const [value, setValue] = useState<string | undefined>('2026-11-30T10:00:00');
return ( <> <GuiDateTimePicker id="date-time-picker-call" label="Call with Grace" value={value} onGuiChange={(event) => setValue(event.detail.value)} /> <p role="status">Value: {value ?? 'empty'}</p> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiDateTimePickerEvents, GuiEventMap,} from '@golemui/gui-components';import '@golemui/gui-components/date-time-picker';
type DateTimePickerEvent<K extends keyof typeof GuiDateTimePickerEvents> = GuiEventMap<typeof GuiDateTimePickerEvents>[K];
@Component({ selector: 'app-date-time-picker-value', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-picker id="date-time-picker-call" label="Call with Grace" [value]="value()" (gui-change)="onChange($event)" ></gui-date-time-picker> <p id="date-time-picker-call-value" role="status"> Value: {{ value() ?? 'empty' }} </p> `,})export class DateTimePickerValueComponent { value = signal<string | undefined>('2026-11-30T10:00:00');
onChange(event: Event) { this.value.set((event as DateTimePickerEvent<'gui-change'>).detail.value); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
const value = ref<string | undefined>('2026-11-30T10:00:00');</script>
<template> <gui-date-time-picker id="date-time-picker-call" label="Call with Grace" :value="value" @gui-change="value = $event.detail.value" ></gui-date-time-picker> <p id="date-time-picker-call-value" role="status"> Value: {{ value ?? 'empty' }} </p></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiDateTimePickerEvents, GuiEventMap,} from '@golemui/gui-components';import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
type DateTimePickerEvent<K extends keyof typeof GuiDateTimePickerEvents> = GuiEventMap<typeof GuiDateTimePickerEvents>[K];
@customElement('date-time-picker-value')export class DateTimePickerValue extends LitElement { @state() private value: string | undefined = '2026-11-30T10:00:00';
protected override createRenderRoot() { return this; }
private onChange(event: DateTimePickerEvent<'gui-change'>) { this.value = event.detail.value; }
override render() { return html` <gui-date-time-picker id="date-time-picker-call" label="Call with Grace" .value=${this.value} @gui-change=${this.onChange} ></gui-date-time-picker> <p role="status">Value: ${this.value ?? 'empty'}</p> `; }}<gui-date-time-picker id="date-time-picker-call" label="Call with Grace" value="2026-11-30T10:00:00"></gui-date-time-picker><p id="date-time-picker-call-value" role="status">Value: 2026-11-30T10:00:00</p>
<script type="module"> const call = document.querySelector('#date-time-picker-call');
call.addEventListener('gui-change', (event) => { document.querySelector('#date-time-picker-call-value').textContent = `Value: ${event.detail.value ?? 'empty'}`; });</script>The time picker in the popup 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 names
it: Time by default. allow-custom-time lets the user type any time there, as in the
Time picker. locale-id and hour-format
set the order of the parts and the clock, as on the
Date-time input.
import { GuiDateTimePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimePickerTimes() { return ( <GuiDateTimePicker label="Pickup" timeLabel="Pickup time" minuteStep={15} minTime="08:00:00" maxTime="12:00:00" value="2026-12-14T09:00:00" /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-time-picker';
@Component({ selector: 'app-date-time-picker-times', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-picker label="Pickup" time-label="Pickup time" [minuteStep]="15" min-time="08:00:00" max-time="12:00:00" value="2026-12-14T09:00:00" ></gui-date-time-picker> `,})export class DateTimePickerTimesComponent {}<script setup lang="ts">import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';</script>
<template> <gui-date-time-picker label="Pickup" time-label="Pickup time" :minute-step="15" min-time="08:00:00" max-time="12:00:00" value="2026-12-14T09:00:00" ></gui-date-time-picker></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
@customElement('date-time-picker-times')export class DateTimePickerTimes extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-picker label="Pickup" time-label="Pickup time" minute-step="15" min-time="08:00:00" max-time="12:00:00" value="2026-12-14T09:00:00" ></gui-date-time-picker> `; }}<gui-date-time-picker label="Pickup" time-label="Pickup time" minute-step="15" min-time="08:00:00" max-time="12:00:00" value="2026-12-14T09:00:00"></gui-date-time-picker>Disabled dates and times
Section titled “Disabled dates and times”min-date, max-date and disabled-ranges work as on the
Date picker: those days can’t
be picked. 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: below, the value is on a Saturday, so its afternoon is disabled; pick a
weekday and it opens up.
import { GuiDateTimePicker } from '@golemui/gui-components/react';import type { DateRange, DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/index.css';
const christmas: DateRange[] = [{ start: '2026-12-24', end: '2026-12-26' }];
const unavailable: DisabledTimeRange[] = [ { start: '13:00:00', end: '13:59:00' }, { start: '16:00:00', end: '19:00:00', weekdays: [6] },];
export function DateTimePickerDisabled() { return ( <GuiDateTimePicker label="Haircut" hint="Closed over Christmas. No appointments over lunch, or after 4:00 PM on Saturdays." minuteStep={60} minTime="09:00:00" maxTime="19:00:00" value="2026-12-12T10:00:00" disabledRanges={christmas} disabledTimeRanges={unavailable} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { DateRange, DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/date-time-picker';
@Component({ selector: 'app-date-time-picker-disabled', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-time-picker label="Haircut" hint="Closed over Christmas. No appointments over lunch, or after 4:00 PM on Saturdays." [minuteStep]="60" min-time="09:00:00" max-time="19:00:00" value="2026-12-12T10:00:00" [disabledRanges]="closed" [disabledTimeRanges]="breaks" ></gui-date-time-picker> `,})export class DateTimePickerDisabledComponent { closed: DateRange[] = [{ start: '2026-12-24', end: '2026-12-26' }];
breaks: DisabledTimeRange[] = [ { start: '13:00:00', end: '13:59:00' }, { start: '16:00:00', end: '19:00:00', weekdays: [6] }, ];}<script setup lang="ts">import type { DateRange, DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
const disabledRanges: DateRange[] = [ { start: '2026-12-24', end: '2026-12-26' },];const disabledTimeRanges: DisabledTimeRange[] = [ { start: '13:00:00', end: '13:59:00' }, { start: '16:00:00', end: '19:00:00', weekdays: [6] },];</script>
<template> <gui-date-time-picker label="Haircut" hint="Closed over Christmas. No appointments over lunch, or after 4:00 PM on Saturdays." :minute-step="60" min-time="09:00:00" max-time="19:00:00" value="2026-12-12T10:00:00" :disabledRanges.prop="disabledRanges" :disabledTimeRanges.prop="disabledTimeRanges" ></gui-date-time-picker></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import type { DateRange, DisabledTimeRange } from '@golemui/gui-components';import '@golemui/gui-components/date-time-picker';import '@golemui/gui-components/index.css';
const christmas: DateRange[] = [{ start: '2026-12-24', end: '2026-12-26' }];
const breaks: DisabledTimeRange[] = [ { start: '13:00:00', end: '13:59:00' }, { start: '16:00:00', end: '19:00:00', weekdays: [6] },];
@customElement('date-time-picker-disabled')export class DateTimePickerDisabled extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-time-picker label="Haircut" hint="Closed over Christmas. No appointments over lunch, or after 4:00 PM on Saturdays." minute-step="60" min-time="09:00:00" max-time="19:00:00" value="2026-12-12T10:00:00" .disabledRanges=${christmas} .disabledTimeRanges=${breaks} ></gui-date-time-picker> `; }}<gui-date-time-picker label="Haircut" hint="Closed over Christmas. No appointments over lunch, or after 4:00 PM on Saturdays." minute-step="60" min-time="09:00:00" max-time="19:00:00" value="2026-12-12T10:00:00" disabled-ranges='[{ "start": "2026-12-24", "end": "2026-12-26" }]' disabled-time-ranges='[ { "start": "13:00:00", "end": "13:59:00" }, { "start": "16:00:00", "end": "19:00:00", "weekdays": [6] } ]'></gui-date-time-picker>A typed date-time outside these is kept as the value, marked invalid for its form and reported with
gui-input-error; show the message with errors, as on the
Date picker. Replace the English
messages with min-date-message, max-date-message, disabled-date-range-message,
min-time-message, max-time-message, disabled-time-range-message, invalid-date-message and
incomplete-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. disabled, readonly and icon work as on the
Date picker.
In a form
Section titled “In a form”With a name, <gui-date-time-picker> submits its ISO date-time under that name, such as
viewingAt=2026-11-30T10:30:00, and nothing while it is empty. These block a submit:
required, while the field is empty;- a day before
min-date, aftermax-dateor indisabled-ranges; - a time before
min-time, aftermax-timeor indisabled-time-rangeson that day.
An impossible date, such as February 31, doesn’t change the value, so it doesn’t block a submit by itself.
import { useState } from 'react';import { GuiButton, GuiDateTimePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DateTimePickerNativeForm() { const [result, setResult] = useState('');
return ( <form id="date-time-picker-form" onSubmit={(event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(event.currentTarget)); setResult(`Submitted ${JSON.stringify(data)}`); }} > <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell gui-grid__cell--span-3"> <GuiDateTimePicker label="Viewing" name="viewingAt" minuteStep={30} minTime="10:00:00" maxTime="18:00:00" required /> </div> <div className="gui-grid__cell"> <GuiButton type="submit" label="Book" /> </div> </div> <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-picker';
@Component({ selector: 'app-date-time-picker-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="date-time-picker-form" #form (submit)="onSubmit($event, form)"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-date-time-picker label="Viewing" name="viewingAt" [minuteStep]="30" min-time="10:00:00" max-time="18:00:00" required ></gui-date-time-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p id="date-time-picker-form-result" role="status">{{ result() }}</p> </form> `,})export class DateTimePickerNativeFormComponent { 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-picker';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-picker-form" ref="form" @submit.prevent="onSubmit"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-date-time-picker label="Viewing" name="viewingAt" :minute-step="30" min-time="10:00:00" max-time="18:00:00" :required="true" ></gui-date-time-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p id="date-time-picker-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-picker';import '@golemui/gui-components/index.css';
@customElement('date-time-picker-native-form')export class DateTimePickerNativeForm 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-picker-form" @submit=${this.onSubmit}> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-date-time-picker label="Viewing" name="viewingAt" minute-step="30" min-time="10:00:00" max-time="18:00:00" required ></gui-date-time-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p role="status"> ${this.data && `Submitted ${JSON.stringify(this.data)}`} </p> </form> `; }}<form id="date-time-picker-form"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-date-time-picker label="Viewing" name="viewingAt" minute-step="30" min-time="10:00:00" max-time="18:00:00" required ></gui-date-time-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p id="date-time-picker-form-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#date-time-picker-form');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#date-time-picker-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-picker> renders into the light DOM, so you can style every part with plain CSS.
It is a <gui-date-time>, a toggle button and a panel with a <gui-date-time-calendar>, which is
there only while it is open:
<gui-date-time-picker> <span class="gui-label" id="appointment_label"> <span class="gui-label__text">Appointment</span> </span> <div class="gui-widget"> <gui-date-time id="appointment_date" class="gui-field"> <!-- The date and time parts, as in gui-date-time --> </gui-date-time> <button type="button" class="gui-date-time-picker__arrow" aria-haspopup="dialog" aria-label="Show calendar" aria-expanded="true" aria-controls="appointment_popup" > <span class="gui-caret" aria-hidden="true"></span> </button> <div class="gui-picker__panel"> <gui-date-time-calendar role="dialog" id="appointment_popup" aria-labelledby="appointment_label" class="gui-field" > <!-- The calendar and its time picker, as in gui-date-time-calendar --> </gui-date-time-calendar> </div> </div> <div class="gui-validator" id="appointment_errors" role="alert"></div></gui-date-time-picker>| Class | Part |
|---|---|
gui-label, gui-label__text | The label and its text with the required marker. |
gui-widget-hint | The hint. |
gui-widget | The box around the field, the button and the panel. |
gui-date-time-picker__arrow | The button that opens and closes the popup. Its caret turns while the popup is open. |
gui-picker__panel | The floating panel around the calendar. |
gui-validator | The errors container under the field. The alert role announces new errors. |
gui-validator--panel | A copy of the errors at the bottom of the open panel, hidden from screen readers. |
gui-validator__error | One error message. |
The field’s classes are on the Date-time input page and the popup’s on the Date-time calendar page.
Tokens
Section titled “Tokens”The field reads the tokens of the Date input, the calendar those of the Calendar and the time list those of the Time picker. The panel is the Date picker’s. Two tokens size the time grid in the popup:
| 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. */gui-date-time-picker { --gui-calendar-time-grid-height: 280px;}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 field’s group and the popup. Without a label the popup is namedCalendar. - Toggle. The button at the end is named
Show calendar, hasaria-haspopup="dialog", reportsaria-expandedand points at the popup witharia-controls. Rename it withtoggle-aria-label, or translate it for every element under the keyshowCalendar. - Popup. The calendar has the
dialogrole. Its buttons, days and time picker are named as on the Date-time calendar; the picker takes the same*-aria-labelprops andtime-label, and passes them on. - Parts. The field’s parts are named as on the Date-time input.
| Key | Action |
|---|---|
| Tab | Moves from the field to the button, then through the popup while it is open, then out. |
| Enter / Space on the button | Opens the popup and focuses today or the selected day, or closes it. |
| Escape | In the open time list, closes it. Elsewhere, closes the popup and returns focus to the field. |
| Keys in the popup | Pick a day and a time, as on the Date-time calendar. |
| Keys in the field | Type and move between the parts, as on the Date-time input. |
What every element shares, such as translating the browser messages, is in Accessibility.