Components · Date & time
A start/end pair of DateTimePickers under one labeled, Surface-grouped control with built-in start ≤ end validation — the end half's lower bound is pinned to the chosen start, and an inline role=alert catches any inverted range. Below, the SchedulerStrip lays a 30-min slot lattice on a single day.
Band = the selected span · endpoints filled · Jul 2 = today ring.
Beagle intake & vetting — Kern County. The end half's lower bound is pinned to the start (07/08), so the native picker won't open earlier. Validity is reported via onValidityChange; isInvertedRange(start,end) is the shared predicate. Values → 2026-07-08T08:00 and 2026-07-11T17:00.
End must be on or after start.
startLabel / endLabel default to Start / End; both halves share granularity, stepMinutes, and timezoneLabel. Pass error to override the default inverted-range message.
onSelectSlot(slotStart) fires on an empty slot (granularity = slotMinutes, default 30); onSelectEvent(event) on a block. Slot hover is an accent 7% wash, the selected slot rings accent, and 11:00 holds the roving :focus. Today + the now-line read via text/aria, not color alone. Below sm the grid collapses to a sorted agenda list.
DateTimePicker, sharing granularity / stepMinutes / timezoneLabel / min / max. React via onStartChange / onEndChange.role=alert. error overrides the message; onValidityChange(valid) fires when validity flips. isInvertedRange is exported.events: CalendarEvent[] + date. Timed events lane-pack as positioned blocks; untimed go to the all-day strip. today drives the now-line + Today chip.onSelectSlot(slotStart) on an empty slot, onSelectEvent(event) on a block. slotMinutes (default 30) sets the selectable lattice; the slot grid is a roving-tabindex group (ArrowUp/Down + Enter).Source · ds/datetime/DateTimeRangePicker.tsx (two DateTimePickers + isInvertedRange + inline role=alert) · ds/datetime/SchedulerStrip.tsx (ds/calendar dayHours / splitDayEvents / placeTimed on a Surface).