Date Picker
import { DatePicker } from '@absoluteui/components/date-picker';import '@absoluteui/components/date-input/style.css';Using Angular? See Date Picker for Angular.
What it does
Section titled “What it does”A date field with a calendar popup (WAI-ARIA APG date picker dialog, non-modal variant). DatePicker composes a Date Input (date granularity) with a calendar button inside the field boundary; the button opens a Calendar in a role="dialog" popup in the top layer. Typing and picking produce the same PlainDate value. The picker, not its inner Date Input, is the form control that Form discovers.
When to use
Section titled “When to use”- Dates users may either type or look up by day of week (appointments, travel, deadlines).
- Forms that need a date field with min/max bounds and unavailable days.
When not to use
Section titled “When not to use”- Well-known dates such as birth dates: use Date Input (a calendar makes distant years slow to reach).
- An always-visible calendar: use Calendar.
- Exact moments with a time of day: use DateTime Picker.
- Date ranges: use Date Range Picker.
Examples
Section titled “Examples”Required date with bounds
Section titled “Required date with bounds”import { plainDate, plainDateToString } from '@absoluteui/core';import { DatePicker, type DatePickerChangeDetail } from '@absoluteui/components/date-picker';
const host = document.createElement('div');document.body.append(host);DatePicker.create(host, { label: 'Check-in', name: 'checkIn', required: true, min: plainDate(2026, 1, 1), max: plainDate(2026, 12, 31) });host.addEventListener('aui-change', (event) => { const { value, validity } = (event as CustomEvent<DatePickerChangeDetail>).detail; host.dataset.value = value && validity.valid ? plainDateToString(value) : '';});Unavailable days and a Monday week start
Section titled “Unavailable days and a Monday week start”import { dayOfWeek } from '@absoluteui/core';import { DatePicker } from '@absoluteui/components/date-picker';
const host = document.createElement('div');document.body.append(host);DatePicker.create(host, { label: 'Delivery date', firstDayOfWeek: 1, isDateDisabled: (date) => dayOfWeek(date) === 7 });Keep the calendar open after picking
Section titled “Keep the calendar open after picking”import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ common: { timezone: 'Europe/London' }, components: { datePicker: { closeOnSelect: false } } });import { dayOfWeek } from '@absoluteui/core';import { DatePicker } from '@absoluteui/components/date-picker';import '@absoluteui/components/date-input/style.css';import '@absoluteui/components/calendar/style.css';import '@absoluteui/components/date-picker/style.css';
const host = document.querySelector<HTMLElement>('#delivery');if (host) { const picker = DatePicker.create(host, { label: 'Delivery date', name: 'delivery', isDateDisabled: (d) => dayOfWeek(d) === 7 }); host.addEventListener('aui-change', () => { if (!picker.validity.valid) picker.reportValidity(); });}More usage
Section titled “More usage”import { plainDate } from '@absoluteui/core';import { DatePicker } from '@absoluteui/components/date-picker';
const host = document.createElement('div');document.body.append(host);DatePicker.create(host, { label: 'Check-in', name: 'checkIn', required: true, min: plainDate(2026, 1, 1) });Anatomy
Section titled “Anatomy”<div class="aui-date-picker" data-aui-control> (consumer host; registered with Form) <div class="aui-date-picker__field aui-date-input"> (inner Date Input host; not registered) <div class="aui-date-input__label"> <div class="aui-date-input__group" role="group"> <div class="aui-date-input__fields"> segments ... </div> <div class="aui-date-picker__trailing"> (Date Input `trailing` option) <button class="aui-date-picker__button" aria-haspopup="dialog" aria-expanded aria-controls aria-label="Choose date"> <div class="aui-date-picker__popup" role="dialog" aria-modal="false" aria-label="Choose date" popover="manual"> <div class="aui-calendar"> ... </div> </div> </div> </div> <div class="aui-date-input__description"> <div class="aui-date-input__hint"> <div class="aui-date-input__error"> <input type="hidden" name> </div></div>The popup sits inside the field group in the DOM (it renders in the top layer), so moving focus between the segments, the button, and the calendar does not mark the field touched, and a picker inside a modal Dialog stays inside the dialog’s subtree.
API reference
Section titled “API reference”Factory
Section titled “Factory”DatePicker.create(host: HTMLElement, options: DatePickerOptions): DatePickerGlobal configuration key: datePicker (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Field label and accessible name of the segment group. Required. | |
value |
PlainDate | null |
null |
Initial value; also the value restored by reset(). |
min |
PlainDate | null |
Inclusive lower bound (validation and calendar). | |
max |
PlainDate | null |
Inclusive upper bound (validation and calendar). | |
name |
string |
Form field name for the ISO 8601 hidden input. | |
description |
string |
Help text referenced by aria-describedby. | |
required |
boolean |
false |
Empty value is invalid (value-missing). |
disabled |
boolean |
false |
Disables the segments and the calendar button. |
readOnly |
boolean |
false |
Prevents editing; the calendar button is disabled. |
isDateDisabled |
(date: PlainDate) => boolean |
Disable days in the calendar; a typed disabled day is invalid (‘This date is not available.’). | |
businessCalendar |
BusinessCalendarEngine | null |
Business calendar (since 0.9.0): non-working days and holidays are marked and named in the calendar. | |
disableNonWorking |
boolean |
"false" |
With businessCalendar: non-working days cannot be picked or typed (since 0.9.0). |
firstDayOfWeek |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
First calendar column (ISO weekday). Default: the locale’s first day of week. | |
icons |
Partial<{ calendar: IconRef }> |
Icon slot overrides. | |
formControl |
boolean |
true |
Advanced, read at creation: false skips registration with Form, for composite controls such as Schedule Picker that register themselves (since 0.6.0). Set at creation only. |
closeOnSelect |
boolean |
true |
Close the popup after a day is picked. Globally configurable. |
locale |
string |
"en-US" |
Segment order, calendar, messages, formatting; forwarded to both children. Globally configurable. Inherited from common configuration. |
timezone |
string (IANA) |
"UTC" |
Zone used only to determine today in the calendar. Globally configurable. Inherited from common configuration. |
direction |
'ltr' | 'rtl' |
"ltr" |
Arrow-key mirroring, calendar direction, and popup alignment. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
host |
HTMLElement |
Consumer host; its children are owned by the component. Registered with Form. |
input |
DateInput |
The inner Date Input (date granularity); not registered with Form. |
calendar |
Calendar |
The Calendar inside the popup. |
button |
HTMLButtonElement |
Calendar button inside the field boundary. |
popup |
HTMLDivElement |
role=dialog aria-modal=false popup (popover=manual). |
value |
PlainDate | null |
Current valid value, or null when empty or invalid. |
validity |
DateInputValidity |
{ valid, error, message, localTimeStatus }. |
isOpen |
boolean |
Whether the popup is open. |
name |
string | null |
Form field name. |
label |
string |
Label text. |
disabled |
boolean |
Disabled state. |
dirty |
boolean |
Value differs from the initial value. |
touched |
boolean |
Focus has left the field (segments, button, and popup) at least once. |
options |
Readonly<DatePickerOptions> |
Current instance options. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
open(): void |
Open the calendar and focus the selected day (or today). No-op when disabled or read-only. |
close(): void |
Close the calendar; focus inside the popup returns to the calendar button. |
setValue(value: PlainDate | null): void |
Programmatic value (no aui-change). |
update(options: Partial<DatePickerOptions>): void |
Merge and apply options to the field and calendar. |
reportValidity(): boolean |
Show messages now and return validity. |
setCustomError(message: string | null): void |
External error (server, cross-field rule); cleared when the user changes the value. |
getFormValue(): string | null |
ISO 8601 YYYY-MM-DD, or null when empty or invalid. |
focus(): void |
Focus the first date segment. |
reset(): void |
Close, restore the initial value, and clear touched/submitted state (no aui-change). |
destroy(): void |
Close and remove rendering, listeners, registration, and subscriptions of the picker and its children. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-change |
{ value: PlainDate | null; validity: DateInputValidity } |
Yes | No | User typing or picking changed the value or validity. Dispatched from the picker host. |
aui-navigate |
{ month: { year: number; month: number } } |
Yes | No | Bubbles from the inner Calendar when the user changes the visible month. |
aui-open |
null |
No | No | The popup opened. |
aui-close |
null |
No | No | The popup closed. |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-date-picker |
Host; –disabled modifier. |
aui-date-picker__field |
Host of the inner Date Input (aui-date-input classes apply inside). |
aui-date-picker__trailing |
Button and popup container inside the Date Input group. |
aui-date-picker__button |
Calendar button. |
aui-date-picker__popup |
Top-layer dialog popup containing the Calendar (aui-calendar classes apply inside). |
Icon slots
Section titled “Icon slots”| Slot | Default icon |
|---|---|
calendar |
calendar |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-color-border | --aui-color-focus-ring | --aui-color-hover | --aui-color-surface | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-control-height-md | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-icon-size-sm | --aui-radius-md | --aui-shadow-md | --aui-space-2 | --aui-space-3
TypeScript definitions
Section titled “TypeScript definitions”interface DatePickerConfig { closeOnSelect: boolean }interface DatePickerIcons { calendar: IconRef }interface DatePickerChangeDetail { value: PlainDate | null; validity: DateInputValidity }type Weekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;Accessibility
Section titled “Accessibility”Pattern: WAI-ARIA APG Date Picker Dialog, non-modal variant: a labelled segmented date field (Date Input), a button with aria-haspopup=dialog, and a role=dialog popup containing the Calendar grid.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Enter / Space | Calendar button | Open (or close) the calendar; focus moves to the selected day, else today. |
| Alt+ArrowDown | Date segment focused | Open the calendar (the segment value does not change). |
| Calendar keys | Day focused | Arrows, Home/End, PageUp/PageDown, Shift+PageUp/PageDown as in Calendar. |
| Enter / Space | Day focused | Pick the day, close the popup (closeOnSelect), and focus the calendar button. |
| Escape | Popup open | Close the popup and return focus to the calendar button. Inside a Dialog, only the popup closes. |
| Tab / Shift+Tab | Popup open | Moves through month buttons and the day; leaving the popup (other than to the calendar button) closes it. |
| Date Input keys | Segment focused | As in Date Input. |
| Element | Role |
|---|---|
| segment group | group (Date Input) |
| calendar button | button (native), aria-haspopup=dialog |
| popup | dialog (aria-modal=false) |
| calendar | grid (Calendar) |
| icons | none (aria-hidden=true) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-label |
calendar button | “Choose date”, or “Change date, <long date>” when a date is set |
aria-expanded |
calendar button | “true” while the popup is open |
aria-controls |
calendar button | Popup id |
aria-label |
popup | Localized “Choose date” |
aria-modal |
popup | “false” (non-modal; the page stays reachable) |
Opening moves focus into the grid; closing with focus inside the popup returns it to the calendar button. Outside pointer presses close the popup. Moving focus between the segments, button, and popup does not mark the field touched.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- The segments remain the primary input; the calendar is an alternative (APG).
- The popup is non-modal: focus is not trapped, and the rest of the page stays reachable.
- A typed day rejected by isDateDisabled produces a localized validation message, so the typed and picked paths agree.
- Screen-reader behavior of the popup (dialog announcement, month changes) has not yet been manually verified with NVDA, JAWS, and VoiceOver.
Behavior
Section titled “Behavior”States
Section titled “States”empty, partial (incomplete), valid, invalid (Date Input errors, unavailable day, custom), open, disabled, read-only.
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| closed | button, or Alt+ArrowDown on a segment | open; focus on the selected day or today; aui-open |
| open | pick a day | value written; aui-change if changed; closed and focus on the button (closeOnSelect) |
| open | Escape | closed; focus on the button; aui-close |
| open | pointer outside, or Tab out of the popup | closed |
| any | user types | aui-change; the calendar follows the typed date while open |
| any | reset() |
closed, initial value, pristine, untouched |
Accessibility notes
Section titled “Accessibility notes”- The segments stay the primary input; the calendar is an alternative.
- The popup is non-modal: focus is not trapped and the page stays reachable. Escape and outside presses close it through the shared overlay stack, so only the topmost overlay responds.
- A typed day rejected by
isDateDisabledgets a localized message, so the typed and picked paths agree. - Manual screen-reader verification is pending.
Responsive behavior
Section titled “Responsive behavior”The field fits its content like Date Input. The popup is positioned under the field (bottom-start), flips above when there is no room, and scrolls when taller than the available height (--aui-available-height).
Right-to-left
Section titled “Right-to-left”Segment order follows the locale; the button sits at the inline end of the field; the popup aligns to the field’s inline start; the calendar mirrors arrows and chevrons.
Theming
Section titled “Theming”Role tokens only; verified in the light, dark, and high-contrast themes. In forced-colors mode the popup has a CanvasText border.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ common: { timezone: 'Europe/London' }, components: { datePicker: { closeOnSelect: false } } });Changes apply to existing instances immediately. Configuration changes that do not change locale, timezone, or direction do not rebuild the field, so partially typed input is kept.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options are dynamic.
Timezones
Section titled “Timezones”Values are PlainDate and never shift. The configured timezone (default UTC, never the host zone) only determines “today” in the calendar, which is focused when the field is empty.
icons.calendar (default calendar).
Security
Section titled “Security”All text uses textContent and Intl formatting; no HTML parsing. The popup uses the Popover API; no inline styles or handlers. CSP-safe.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry. When name is set, the inner Date Input’s hidden input carries YYYY-MM-DD.
Performance
Section titled “Performance”One Date Input and one Calendar per picker, created once; opening re-renders only the calendar month. Budget: under 10 ms average per DatePicker.create across 100 instances.
Supported data scale
Section titled “Supported data scale”100 pickers per page.
Edge cases
Section titled “Edge cases”- Opening an empty field focuses today in the configured zone: at 2026-03-15 10:30 UTC with
timezone: 'Pacific/Kiritimati', 16 March. - Re-picking the current value closes the popup without
aui-change. - Picking a date while the segments hold an incomplete entry replaces it and emits
aui-change. - Inside a modal Dialog, the first Escape closes the popup and the second closes the dialog.
- Opening from an empty required field does not show “This field is required.” until focus leaves the whole field.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
DATE_PICKER_OPTIONS |
configuration | Missing label, or value/min/max not a PlainDate. |
CALENDAR_OPTIONS |
configuration | min > max, firstDayOfWeek outside 1-7, or a non-function isDateDisabled. |
CFG_* |
configuration | Invalid locale, timezone, direction, or closeOnSelect. |
Validation (validity.error) |
validation | Date Input errors (incomplete, invalid-date, value-missing, range-underflow, range-overflow) and custom (consumer error, or “This date is not available.”). |
A failed construction leaves no rendering, listeners, registrations, or subscriptions behind.
Known limitations
Section titled “Known limitations”- Gregorian calendar only; years 1 to 9999; single dates only (for ranges use Date Range Picker).
- Validation messages follow Date Input timing;
validationTimingis not supported. - A typed unavailable day is reported as
custom; a consumer custom error takes precedence over it. - No Cancel/OK buttons: picking commits.
- Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
Unsupported scenarios
Section titled “Unsupported scenarios”- Minute granularity.
- HTML in the label, help text, or messages.