Date Picker

A date picker component combining segmented input field with a calendar dropdown. Supports keyboard navigation, single date and range selection, min/max constraints, and full accessibility.

/ /
Mon Tue Wed Thu Fri Sat Sun

Overview

The Date Picker component combines two patterns into an interactive widget:

  • Input — displays the selected date and accepts typed input
  • Calendar — a dropdown grid for visual date selection

Together they form a component that can be opened, navigated, and dismissed — with full keyboard and screen reader support.

Composition

.form-field.date-picker-field
├── label.field-label [for="date-day"]
└── .input-field.date [role="group", aria-labelledby]
    ├── .date-segments
    │   ├── input.date-segment.day [maxlength=2, inputmode=numeric, aria-label="Day"]
    │   ├── span.date-separator  "/"
    │   ├── input.date-segment.month [maxlength=2, inputmode=numeric, aria-label="Month"]
    │   ├── span.date-separator  "/"
    │   └── input.date-segment.year [maxlength=4, aria-label="Year"]
    ├── .input-field-control
    │   └── button [aria-label="Open calendar", aria-expanded, aria-controls]
    │       └── span.icon (calendar icon)
    └── .calendar [hidden until opened]
        ├── .calendar-header
        └── table.calendar-table [role="grid"]

Patterns Used

Pattern Role
Input Text field showing the formatted date
Input control button Calendar toggle trigger, matching clear and increment controls
Button ghost Calendar month navigation
Icon Calendar and chevron icons
Calendar Date grid with selection and keyboard nav

Interaction

Action Result
Type 2 digits in Day Auto-advances focus to Month
Type 2 digits in Month Auto-advances focus to Year
Press / or . in any segment Advances to next segment
Backspace on empty segment Moves focus to previous segment
Arrow Left at start of segment Moves to previous segment
Arrow Right at end of segment Moves to next segment
Click label Focuses Day and opens the calendar
Click field surface Focuses the nearest segment and opens the calendar
Click calendar icon Open/close the calendar dropdown
Click a day cell Fill segments, close calendar
Alt + Arrow Down in a segment Opens the calendar and moves focus to the grid
Escape (while calendar open) Close calendar without selecting
Tab Moves between segments, then to calendar trigger

States

State Description
Closed Only the input field is visible
Open Calendar is visible below the input
Date selected Input shows formatted date, calendar highlights the day
Invalid Input shows error state (out of range, invalid format)
Disabled Both input and calendar trigger are disabled

Accessibility

  • The visible label is associated with the Day segment and names the grouped field
  • Calendar trigger button has aria-label="Open calendar"
  • Calendar state is exposed with aria-expanded and aria-controls on the trigger
  • Pointer-open from the label or field keeps focus in the editable segments
  • Button/keyboard-open can move focus into the calendar grid
  • All day cells have aria-label with full date
  • Escape closes without selecting
  • Works entirely via keyboard (no mouse required)

Tokens

Reuses tokens from the composed patterns:

  • Input tokens (--input-*)
  • Input control button behavior from .input-field-control
  • Calendar tokens (--calendar-cell-*)

No additional component-specific tokens needed — the composition inherits from its parts.

Why Custom?

The native <input type="date"> has real accessibility and UX limitations:

Issue Native This Component
Touch targets Under 44px (browser controls) 44×44px minimum per cell
Keyboard navigation Inconsistent across browsers Full arrow/Home/End/PageUp/PageDown
Styling Shadow DOM, not brand/mode adaptable Token-based, Brand/Mode aware
Range selection Not supported Prepared (.is-range-start, .is-range-middle, .is-range-end)
Cross-browser consistency Safari ≠ Chrome ≠ Firefox Identical behavior everywhere
Screen reader experience Varies, often poor Explicit aria-label per cell, aria-live for month changes
Segment navigation Browser-dependent Tab between DD/MM/YYYY, ArrowUp/Down to increment

This component is a progressive enhancement: it provides a superior experience while maintaining the same semantics and data format as the native date input.

JavaScript

The Date Picker requires src/ui/components/date-input.js for:

  • Opening/closing the calendar popover
  • Synchronizing input value ↔ calendar selection
  • Keyboard event handling (Escape to close, Enter to select)
  • Date parsing and formatting
<script type="module">
  import { DateInput } from './components/date-input.js';
  document.querySelectorAll('.date-input').forEach(el => new DateInput(el));
</script>