Date picker
<gui-date-picker> is a date field with a calendar popup. The user types the date part by part,
as in a Date input, or picks it from a
Calendar that opens under the field. Its value is an ISO
date, such as 2026-10-05. For a start and an end date, use the
Date range picker.
import { GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DatePickerBasic() { return <GuiDatePicker label="Check-in" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-picker';
@Component({ selector: 'app-date-picker-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-date-picker label="Check-in"></gui-date-picker>`,})export class DatePickerBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';</script>
<template> <gui-date-picker label="Check-in"></gui-date-picker></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
@customElement('date-picker-basic')export class DatePickerBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html`<gui-date-picker label="Check-in"></gui-date-picker>`; }}<gui-date-picker label="Check-in"></gui-date-picker>The element’s entry point is @golemui/gui-components/date-picker, which registers
<gui-date-picker> and the <gui-date> and <gui-calendar> it is made of. 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 { GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function CheckInField() { const [checkIn, setCheckIn] = useState('');
return ( <GuiDatePicker label="Check-in" name="checkIn" value={checkIn} onGuiChange={(event) => setCheckIn(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, GuiDatePickerEvents } from '@golemui/gui-components';import '@golemui/gui-components/date-picker';
type ChangeEvent = GuiEventMap<typeof GuiDatePickerEvents>['gui-change'];
@Component({ selector: 'app-check-in-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-picker label="Check-in" name="checkIn" [value]="checkIn" (gui-change)="onChange($event)" ></gui-date-picker> `,})export class CheckInFieldComponent { checkIn = '';
onChange(event: Event) { this.checkIn = (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-picker';import '@golemui/gui-components/index.css';
const checkIn = ref('');</script>
<template> <gui-date-picker label="Check-in" name="checkIn" :value="checkIn" @gui-change="checkIn = $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-picker';import '@golemui/gui-components/index.css';
@customElement('check-in-field')export class CheckInField extends LitElement { @state() private checkIn = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-picker label="Check-in" name="checkIn" .value=${this.checkIn} @gui-change=${(event: CustomEvent) => (this.checkIn = event.detail.value)} ></gui-date-picker> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-date-picker label="Check-in" name="checkIn"></gui-date-picker>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-date-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. |
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, as an ISO date (YYYY-MM-DD). |
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. |
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. |
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. |
Examples
Section titled “Examples”Value and events
Section titled “Value and events”value takes an ISO date (YYYY-MM-DD). The calendar 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 or on the current month. It
closes when the user picks a day, presses Escape, clicks outside or moves focus out of
the element; picking a day and Escape return focus to the field.
A picked day, or a date typed in full, fires gui-input and then gui-change, with the ISO date
in event.detail.value.
Value: 2026-11-30
import { useState } from 'react';import { GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DatePickerValue() { const [value, setValue] = useState<string | undefined>('2026-11-30');
return ( <> <GuiDatePicker id="date-picker-delivery" label="Delivery date" value={value} onGuiChange={(event) => setValue(event.detail.value)} /> <p role="status">Value: {value}</p> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiDatePickerEvents, GuiEventMap } from '@golemui/gui-components';import '@golemui/gui-components/date-picker';
type DatePickerEvent<K extends keyof typeof GuiDatePickerEvents> = GuiEventMap< typeof GuiDatePickerEvents>[K];
@Component({ selector: 'app-date-picker-value', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-picker id="date-picker-delivery" label="Delivery date" [value]="value()" (gui-change)="onChange($event)" ></gui-date-picker> <p id="date-picker-delivery-value" role="status">Value: {{ value() }}</p> `,})export class DatePickerValueComponent { value = signal<string | undefined>('2026-11-30');
onChange(event: Event) { this.value.set((event as DatePickerEvent<'gui-change'>).detail.value); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
const value = ref<string | undefined>('2026-11-30');</script>
<template> <gui-date-picker id="date-picker-delivery" label="Delivery date" :value="value" @gui-change="value = $event.detail.value" ></gui-date-picker> <p id="date-picker-delivery-value" role="status">Value: {{ value }}</p></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiDatePickerEvents, GuiEventMap } from '@golemui/gui-components';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
type DatePickerEvent<K extends keyof typeof GuiDatePickerEvents> = GuiEventMap< typeof GuiDatePickerEvents>[K];
@customElement('date-picker-value')export class DatePickerValue extends LitElement { @state() private value: string | undefined = '2026-11-30';
protected override createRenderRoot() { return this; }
private onChange(event: DatePickerEvent<'gui-change'>) { this.value = event.detail.value; }
override render() { return html` <gui-date-picker id="date-picker-delivery" label="Delivery date" .value=${this.value} @gui-change=${this.onChange} ></gui-date-picker> <p role="status">Value: ${this.value}</p> `; }}<gui-date-picker id="date-picker-delivery" label="Delivery date" value="2026-11-30"></gui-date-picker><p id="date-picker-delivery-value" role="status">Value: 2026-11-30</p>
<script type="module"> const delivery = document.querySelector('#date-picker-delivery');
delivery.addEventListener('gui-change', (event) => { document.querySelector('#date-picker-delivery-value').textContent = `Value: ${event.detail.value}`; });</script>Locale
Section titled “Locale”locale-id takes a BCP 47 locale, such as en-US or es-ES. It sets the order of the parts of
the field, the names of the months and weekdays and the first day of the week. Without it, the
picker uses English (en). Set locale-id for another language. The value is the same ISO date
in every locale.
import { GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DatePickerLocale() { return ( <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiDatePicker label="Check-in" localeId="en-US" value="2026-12-14" /> </div> <div className="gui-grid__cell"> <GuiDatePicker label="Llegada" localeId="es-ES" value="2026-12-14" /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-picker';
@Component({ selector: 'app-date-picker-locale', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Check-in" locale-id="en-US" value="2026-12-14" ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Llegada" locale-id="es-ES" value="2026-12-14" ></gui-date-picker> </div> </div> `,})export class DatePickerLocaleComponent {}<script setup lang="ts">import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Check-in" locale-id="en-US" value="2026-12-14" ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Llegada" locale-id="es-ES" value="2026-12-14" ></gui-date-picker> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
@customElement('date-picker-locale')export class DatePickerLocale extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Check-in" locale-id="en-US" value="2026-12-14" ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Llegada" locale-id="es-ES" value="2026-12-14" ></gui-date-picker> </div> </div> `; }}<div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Check-in" locale-id="en-US" value="2026-12-14"></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Llegada" locale-id="es-ES" value="2026-12-14"></gui-date-picker> </div></div>weekday-format, month-format and day-format change how the calendar writes the weekdays,
the month and the day numbers. See Calendar for an example.
Bounds and disabled dates
Section titled “Bounds and disabled dates”min-date and max-date take ISO dates and are inclusive. disabled-ranges takes a JSON list of
{ "start", "end" } ISO dates, inclusive too; leave end out to disable one day. The calendar
can’t pick those days, and its month buttons stop at the bounds.
The user can still type a date the calendar doesn’t offer. The element keeps it, marks it as
invalid for its form and fires gui-input-error with the reason. Show the message with errors,
as below. Change the day to 25 to see it. An impossible date, such as February 31, fires
gui-input-error too, and doesn’t change the value.
import { useState } from 'react';import { GuiDatePicker } from '@golemui/gui-components/react';import type { DateRange } from '@golemui/gui-components';import '@golemui/gui-components/index.css';
const closed: DateRange[] = [ { start: '2026-12-24', end: '2026-12-26' }, { start: '2026-12-31' },];
export function DatePickerBounds() { const [value, setValue] = useState<string | undefined>('2026-12-14'); const [errors, setErrors] = useState<string[]>([]);
return ( <GuiDatePicker id="date-picker-stay" label="Check-in" hint="Open from December to February. Closed over Christmas and on New Year's Eve." value={value} minDate="2026-12-01" maxDate="2027-02-28" disabledRanges={closed} errors={errors} // The element reports what it rejects; show it with errors. onGuiInputError={(event) => setErrors([event.detail.message])} onGuiInput={(event) => { setValue(event.detail.value); setErrors([]); }} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { DateRange, GuiDatePickerEvents, GuiEventMap,} from '@golemui/gui-components';import '@golemui/gui-components/date-picker';
type DatePickerEvent<K extends keyof typeof GuiDatePickerEvents> = GuiEventMap< typeof GuiDatePickerEvents>[K];
@Component({ selector: 'app-date-picker-bounds', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-picker id="date-picker-stay" label="Check-in" hint="Open from December to February. Closed over Christmas and on New Year's Eve." value="2026-12-14" min-date="2026-12-01" max-date="2027-02-28" [disabledRanges]="closed" [errors]="errors()" (gui-input-error)="onInputError($event)" (gui-input)="errors.set([])" ></gui-date-picker> `,})export class DatePickerBoundsComponent { closed: DateRange[] = [ { start: '2026-12-24', end: '2026-12-26' }, { start: '2026-12-31' }, ];
errors = signal<string[]>([]);
// The element reports what it rejects; show it with errors. onInputError(event: Event) { this.errors.set([ (event as DatePickerEvent<'gui-input-error'>).detail.message, ]); }}<script setup lang="ts">import { ref } from 'vue';import type { DateRange } from '@golemui/gui-components';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
const disabledRanges: DateRange[] = [ { start: '2026-12-24', end: '2026-12-26' }, { start: '2026-12-31' },];const errors = ref<string[]>([]);</script>
<template> <!-- The element reports what it rejects; show it with errors. --> <gui-date-picker id="date-picker-stay" label="Check-in" hint="Open from December to February. Closed over Christmas and on New Year's Eve." value="2026-12-14" min-date="2026-12-01" max-date="2027-02-28" :disabledRanges.prop="disabledRanges" :errors.prop="errors" @gui-input-error="errors = [$event.detail.message]" @gui-input="errors = []" ></gui-date-picker></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { DateRange, GuiDatePickerEvents, GuiEventMap,} from '@golemui/gui-components';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
type DatePickerEvent<K extends keyof typeof GuiDatePickerEvents> = GuiEventMap< typeof GuiDatePickerEvents>[K];
const closedDays: DateRange[] = [ { start: '2026-12-24', end: '2026-12-26' }, { start: '2026-12-31' },];
@customElement('date-picker-bounds')export class DatePickerBounds extends LitElement { @state() private errors: string[] = [];
protected override createRenderRoot() { return this; }
// The element reports what it rejects; show it with errors. private onInputError(event: DatePickerEvent<'gui-input-error'>) { this.errors = [event.detail.message]; }
private onInput() { this.errors = []; }
override render() { return html` <gui-date-picker id="date-picker-stay" label="Check-in" hint="Open from December to February. Closed over Christmas and on New Year's Eve." value="2026-12-14" min-date="2026-12-01" max-date="2027-02-28" .disabledRanges=${closedDays} .errors=${this.errors} @gui-input-error=${this.onInputError} @gui-input=${this.onInput} ></gui-date-picker> `; }}<gui-date-picker id="date-picker-stay" label="Check-in" hint="Open from December to February. Closed over Christmas and on New Year's Eve." value="2026-12-14" min-date="2026-12-01" max-date="2027-02-28" disabled-ranges='[{ "start": "2026-12-24", "end": "2026-12-26" }, { "start": "2026-12-31" }]'></gui-date-picker>
<script type="module"> const stay = document.querySelector('#date-picker-stay');
// The element reports what it rejects; show it with errors. stay.addEventListener('gui-input-error', (event) => { stay.errors = [event.detail.message]; }); stay.addEventListener('gui-input', () => { stay.errors = []; });</script>Replace the English messages with min-date-message, max-date-message,
disabled-date-range-message, invalid-date-message and incomplete-message, or translate them
for every element as in i18n.
Several months
Section titled “Several months”number-of-months shows that many months side by side. The month buttons move one month at a
time.
import { GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DatePickerMonths() { return ( <GuiDatePicker label="Departure" numberOfMonths={2} value="2026-12-14" /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-picker';
@Component({ selector: 'app-date-picker-months', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-date-picker label="Departure" [numberOfMonths]="2" value="2026-12-14" ></gui-date-picker> `,})export class DatePickerMonthsComponent {}<script setup lang="ts">import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';</script>
<template> <gui-date-picker label="Departure" :number-of-months="2" value="2026-12-14" ></gui-date-picker></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
@customElement('date-picker-months')export class DatePickerMonths extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-date-picker label="Departure" number-of-months="2" value="2026-12-14" ></gui-date-picker> `; }}<gui-date-picker label="Departure" number-of-months="2" value="2026-12-14"></gui-date-picker>Disabled, read-only and icon
Section titled “Disabled, read-only and icon”A disabled picker can’t be focused, changed or opened, and a form doesn’t submit it. A
readonly picker opens its calendar but doesn’t change, and a form submits its value. icon
shows an icon at the start of the field, as on the
Date input.
import { GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DatePickerDisabledReadonly() { return ( <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiDatePicker label="Ordered on" value="2026-09-21" disabled /> </div> <div className="gui-grid__cell"> <GuiDatePicker label="Shipped on" value="2026-09-23" readOnly /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/date-picker';
@Component({ selector: 'app-date-picker-disabled-readonly', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Ordered on" value="2026-09-21" disabled ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Shipped on" value="2026-09-23" readonly ></gui-date-picker> </div> </div> `,})export class DatePickerDisabledReadonlyComponent {}<script setup lang="ts">import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Ordered on" value="2026-09-21" :disabled="true" ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Shipped on" value="2026-09-23" :readOnly="true" ></gui-date-picker> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/date-picker';import '@golemui/gui-components/index.css';
@customElement('date-picker-disabled-readonly')export class DatePickerDisabledReadonly extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Ordered on" value="2026-09-21" disabled ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Shipped on" value="2026-09-23" readonly ></gui-date-picker> </div> </div> `; }}<div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-date-picker label="Ordered on" value="2026-09-21" disabled></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-date-picker label="Shipped on" value="2026-09-23" readonly></gui-date-picker> </div></div>In a form
Section titled “In a form”With a name, <gui-date-picker> submits its ISO date under that name, such as
checkIn=2026-12-14, and nothing while it is empty. required, a date outside min-date and
max-date and a date in disabled-ranges block a submit. An impossible date, such as February
31, doesn’t change the value, so it doesn’t block a submit by itself. Clear the field and submit, then type a
date in March.
import { useState } from 'react';import { GuiButton, GuiDatePicker } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function DatePickerNativeForm() { const [checkIn, setCheckIn] = useState<string | undefined>('2026-12-14'); const [result, setResult] = useState('');
return ( <form id="date-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"> <GuiDatePicker label="Check-in" name="checkIn" minDate="2026-12-01" maxDate="2027-02-28" value={checkIn} onGuiChange={(event) => setCheckIn(event.detail.value)} 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-picker';
@Component({ selector: 'app-date-picker-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="date-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-picker label="Check-in" name="checkIn" min-date="2026-12-01" max-date="2027-02-28" value="2026-12-14" required ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p id="date-picker-form-result" role="status">{{ result() }}</p> </form> `,})export class DatePickerNativeFormComponent { 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-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-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-picker label="Check-in" name="checkIn" min-date="2026-12-01" max-date="2027-02-28" value="2026-12-14" :required="true" ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p id="date-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-picker';import '@golemui/gui-components/index.css';
@customElement('date-picker-native-form')export class DatePickerNativeForm 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-picker-form" @submit=${this.onSubmit}> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-date-picker label="Check-in" name="checkIn" min-date="2026-12-01" max-date="2027-02-28" value="2026-12-14" required ></gui-date-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-picker-form"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-date-picker label="Check-in" name="checkIn" min-date="2026-12-01" max-date="2027-02-28" value="2026-12-14" required ></gui-date-picker> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Book"></gui-button> </div> </div> <p id="date-picker-form-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#date-picker-form');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#date-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-picker> renders into the light DOM, so you can style every part with plain CSS. It is
a <gui-date>, a toggle button and a panel with a <gui-calendar>, which is there only while it
is open. Here it is open, with en-US parts:
<gui-date-picker> <span class="gui-label" id="checkin_label"> <span class="gui-label__text">Check-in</span> </span> <div class="gui-widget"> <gui-date id="checkin_date" class="gui-field"> <div class="gui-widget"> <div role="group" id="checkin" class="gui-widget-input gui-parts gui-parts-ring gui-date-input" aria-labelledby="checkin_label" > <!-- The month, day and year parts, as in gui-date --> </div> </div> </gui-date> <button type="button" class="gui-date-picker__arrow" aria-haspopup="dialog" aria-label="Show calendar" aria-expanded="true" aria-controls="checkin_popup" > <span class="gui-caret" aria-hidden="true"></span> </button> <div class="gui-picker__panel"> <gui-calendar role="dialog" id="checkin_popup" aria-labelledby="checkin_label" class="gui-field" > <!-- The month buttons, header and days grid, as in gui-calendar --> </gui-calendar> </div> </div> <div class="gui-validator" id="checkin_errors" role="alert"></div></gui-date-picker>| Class | Part |
|---|---|
gui-label | The label, with the text and the hint. |
gui-label__text | The label text and the required marker. |
gui-widget-hint | The hint. |
gui-widget | The box around the field, the button and the panel. |
gui-date-picker__arrow | The button that opens and closes the calendar. Its caret turns while the calendar is open. |
gui-caret | The caret in the button. See Carets. |
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 input page and the calendar’s on the Calendar page.
Tokens
Section titled “Tokens”The field reads the tokens of the Date input, and the calendar those of the Calendar. The picker adds its panel:
| Token | What it styles |
|---|---|
--gui-bg-default | The panel’s background. |
--gui-border-default | The panel’s border. |
--gui-radius-md | The panel’s corner radius. |
--gui-shadow-md | The panel’s shadow. |
--gui-space-2 | The panel’s padding. |
--gui-space-3 | The space between the button and the edge. |
--gui-intent-error | The panel’s border while there are errors. |
/* Bigger days in every date picker. */gui-date-picker { --gui-calendar-day-button-size: 44px;}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 calendar 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 month buttons, year button, year grid and days are named as on the Calendar; the picker takes the same*-aria-labelprops and passes them on. - Parts. The field’s parts are named
Day,MonthandYear, as on the Date input. - Errors. While errors show, the field has
aria-invalid="true"and points at them witharia-errormessage.
| Key | Action |
|---|---|
| Tab | Moves from the field to the button, then through the calendar while it is open, then out. |
| Enter / Space on the button | Opens the calendar and focuses today or the selected day, or closes it. |
| Escape | Closes the calendar and returns focus to the field. |
| Enter / Space on a day | Picks the day, closes the calendar and returns focus to the field. |
| Arrow keys in the calendar | Move between the days, as on the Calendar. |
| Page Up / Page Down in the calendar | Move to the same day in the previous or next month (the last day of a shorter month), or the nearest day the user can pick. With Shift, the same day in the previous or next year. |
| Home / End in the calendar | Move to the first or last day of the week the user can pick, within the month. |
| Keys in the field | Type and move between the parts, as on the Date input. |
What every element shares, such as translating the browser messages, is in Accessibility.