Accessibility
The date picker follows the WAI-ARIA Date Picker Dialog pattern — a text field paired with a calendar the user opens in a dialog — and meets WCAG 2.1 AA. An accessible date picker keeps typed entry available alongside the calendar, moves and returns focus predictably as the dialog opens and closes, announces the month as it changes, and gives every element an accessible name — so the chosen date is clear whether the user sees it, hears it, or tabs through it.
For designers
Section titled “For designers”- Keep the label visible and outside the field in every state, and keep the format hint (for example,
DD/MM/YYYY) in helper text at all times — never let the placeholder carry the label or the format. - The field, its text, the helper and error text, the calendar trigger, and every day cell must maintain accessible contrast on every supported surface and in every state. Source: WCAG 1.4.3 Contrast (Minimum).
- The selected, today, error, and disabled states must never be conveyed by colour alone — pair each with an icon, a shape, or text. Source: WCAG 1.4.1 Use of Color.
- Focus styles must stay visible across the field, trigger, month controls, and day cells, meet contrast requirements on every supported surface, and be clearly distinguishable from hover — including high-contrast mode. Source: WCAG 2.4.7 Focus Visible.
- Use semantic colour tokens (e.g.
foreground/neutral/default) rather than fixed values, so the component adapts correctly across themes and surfaces.
For developers
Section titled “For developers”The date picker combines a native text field with a Dialog that holds a calendar grid. Typed entry must always stay available — users must be able to complete the task without traversing day-by-day cells. Opening the dialog moves focus into the calendar, focus is trapped inside the dialog while it is open, and closing it (selection, Escape, or outside click) returns focus to the trigger; that traversal is shown in Focus order below and the keys that drive it are in the Keyboard interaction table. Interactive targets must be at least 44 px high for comfortable touch use — comfortably clearing the WCAG AA minimum. Source: WCAG 2.5.8 Target Size (Minimum). The per-element roles, names, and states — the field, trigger, dialog, grid, and day cells — are in the Labelling elements section below.
Keyboard interaction
Section titled “Keyboard interaction”| Key | Action |
|---|---|
| Enter / Space | Open the picker from the trigger, or select the focused day |
| Arrow keys | Move focus between days in the grid; ↓ also opens the picker from the field |
| Home / End | Move focus to the first / last day of the grid |
| Escape | Close the picker and return focus to the trigger |
| Tab / Shift + Tab | Move through the field, trigger, and the controls inside the open picker |
Focus order
Section titled “Focus order”Focus reaches the field and its trigger; opening the picker moves focus into the dialog, where Tab walks the month controls and the calendar grid, which is a single stop — a roving tabindex moves focus between days while the arrow keys rove within it. Closing the picker returns focus to the trigger. The keys that drive this are in the Keyboard interaction table above.
March 2026
- Date field
- Calendar trigger
- Previous month
- Next month
- Calendar grid
Source: WCAG 2.4.3 Focus Order.
Labelling elements
Section titled “Labelling elements”Give every element the role, name, and state assistive technology needs.
Date field
A native <input type="text"> associated with its <label> through for/id. Link the format hint and any error text with aria-describedby, and announce error changes with aria-live="polite". Set inputmode="numeric" so mobile surfaces the numeric keyboard.
Trigger
The calendar <button> carries aria-haspopup="dialog", aria-expanded kept in sync with the open state, and aria-controls pointing to the dialog. Give it an accessible name with aria-label (e.g. "Choose date"); its icon is aria-hidden="true".
Dialog
The popup surface takes role="dialog" and is named with aria-label or aria-labelledby. It is non-modal (aria-modal="false") while still trapping and returning focus, so the underlying page stays reachable.
Calendar grid
role="grid" with aria-labelledby pointing to the month/year heading. Weekday labels are role="columnheader" and each week is a role="row", so screen readers announce the day’s column and position.
Month & year heading
The visible <h2> names the grid via aria-labelledby and carries aria-live="polite", so the new month is announced when the user steps between months.
Month navigation buttons
Each icon-only <button> takes its accessible name from aria-label ("Previous month" / "Next month"), with the chevron marked aria-hidden="true".
Day cell
A role="gridcell" wrapper around a native <button>. Expose the full date as the button’s accessible name (aria-label), set aria-selected="true" on the chosen date and aria-current="date" on today, and keep unavailable days as native disabled so they stay visible but out of the tab sequence.
Source: WCAG 4.1.2 Name, Role, Value.