Skip to content

Find what you need, instantly

Search components, ARIA roles & attributes, and WCAG criteria.

Forms & Controls

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.

date-picker-pattern.tsx
function onGridKeyDown(e) {
  switch (e.key) {
    case "ArrowRight": moveFocus(1); break;   // next day
    case "ArrowLeft":  moveFocus(-1); break;  // previous day
    case "ArrowDown":  moveFocus(7); break;   // next week
    case "ArrowUp":    moveFocus(-7); break;  // previous week
    case "Home":       setFocusedDate(startOfWeek(focused)); break;
    case "End":         /* last day of current week */ break;
    case "PageUp":      e.shiftKey ? moveYears(-1) : moveMonths(-1); break;
    case "PageDown":    e.shiftKey ? moveYears(1) : moveMonths(1); break;
    case "Enter": case " ": selectDate(focusedDate); break;
  }
}

<div role="dialog" aria-modal="true" aria-label="Choose date, July 2026">
  <div role="grid" onKeyDown={onGridKeyDown}>
    <div role="row">
      {week.map(day => (
        <button
          role="gridcell"
          tabIndex={isSameDay(day, focusedDate) ? 0 : -1}
          aria-selected={isSameDay(day, selected)}
          aria-label={isToday(day) ? `Today, ${formatLong(day)}` : formatLong(day)}
        >
          {day.getDate()}
        </button>
      ))}
    </div>
  </div>
</div>

/* Focus trap / Escape / focus-restore-to-trigger reuse the exact same
   logic as dialog-pattern.tsx — this popup IS a specialized dialog. */

Live demo

Required roles, states & properties

Required roles, states, and properties
ElementAttributeWhy
Popup containerrole="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 containeraria-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 tablerole="grid"Exposes the calendar as a 2D grid so assistive technology's grid navigation commands and cell/row semantics apply.
Each weekrole="row"Groups seven date cells into a row, matching the calendar's visual week structure.
Each daterole="gridcell"Marks each date as a selectable grid cell rather than generic content.
Selected datearia-selected="true"Communicates the currently selected date's state programmatically, not just via a visual highlight.
Today's / selected datearia-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 onlytabIndex={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

Keyboard interaction model
KeyBehavior
Right ArrowMoves focus to the next day.
Left ArrowMoves focus to the previous day.
Down ArrowMoves focus to the same day one week later (+7 days).
Up ArrowMoves focus to the same day one week earlier (-7 days).
HomeMoves focus to the first day (Sunday) of the current week.
EndMoves focus to the last day (Saturday) of the current week.
Page UpMoves focus to the same day in the previous month.
Shift+Page UpMoves focus to the same day in the previous year.
Page DownMoves focus to the same day in the next month.
Shift+Page DownMoves focus to the same day in the next year.
Enter / SpaceSelects the focused date, closes the dialog, and returns focus to the trigger input.
EscapeCloses the dialog without changing the selection and returns focus to the trigger.
Tab / Shift+TabCycles 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 dialog-pattern.tsx).
  • 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

WCAG 2.2 success criteria that apply to this component
SCNameLevelWhy it applies
2.1.1KeyboardAAll functionality is operable through a keyboard interface with no specific timing.
2.4.3Focus OrderAFocusable components receive focus in an order that preserves meaning and operability.
3.3.2Labels or InstructionsALabels or instructions are provided when content requires user input.
4.1.2Name, Role, ValueAFor all UI components, name, role, and value are programmatically determinable; states and changes are announced.

References