Skip to content

Pickers (date + time)

components specs/components/pickers.kmd

Compound controls for selecting a date, time, date range, or date-time. Material parity (`/components/date-pickers` and `/components/time-pickers`). Covers docked vs modal date pickers, dial vs input time pickers, date ranges, locale rules, and keyboard navigation.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Pickers (date + time)

Facet Visual of Koder Design. Material parity: https://m3.material.io/components/date-pickers and https://m3.material.io/components/time-pickers.

Variant matrix

PickerModesUse
Date — modalCalendar grid, dialogDefault for date selection
Date — modal inputText input within dialogPower-user keyboard entry
Date — dockedCalendar attached to anchor inputContinuous form context
Date range — modalTwo-month calendar with range selectionTrip / report range
Time — dialAnalog clock faceDefault mobile entry
Time — inputTwo numeric fields HH:MMDesktop / keyboard preference

R1 — Date picker — modal (calendar)

Anatomy:

┌──────────────────────────────────────┐
│ Select date           ⌄  ⌃ Year      │
│ ────────────────────────────────────  │
│ ◂  May 2026                    ▸     │
│                                       │
│  S  M  T  W  T  F  S                 │
│                  1  2                 │
│  3  4  5  6  7  8  9                 │
│ 10 11 12 13 14 15 16                  │
│ 17 18 19 20 21 22 23                  │
│ 24 25 26 27 28 29 30                  │
│ 31                                    │
│                                       │
│                [Cancel]  [OK]         │
└──────────────────────────────────────┘
  • Container: dialog, 360 dp wide (mobile portrait) / 328 dp (desktop)
  • Header: shows current selection in headline-small
  • Month navigation: arrows + month name; tap month opens year picker
  • Day grid: 7 columns, 6 rows max; current day with circle outline; selected day with filled circle (accent color)
  • Footer: Cancel + OK text buttons
  • Today indicator: 1 px circle outline on today's date
  • Selected: filled circle, primary bg, on-primary text

R2 — Date picker — docked

Calendar floats below an anchor text field (no modal scrim). Used in forms with multiple date fields, where modal context-switch is heavy.

  • Anchor: text field with calendar icon trailing
  • On focus / icon tap: calendar appears below (above if no room)
  • Closes when user picks a date OR clicks outside
  • No scrim — page stays interactive

R3 — Date range picker

Two-month side-by-side calendar:

┌──────────────────────────────────────────────────────┐
│  Start date  →  End date                              │
│  Mar 12      →  Mar 28                                │
│ ──────────────────────────────────────────────────── │
│ ◂  March 2026         April 2026                ▸    │
│                                                       │
│  S  M  T  W  T  F  S    S  M  T  W  T  F  S          │
│  ...                     ...                          │
│                                                       │
│                       [Cancel]  [Save]                │
└──────────────────────────────────────────────────────┘
  • Two months visible; tap-and-drag OR two-tap to define range
  • Selected range: tonal bg (secondary-container) for days in range; filled circle on start + end
  • Mobile (Compact): one month with scroll, range still applies

R4 — Time picker — dial

Anatomy:

┌───────────────────────────────┐
│        Select time             │
│ ─────────────────────────────  │
│         ┌─ 10 : 30 ─┐  AM  PM │
│         └───────────┘          │
│                                │
│            ╭─────╮             │
│         11 │  ● 12             │
│       10  ╱│       ╲ 1         │
│         9  │   ●   │ 2         │
│           ╲│       │            │
│         8  └─────┘  3          │
│           7  6  5  4            │
│                                │
│            [Cancel]  [OK]      │
└───────────────────────────────┘
  • Display: HH : MM in large text; tap each segment to edit
  • Dial: hour ring (12 positions) or minute ring (60 positions, shown as 12 multiples of 5)
  • AM / PM segmented button (12-hour locale only)
  • Switch to keyboard: small icon toggles to Input variant

R5 — Time picker — input

Two text fields for HH and MM with : separator, plus AM/PM segmented button (12-hour locale only).

  • Each field: numeric keyboard on mobile; type-to-edit on desktop
  • Auto-advance: typing 2 digits in HH jumps to MM field
  • Validation: HH ∈ [0, 23] or [1, 12]; MM ∈ [0, 59]
  • Smaller dialog than dial variant (no clock face)

Used as default on desktop AND as toggle from dial when user prefers keyboard.

R6 — Locale rules

Pickers ALWAYS respect locale (per i18n/contract.kmd):

Locale aspectDriven by
First day of weekLocale (Sun / Mon / Sat)
Month / weekday namesLocale
Date format in headerLocale
12 / 24-hourLocale (en-US 12h; pt-BR 24h)
Number systemLocale (Latin / Arabic-Indic / etc.)

Override via prop only if domain requires (e.g., 24-hour for medical schedules in en-US). Document why.

R7 — Constraints

Pickers support:

  • min / max dates → out-of-range days disabled (38% opacity, not selectable)
  • disabledDates array → specific dates blocked (holidays, booked slots)
  • disabledRanges for date range picker

Disabled dates are still navigable via keyboard but cannot be selected; screen reader announces "Disabled".

R8 — Keyboard

KeyDate pickerTime picker
TabEnters → cycles through controlsSame
Arrow Up/DownPrevious/next weekIncrement by 1
Arrow Left/RightPrevious/next daySwitch HH ↔ MM
Page Up/DownPrevious/next monthIncrement by 5 (minute) / 1 (hour)
Home / EndStart/end of weekn/a
EnterSelects date / timeSelects
EscCancelsCancels

R9 — Accessibility

  • Dialog wrapper: role="dialog" + aria-modal="true" + aria-labelledby (pointing to "Select date")
  • Calendar grid: role="grid" + aria-label="Date picker"; each cell role="gridcell" with aria-selected on current
  • Out-of-range / disabled cells: aria-disabled="true"
  • Today cell: aria-current="date"
  • Time dial: role="slider" per ring (hour / minute) with aria-valuenow / valuemin / valuemax
  • Time input fields: native <input type="number"> semantics; labels read "Hour" / "Minute"
  • Screen reader announces selection: "Selected: Monday, May 11, 2026"

R10 — Animation

  • Modal open: scale-in 0.95 → 1 + fade (motion-medium)
  • Month change: slide horizontal (motion-medium)
  • Year picker: vertical scroll list (in-place, no slide)
  • Dial click: hand rotates to target position (motion-emphasized-decelerate)
  • Range drag: tonal fill grows in real-time
  • Reduced motion: instant transitions; no rotation animation (snap dial)

R11 — Per-preset variation

PresetDate visualTime visual
material3Tonal selection, rounded buttonsDial first, input toggle
material2Solid accent selectionDial first, no input toggle
ios_cupertinoWheel pickers stacked (year/month/day)Wheel pickers (HH/MM)
gnomeAdwaita calendar w/ accent todaySpin buttons for HH/MM
windows_11Combo button revealing flyoutCombo button revealing flyout
brutalistSharp grid, no animationSharp dial, no rotation
terminal_classicText prompt YYYY-MM-DD>Text prompt HH:MM>

R12 — Density

DensityCell sizeFooter height
Compact36 px48 px
Default40 px52 px
Comfortable48 px64 px

R13 — Forbidden patterns

  • ❌ Date picker without min / max for time-bounded contexts (birthdate, expiry, etc.)
  • ❌ Time picker dial < 240 dp diameter (touch targets too small)
  • ❌ Ignoring locale first-day-of-week
  • ❌ Picker that requires server roundtrip per month change (latency)
  • ❌ Picker without keyboard fallback (Input variant always available for accessibility)
  • ❌ Calendar grid that scrolls vertically through years (use year picker overlay instead)
  • ❌ Disabled dates without explanation (provide tooltip or description)
  • ❌ Range picker that allows end < start without auto-swap
  • i18n/contract.kmd — locale-aware formatting
  • themes/color-roles.kmdprimary for selected, secondary-container for range
  • themes/typography.kmdheadline-small for header, body-large for cells
  • components/text-fields.kmd — anchor for docked variant
  • components/dialogs.kmd — modal wrapper pattern
  • interaction/states.kmd — disabled / selected layers
  • foundations/elements.kmd — Control + Container families

References