Pickers (date + time)
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.
Spec — Pickers (date + time)
Facet *isual*of Koder Design. Material parity: https://m3.material.io/components/date-pickers and https://m3.material.io/components/time-pickers.
Variant matrix
| Picker | Modes | Use |
|---|---|---|
| *ate — modal* | Calendar grid, dialog | Default for date selection |
| *ate — modal input* | Text input within dialog | Power-user keyboard entry |
| *ate — docked* | Calendar attached to anchor input | Continuous form context |
| *ate range — modal* | Two-month calendar with range selection | Trip / report range |
| *ime — dial* | Analog clock face | Default mobile entry |
| *ime — input* | Two numeric fields HH:MM | Desktop / 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] │
└──────────────────────────────────────┘- *ontainer* dialog, 360 dp wide (mobile portrait) /
328 dp (desktop)
- *eader* shows current selection in
headline-small - *onth navigation* arrows + month name; tap month opens year
picker
- *ay grid* 7 columns, 6 rows max; current day with circle outline;
selected day with filled circle (accent color)
- *ooter* Cancel + OK text buttons
- *oday indicator* 1 px circle outline on today's date
- *elected* filled circle,
primarybg,on-primarytext
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
Twomonth sideby-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
anddrag 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] │
└───────────────────────────────┘- *isplay* HH : MM in large text; tap each segment to edit
- *ial* hour ring (12 positions) or minute ring (60 positions,
shown as 12 multiples of 5)
- *M / PM*segmented button (12-hour locale only)
- *witch 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:
numerickeyboard on mobile; typetoedit 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 aspect | Driven by |
|---|---|
| First day of week | Locale (Sun / Mon / Sat) |
| Month / weekday names | Locale |
| Date format in header | Locale |
| 12 / 24-hour | Locale (en |
| Number system | Locale (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/maxdates → outofrange days disabled (38% opacity, notselectable)
disabledDatesarray → specific dates blocked (holidays, bookedslots)
disabledRangesfor date range picker
Disabled dates are still navigable via keyboard but cannot be selected; screen reader announces "Disabled".
R8 — Keyboard
| Key | Date picker | Time picker |
|---|---|---|
| Tab | Enters → cycles through controls | Same |
| Arrow Up/Down | Previous/next week | Increment by 1 |
| Arrow Left/Right | Previous/next day | Switch HH ↔ MM |
| Page Up/Down | Previous/next month | Increment by 5 (minute) / 1 (hour) |
| Home / End | Start/end of week | n/a |
| Enter | Selects date / time | Selects |
| Esc | Cancels | Cancels |
R9 — Accessibility
- Dialog wrapper:
role="dialog"+aria-modal="true"+aria-labelledby(pointing to "Select date") - Calendar grid:
role="grid"+aria-label="Date picker"; eachcell
role="gridcell"witharia-selectedon current - Out
ofrange / disabled cells:aria-disabled="true" - Today cell:
aria-current="date" - Time dial:
role="slider"per ring (hour / minute) witharia-valuenow / valuemin / valuemax - Time input fields: native
<input type="number">semantics; labelsread "Hour" / "Minute"
- Screen reader announces selection: "Selected: Monday, May 11, 2026"
R10 — Animation
- *odal open* scale
in 0.95 → 1 + fade (motionmedium) - *onth change* slide horizontal (motion-medium)
- *ear picker* vertical scroll list (in-place, no slide)
- *ial click* hand rotates to target position
(motion
emphasizeddecelerate) - *ange drag* tonal fill grows in real-time
- Reduced motion: instant transitions; no rotation animation
(snap dial)
R11 — Per-preset variation
| Preset | Date visual | Time visual |
|---|---|---|
material3 |
Tonal selection, rounded buttons | Dial first, input toggle |
material2 |
Solid accent selection | Dial first, no input toggle |
ios_cupertino |
Wheel pickers stacked (yearmonthday) | Wheel pickers (HH/MM) |
gnome |
Adwaita calendar w/ accent today | Spin buttons for HH/MM |
windows_11 |
Combo button revealing flyout | Combo button revealing flyout |
brutalist |
Sharp grid, no animation | Sharp dial, no rotation |
terminal_classic |
Text prompt YYYY-MM-DD> |
Text prompt HH:MM> |
R12 — Density
| Density | Cell size | Footer height |
|---|---|---|
| Compact | 36 px | 48 px |
| Default | 40 px | 52 px |
| Comfortable | 48 px | 64 px |
R13 — Forbidden patterns
- ❌ Date picker without
min/maxfor time-bounded contexts(birthdate, expiry, etc.)
- ❌ Time picker dial < 240 dp diameter (touch targets too small)
- ❌ Ignoring locale first
dayof-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
Cross-link
i18n/contract.kmd— locale-aware formattingthemes/color-roles.kmd—primaryfor selected,secondary-containerfor rangethemes/typography.kmd—headline-smallfor header,body-largefor cellscomponents/text-fields.kmd— anchor for docked variantcomponents/dialogs.kmd— modal wrapper patterninteraction/states.kmd— disabled / selected layersfoundations/elements.kmd— Control + Container families