Calendar
A month calendar on the page for choosing one date where the choice is the point of the page: a booking day, a delivery day, an appointment. The chosen date submits as YYYY-MM-DD under name. For a date entered in a form field, typed as often as picked, use aihio-date-picker, which opens the same calendar under a field.
Intents
form-field
date-entry
selection
Examples
Each one rendered live, over the markup that produces it.
A booking day
In a field, named by its label. Days outside min and max are struck through.
<form>
<aihio-field>
<span slot="label">Delivery day</span>
<aihio-calendar name="delivery-day" min="2026-10-12" max="2026-11-27"></aihio-calendar>
<span slot="description">Deliveries run until 27 November.</span>
</aihio-field>
</form>
A chosen date, read only
readonly keeps the months browsable and the date fixed.
<aihio-calendar aria-label="Contract start" value="2026-11-02" readonly></aihio-calendar>
Weeks from Monday
first-day-of-week overrides the language's own first day.
<aihio-calendar aria-label="Week starting" first-day-of-week="mon" lang="en-US"></aihio-calendar>
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.
The heading is not associated with the calendar, so its group is unnamed.
Point aria-labelledby at the heading, or put the calendar in aihio-field.
Don't
<h2>Choose a day</h2>
<aihio-calendar name="delivery-day"></aihio-calendar>
aihio lint reports
-
calendar-labelerror
Do
Choose a day
<h2 id="delivery-day-heading">Choose a day</h2>
<aihio-calendar name="delivery-day" aria-labelledby="delivery-day-heading"></aihio-calendar>
Dates in attributes are YYYY-MM-DD whatever the page's language.
The calendar shows the date the page's way.
Don't
<aihio-calendar aria-label="Delivery day" value="9.10.2026"></aihio-calendar>
aihio lint reports
-
date-valueerror
Do
<aihio-calendar aria-label="Delivery day" value="2026-10-09"></aihio-calendar>
The earliest date is min.
Nothing reads min-date, so every day stays open.
Don't
<aihio-calendar aria-label="Delivery day" min-date="2026-10-12"></aihio-calendar>
aihio lint reports
-
unknown-attributeerror Suggestsmin
Do
<aihio-calendar aria-label="Delivery day" min="2026-10-12"></aihio-calendar>
API
Every attribute, property, method, and event the schema declares for aihio-calendar.
Attributes
value- string
- The initially chosen date as YYYY-MM-DD, whatever the page's language, and the date a form reset returns to. The calendar opens on its month. The value property carries the live choice.
name- string
- Form field name. The chosen date submits under it as YYYY-MM-DD. Without it the calendar submits nothing.
form- string
- Id of an external form that owns the calendar.
min- string
- The earliest date that can be chosen, as YYYY-MM-DD. Earlier days are struck through, and the months and years offered stop at it.
max- string
- The latest date that can be chosen, as YYYY-MM-DD.
disabled-
boolean
default
false - Shows the calendar dimmed, out of the tab order, and submits nothing.
readonly-
boolean
default
false - Shows the chosen date and lets the months be browsed, without letting the date change. The date still submits.
first-day-of-week-
one of
montuewedthufrisatsun - The day the 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.
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.
Properties
value-
string - Get or set the chosen date as YYYY-MM-DD, or "" for none. Anything that is not a date clears it. Setting it shows its month and 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 chosen 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 shown, as YYYY-MM-DD. A date it returns true for is struck through and cannot be chosen. Set it again when the dates it rules out change. Read a weekday as new Date(date).getUTCDay().
month-
string - Get or set the month shown, as YYYY-MM. Setting it shows that month without choosing a date, and fires
aihio-month. form-
HTMLFormElement | nullread-only - The owning form, or null outside one
Methods
focus(options?: FocusOptions): void- Moves focus to the grid's tab stop: the chosen date, or else today.
Events
aihio-change-
detail
{ value: string } - Fired when the person chooses a different date, or when a form reset changes it. Not fired for programmatic changes.
aihio-month-
detail
{ month: string, start: string, end: string } - Fired when the calendar 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. The month property has the one shown first.
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 calendar is not inside anaihio-fieldName it after what the date is for, with aria-labelledby pointing at visible text or aria-label. Without a name, its group says nothing about the date it holds.
Checked as
calendar-label -
warn When the calendar is inside a<form>and its date should be submittedSet name. A calendar without a name is omitted from FormData.
Checked as
calendar-form-name
Handled for you
- The calendar is a group, named by its label, holding the month controls and a
<table role="grid">named by the month it shows - The grid is one tab stop, on the chosen date or today. 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 chooses 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, and the buttons are never disabled under focus
- 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 in the page's language (the closest lang). Where lang names no region (en), the browser's region for that language fills it in
- The grid is the same height every month, so the controls above it stay under the pointer
- readonly sets aria-readonly and leaves the months browsable; disabled takes the grid out of the tab order
- The chosen date submits as YYYY-MM-DD through a hidden input under name; form reset,
<fieldset disabled>, and the form attribute behave natively - Forced colours restate the chosen, today, and unavailable days with system colours