Date Picker
A form field for one date (a due date, a birth date, a travel date), typed in the page's language or picked from a month calendar that a button opens under the field. The date submits as YYYY-MM-DD, as a native date input's does. For a date chosen on the page itself, such as a booking day, use aihio-calendar.
Intents
form-field
date-entry
text-entry
Variants
Every value of the attributes that change how it looks. The values come from the schema.
size
Field size, matching aihio-input.
size="sm"size="md"size="lg"Examples
Each one rendered live, over the markup that produces it.
In a field
The placeholder shows the date format in the page's language, and the date submits as YYYY-MM-DD.
<aihio-field>
<label slot="label">Due date</label>
<aihio-date-picker name="due-date" value="2026-10-30"></aihio-date-picker>
<span slot="description">Invoices are sent on this day.</span>
</aihio-field>
A range of dates
Days outside min and max are struck through in the calendar, and the months and years it offers stop at them.
<aihio-field>
<label slot="label">Appointment</label>
<aihio-date-picker name="appointment" min="2026-10-12" max="2026-12-18" required></aihio-date-picker>
</aihio-field>
A birth date
Most people type it. autocomplete="bday" lets the browser fill it in, and the year select reaches back a century.
<aihio-field>
<label slot="label">Date of birth</label>
<aihio-date-picker name="birth-date" autocomplete="bday" max="2026-10-09"></aihio-date-picker>
</aihio-field>
In another language
lang sets how dates are written and read, the names of months and weekdays, and the first day of the week. The text attributes put the rest in the same language.
<aihio-field lang="fi">
<label slot="label">Eräpäivä</label>
<aihio-date-picker name="due-date" choose-text="Valitse päivä" previous-month-text="Edellinen kuukausi" next-month-text="Seuraava kuukausi" invalid-text="Kirjoita päivä muodossa {format}." min-text="Valitse {min} tai myöhemmin." max-text="Valitse {max} tai aiemmin." unavailable-text="{date} ei ole valittavissa."></aihio-date-picker>
</aihio-field>
Mistakes
Each one beside its fix, with what aihio lint says about it. The prompt fragment gives agents the
same pairs, and the build fails if the linter stops catching one or a fix stops passing.
Placeholder is not a label, and this one hides the date format the field would show there.
Put the picker in aihio-field with a label.
Don't
<aihio-date-picker placeholder="Due date"></aihio-date-picker>
aihio lint reports
-
date-picker-labelerror
Do
<aihio-field>
<label slot="label">Due date</label>
<aihio-date-picker name="due-date"></aihio-date-picker>
</aihio-field>
A wrapping <label> takes its name from all of its text, so the open calendar's month, weekdays, and days become part of the field's name.
Use aihio-field instead.
Don't
<label>
Due date
<aihio-date-picker name="due-date"></aihio-date-picker>
</label>
aihio lint reports
-
date-picker-labelerror
Do
<aihio-field>
<label slot="label">Due date</label>
<aihio-date-picker name="due-date"></aihio-date-picker>
</aihio-field>
Dates in attributes are YYYY-MM-DD whatever the page's language: 10/09/2026 is 9 October in Finland and 10 September in the US.
The field shows the date the page's way.
Don't
<aihio-date-picker aria-label="Due date" value="10/09/2026"></aihio-date-picker>
aihio lint reports
-
date-valueerror
Do
<aihio-date-picker aria-label="Due date" value="2026-10-09"></aihio-date-picker>
min is after max, so there is no date to choose.
Don't
<aihio-date-picker aria-label="Check-in" min="2026-12-31" max="2026-01-01"></aihio-date-picker>
aihio lint reports
-
date-rangeerror
Do
<aihio-date-picker aria-label="Check-in" min="2026-01-01" max="2026-12-31"></aihio-date-picker>
Nothing reads format or locale.
The field writes and reads dates the way the page's language does: set lang, on the page or the picker.
Don't
<aihio-date-picker aria-label="Start date" format="dd.MM.yyyy" locale="fi"></aihio-date-picker>
aihio lint reports
-
unknown-attributeerror Suggestsform -
unknown-attributeerror
Do
<aihio-date-picker aria-label="Alkupäivä" lang="fi"></aihio-date-picker>
API
Every attribute, property, method, and event the schema declares for aihio-date-picker.
Attributes
value- string
- The initial date as YYYY-MM-DD, whatever the page's language, and the date a form reset returns to. The value property carries the live date.
name- string
- Form field name. The date submits under it as YYYY-MM-DD, not as the text on show. Without it the field submits nothing.
form- string
- Id of an external form that owns the field.
min- string
- The earliest date that can be chosen, as YYYY-MM-DD. Earlier days are struck through in the calendar, and a typed earlier date makes the field invalid.
max- string
- The latest date that can be chosen, as YYYY-MM-DD.
placeholder- string
- Placeholder text. Defaults to the date format in the page's language, such as mm/dd/yyyy or pp.kk.vvvv. It is not a label.
disabled-
boolean
default
false - Disables the field and its calendar. A disabled field submits nothing.
readonly-
boolean
default
false - Shows the date without letting it change. The date still submits.
required-
boolean
default
false - Requires a date for native constraint validation.
error-
boolean
default
false - Shows error styling and sets aria-invalid.
aihio-fieldsets this from its error slot. size-
one of
smmdlgdefaultmd - Field size, matching
aihio-input. open-
boolean
default
false - Reflects whether the calendar is showing. Set or remove it to open or close the calendar.
autocomplete-
string
default
off - Forwarded to the text field. Off by default, so the browser's list of past entries does not cover the calendar; bday lets the browser fill in a birth date.
first-day-of-week-
one of
montuewedthufrisatsun - The day the calendar's weeks start on. Defaults to the page's language and region: Monday in Finland, Sunday in the US, Saturday in Egypt. Where lang names no region (en), the browser's region for that language fills it in.
choose-text-
string
default
Choose date - The calendar button's name and the calendar's, each followed by the field's label. For localisation.
previous-month-text-
string
default
Previous month - The previous month button's name. For localisation.
next-month-text-
string
default
Next month - The next month button's name. For localisation.
invalid-text-
string
default
Enter a date as {format}. - Validation message for text that is not a date, with {format} for the date format (dd.mm.yyyy). For localisation.
min-text-
string
default
Choose {min} or later. - Validation message for a date before min, with {min} for it. For localisation.
max-text-
string
default
Choose {max} or earlier. - Validation message for a date after max, with {max} for it. For localisation.
unavailable-text-
string
default
{date} is not available. - Validation message for a date isDateDisabled rules out, with {date} for it. For localisation.
Properties
value-
string - Get or set the date as YYYY-MM-DD, or "" for none. Anything that is not a date clears it, as with a native date input. Setting it does not fire
aihio-change. defaultValue-
string - Get or set the reset value reflected by the value attribute
valueAsDate-
Date | null - Get or set the date as the UTC midnight it starts at, as a native date input's valueAsDate is.
isDateDisabled-
((date: string) => boolean) | null - Called with each date the calendar shows, as YYYY-MM-DD. A date it returns true for is struck through and cannot be chosen, and typing it makes the field invalid. Set it again when the dates it rules out change. Read a weekday as new Date(date).getUTCDay().
control-
HTMLInputElement | nullread-only - The native text field the date is typed into
form-
HTMLFormElement | nullread-only - The owning form, or null outside one
validity-
ValidityState | nullread-only - Native constraint validation state, including the field's own checks: text that is not a date, a date outside min and max, and one isDateDisabled rules out
validationMessage-
stringread-only - Native validation message
willValidate-
booleanread-only - Whether the field participates in constraint validation
Methods
open(): void- Opens the calendar and moves focus into it, on the chosen date or today. Focus goes back to whatever had it when the calendar closes.
close(): void- Closes the calendar.
toggle(): void- Opens or closes the calendar.
focus(options?: FocusOptions): void- Moves focus to the text field.
checkValidity(): boolean- Runs native constraint validation and returns whether the field is valid.
reportValidity(): boolean- Runs native constraint validation and shows the browser message if invalid.
setCustomValidity(message: string): void- Sets a custom validation message, which wins over the field's own. An empty message hands validation back to the field.
Events
aihio-change-
detail
{ value: string } - Fired when the person commits a different date: picks one, or leaves the field (or presses Enter) after typing. Text that is not a date commits as "". Also fired when a form reset changes the date, and not for programmatic changes. The native input and change events come from the text field and carry its text.
aihio-month-
detail
{ month: string, start: string, end: string } - Fired when the calendar opens, and whenever it shows another month: the month as YYYY-MM, and its first and last day. Fetch the dates to rule out for that month here, then set isDateDisabled again.
aihio-open- Fired after the calendar opens.
aihio-close-
detail
{ reason: string } - Fired after the calendar closes, with why: select, escape, outside, blur, toggle, reset, or api.
Composition
- Allowed children
- none
Accessibility
What you have to provide, and what the component already does. The obligations with a rule are checked by
aihio lint and by the dev build's console warnings.
Your obligations
-
error When the date picker is not inside anaihio-fieldLabel it with aria-labelledby pointing at visible text, or aria-label. Placeholder is not a label, and a wrapping
<label>would fold the open calendar's text into the field's name.Checked as
date-picker-label -
warn When the date picker is inside a<form>and its date should be submittedSet name. A date picker without a name is omitted from FormData.
Checked as
date-picker-form-name
Handled for you
- A real text
<input>to type into, and a real<button>witharia-haspopup="dialog", aria-expanded, and aria-controls that opens the calendar. The button is named "Choose date" followed by the field's label, and described by the chosen date in words - Typed dates are read in the page's language (the closest lang): 9.10.2026 in Finland, 10/9/2026 in the US, and 2026-10-09 anywhere, with any separator, a month name ("9 Oct 2026", "9. lokakuuta"), two-digit years, no year (this year), the language's own digits, or no separators at all (09102026). Where lang names no region (en), the browser's region for that language fills it in, so a browser set to en-GB reads 9/10/2026 as 9 October; a region lang names is kept. The placeholder is the format, written the language's way (mm/dd/yyyy, pp.kk.vvvv, tt.mm.jjjj)
- Text that is not a date is kept to be corrected, not thrown away, and the field reports it through native validation; so does a date outside min and max or one isDateDisabled rules out, with a localisable message
- The calendar is a dialog named "Choose date" and the field's label. Focus moves into its grid on the chosen date, or today, and goes back to the button (or the field, when Alt+ArrowDown opened it) when a date is picked or Escape closes it. Focus leaving the field closes it
- The grid is a
<table role="grid">with one tab stop. Arrow keys move a day or a week (mirrored right to left), Home and End go to the ends of the week, Page Up and Page Down move a month and, with Shift, a year; Enter or Space picks the day - Each day is named by its full date ("Friday, October 9, 2026") and carries aria-selected for the chosen date,
aria-current="date"for today, and aria-disabled for a day outside min and max or ruled out by isDateDisabled, which is struck through, not only greyed - Previous and next month buttons and native month and year selects head the grid; a step is announced in a polite status region. Weeks start on the first day of the language's region, the Gregorian calendar is used whatever the language's own, and month and weekday names come from Intl
- Escape on a closed field is left alone, so a surrounding dialog still closes. Keys pressed while an IME is composing are left to the IME
- The date submits as YYYY-MM-DD through a hidden input under name; required, form reset,
<fieldset disabled>, and the form attribute behave natively - The calendar renders in the top layer, flips above the field when there is more room there, and stays inside the visual viewport; on touch screens its days are larger
- Forced colours restate the chosen, today, and unavailable days with system colours; reduced motion removes the open animation