Forms & Controls
Accessible Date Picker
A dialog-based grid pattern for choosing a date, paired with a text input.
Last verified against WCAG 2.2 and WAI-ARIA APG 1.2 on 2026-07-02.
Implementation
A dialog containing an ARIA grid, with the same focus-trap/Escape/focus-restore logic as the flagship Dialog pattern, plus arrow-key date navigation.
<button id="date-trigger" aria-haspopup="dialog" aria-expanded="false">
Choose date
</button>
<!-- The calendar popup IS a dialog. Inside, a role="grid" holds the day
buttons: one owns tabindex="0" (roving), the selected day carries
aria-selected, and each has a full aria-label so it reads as a date,
not just a bare number. -->
<div id="date-dialog" role="dialog" aria-modal="true"
aria-label="Choose date, July 2026" hidden>
<div role="grid">
<div role="row">
<button role="gridcell" tabindex="0" aria-label="Wednesday, 1 July 2026">1</button>
<button role="gridcell" tabindex="-1" aria-label="Thursday, 2 July 2026">2</button>
<!-- … the rest of the week / month … -->
</div>
</div>
</div>Required roles, states & properties
| Element | Attribute | Why |
|---|---|---|
| Popup container | role="dialog" + aria-modal="true" | Same reasoning as the flagship Dialog pattern: identifies the popup and marks background content inert, backed by an explicit focus trap. |
| Popup container | aria-label (month + year) | Gives the popup an accessible name that includes the currently displayed month, since there's no single visible heading element serving that role. |
| Calendar table | role="grid" | Exposes the calendar as a 2D grid so assistive technology's grid navigation commands and cell/row semantics apply. |
| Each week | role="row" | Groups seven date cells into a row, matching the calendar's visual week structure. |
| Each date | role="gridcell" | Marks each date as a selectable grid cell rather than generic content. |
| Selected date | aria-selected="true" | Communicates the currently selected date's state programmatically, not just via a visual highlight. |
| Today's / selected date | aria-label="Today, July 2, 2026" / "Selected, ..." | Adds meaning that would otherwise be conveyed only by a visual ring or dot, so it reaches screen reader users too. |
| Focused date cell only | tabIndex={0} (all others tabIndex={-1}) | Roving tabindex: keeps a single Tab stop for the whole grid, the same approach used by Menu and Tabs on this site, while arrow keys move the active cell. |
Keyboard interaction model
| Key | Behavior |
|---|---|
| Right Arrow | Moves focus to the next day. |
| Left Arrow | Moves focus to the previous day. |
| Down Arrow | Moves focus to the same day one week later (+7 days). |
| Up Arrow | Moves focus to the same day one week earlier (-7 days). |
| Home | Moves focus to the first day (Sunday) of the current week. |
| End | Moves focus to the last day (Saturday) of the current week. |
| Page Up | Moves focus to the same day in the previous month. |
| Shift+Page Up | Moves focus to the same day in the previous year. |
| Page Down | Moves focus to the same day in the next month. |
| Shift+Page Down | Moves focus to the same day in the next year. |
| Enter / Space | Selects the focused date, closes the dialog, and returns focus to the trigger input. |
| Escape | Closes the dialog without changing the selection and returns focus to the trigger. |
| Tab / Shift+Tab | Cycles only within the dialog's focusable elements (month nav buttons, the focused date cell), trapped exactly as in the Dialog pattern. |
Focus management rules
- On open: focus moves to the selected date's cell, or today's cell if nothing is selected yet, the same "move focus into the dialog" requirement as the Dialog pattern.
- While open: Tab/Shift+Tab cycle only within the dialog's focusable elements (reused focus-trap logic from the Dialog pattern).
- Arrow keys, Home/End, and PageUp/PageDown move a roving tabindex within the grid, only one cell is ever a Tab stop at a time.
- On close (Enter/Space select, Escape, or scrim click): focus returns to the trigger button, exactly as the Dialog pattern restores focus to its trigger.
WCAG 2.2 success criteria mapping
| SC | Name | Level | Why it applies |
|---|---|---|---|
| 2.1.1 | Keyboard | A | All functionality is operable through a keyboard interface with no specific timing. |
| 2.4.3 | Focus Order | A | Focusable components receive focus in an order that preserves meaning and operability. |
| 3.3.2 | Labels or Instructions | A | Labels or instructions are provided when content requires user input. |
| 4.1.2 | Name, Role, Value | A | For all UI components, name, role, and value are programmatically determinable; states and changes are announced. |