DatePicker

A date field with a calendar, a list of times, or both behind a button inside it; a range picker on two months; and a time zone select — on the platform's popover, with no portal.
Import
JSX
import DatePicker, { DateRangePicker } from '@box-kite/react/components/datePicker';
import TimeZoneSelect from '@box-kite/react/components/timeZoneSelect';
Usage
Check-in
mm
dd
yyyy
value: null
JSX
<DatePicker label="Check-in" value={value} onValueChange={setValue} clearable />

Type it or pick it

The field is a DateField, so everything typed is in the reader's order and the value is the same ISO string with the same readings beside it. The button says what is chosen — Choose date, Oct 19, 2026 — and opening it puts focus on the chosen day, or today. Choosing closes the panel and hands focus back.

Times and date-times

type="time" opens a list of times every step minutes, and type="datetime" puts the calendar and the list side by side: a day keeps the time already chosen, and the panel closes once a time is picked. The zone rules are DateField's — 'local' by default, 'utc' or any IANA zone, or 'none'.
Publish at (UTC)
mm
dd
yyyy
hh
mm
AM
value: null
utc: null
JSX
<DatePicker type="datetime" zone="utc" label="Publish at (UTC)" step={15}
  onValueChange={(value, { utc }) => schedule(utc)} />

A range, with presets

DateRangePicker is two fields — start and end, each named for a screen reader — over a range calendar on two months. Its value is { start, end }, reported once both are filled in and in order; an end before its start marks the pair invalid rather than swapping it. presets puts shortcuts beside the calendar, and the date layer's arithmetic writes them.
Stay
mm
dd
yyyy
mm
dd
yyyy
value: null
JSX
<DateRangePicker label="Stay" startName="from" endName="to" clearable
  presets={[{ label: 'Next 7 days', value: { start: today(), end: add(today(), { days: 6 })! } }]} />

Choosing a zone

TimeZoneSelect is a Combobox over every zone the runtime knows, each with the offset it is at now, searchable by place, region or offset — chis, europe and +3 all find Chișinău. Its value is the IANA name every date component takes, so a meeting typed on another city's clock is two lines.
Meeting
10
19
2026
02
30
PM
GMT+3
JSX
<TimeZoneSelect label="Time zone" value={zone} onValueChange={setZone} />
<DateField type="datetime" zone={zone} label="Meeting" />

The panel

The top layer, no portal
The panel is the platform's popover: it paints above every stacking context, light dismiss and focus return are the browser's, and it inherits the theme and the direction around it. It hangs off the whole field, not off the button.
Mounted while open
A closed picker renders no calendar and no list of times, so a form with twenty of them carries twenty fields and nothing else.
Why not the native input
<input type="date"> follows the browser's language rather than the page's, has no zone, no range and no presets, and its popup cannot be styled. Textbox type="date" is still there for the cases it suits.

Styling

The field is a dateField and the panel a popover; what the picker adds is datePicker.trigger, datePicker.clear, datePicker.presets/preset and datePicker.times/time, whose selected variant is the chosen slot.

DatePicker props

Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on /box rather than repeated here.
PropTypeDefaultWhat it does
type'date' | 'time' | 'datetime''date'A day ('date', default), a time of day ('time'), or both ('datetime').
valuestring | null—Controlled value, an ISO string of the picker's kind — see DateField.
defaultValuestring | nullnullThe value it starts with when the picker owns its state.
onValueChangeChangeHandler<string | null, DateChangeReason, DateChangeDetails>—Fires with the value and its readings — utc, the reader's local wall clock, a Date.
zoneZoneInput | 'none'—Whose wall clock a date-time is on. Default 'local'; 'none' for no zone at all.
hourCycle12 | 24—A 12- or 24-hour clock, in the field and the list of times. Default the locale's.
granularityFieldGranularity—Whether a time is edited to the minute (default) or the second.
disambiguationDisambiguation—What a wall-clock time a zone skips or repeats means. Default 'compatible'.
namestring—What the value posts under in a form.
stepnumber30Minutes between the times the list offers. Default 30.
defaultTimestring'00:00'The time a day picked before any time takes. Default '00:00'.
presetsreadonly DatePreset[]—Shortcuts beside the calendar — { label: 'Today', value: today() }.
showZoneboolean—Whether a date-time names its zone after the time. Default: when the zone is not the reader's own.
localestring—The language of the field, the calendar and the times. Default the reader's.
minstring | null—The earliest value that is valid, and the first day the calendar offers.
maxstring | null—The latest.
isDateUnavailable(date: string) => boolean—Days nobody can choose.
disabledbooleanfalseNot editable, not focusable and not openable.
readOnlybooleanfalseFocusable and read out, but not editable, and the button does not open.
requiredboolean—The form is not valid until it is filled in — the browser's own check.
invalidboolean—Marks the value invalid whatever the bounds say. Default: invalid when out of bounds or unavailable.
labelReact.ReactNode—The picker's visible name, above the field.
labelledBystring—Names the field after an element already on the page, instead of label.
describedBystring—An element that describes the field — a hint, an error.
openboolean—Whether the panel is open. Controlled.
defaultOpenboolean—Whether it starts open, when the picker owns that itself.
onOpenChangeChangeHandler<boolean, DatePickerOpenReason>—Fires with the new state and why — the layer's four reasons, and 'select' when choosing closed it.
closeOnSelectbooleantrueWhether choosing closes the panel. Default true; a date-time closes once its time is chosen.
firstDayOfWeeknumber—The day the week starts on, 0 for Sunday. Default the locale's.
weekNumbersboolean—ISO week numbers down the calendar's side.
sideAnchorSide'bottom'Which side of the field the panel opens on. Default 'bottom'.
alignAnchorAlign'start'Which of the field's edges it lines up with. Default 'start'.
clearablebooleanfalseA button inside the field that empties it.
triggerLabelstring—The calendar button's name. Default 'Choose date', or 'Choose time'.
clearLabelstring'Clear'The clear button's name. Default 'Clear'.
tabIndexnumber—Every segment's and button's tab index. Default 0; -1 keeps the picker out of the page's tab order, which a grid cell wants.

DatePicker keyboard

KeyResult
Enter / SpaceOn the button, opens the panel.
EscapeCloses it and returns focus to the button.
Up / DownIn the list of times, the time before or after.

DatePicker accessibility

  • The button says what it does and what is chosen — Choose date, Oct 19, 2026 — and carries aria-expanded and aria-haspopup="dialog"; the panel is a role="dialog" named by the picker's label.
  • Opening puts focus on the chosen day (or today) in the calendar, or on the chosen time in the list.
Swept with axe on every release, in this state: DatePicker. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.

DatePicker style tree

Every part the component draws is a node with a name, so a default can be restyled with Box.components() instead of a selector — and a variant is a name too.
datePicker
datePicker.part
datePicker.trigger
datePicker.clear
datePicker.presets
datePicker.presetvariants: selected
datePicker.times
datePicker.timevariants: selected

DateRangePicker props

Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on /box rather than repeated here.
PropTypeDefaultWhat it does
valueDateRange | null—Controlled value: both ends, YYYY-MM-DD, or null.
defaultValueDateRange | nullnullThe span it starts with when the picker owns its value.
onValueChangeChangeHandler<DateRange | null, DateChangeReason>—Fires once both ends are filled in and in order, and with null when either is emptied.
startNamestring—What the start posts under in a form.
endNamestring—What the end posts under.
startLabelstring'Start date'The names a screen reader gives the two fields. Default 'Start date' and 'End date'.
endLabelstring'End date'The end field's name. Default 'End date'.
months1 | 2 | 32Months side by side in the panel. Default 2.
presetsreadonly DatePreset<DateRange>[]—Shortcuts beside the calendar — { label: 'This week', value: { start, end } }.
localestring—The language of the field, the calendar and the times. Default the reader's.
minstring | null—The earliest value that is valid, and the first day the calendar offers.
maxstring | null—The latest.
isDateUnavailable(date: string) => boolean—Days nobody can choose.
disabledbooleanfalseNot editable, not focusable and not openable.
readOnlybooleanfalseFocusable and read out, but not editable, and the button does not open.
requiredboolean—The form is not valid until it is filled in — the browser's own check.
invalidboolean—Marks the value invalid whatever the bounds say. Default: invalid when out of bounds or unavailable.
labelReact.ReactNode—The picker's visible name, above the field.
labelledBystring—Names the field after an element already on the page, instead of label.
describedBystring—An element that describes the field — a hint, an error.
openboolean—Whether the panel is open. Controlled.
defaultOpenboolean—Whether it starts open, when the picker owns that itself.
onOpenChangeChangeHandler<boolean, DatePickerOpenReason>—Fires with the new state and why — the layer's four reasons, and 'select' when choosing closed it.
closeOnSelectbooleantrueWhether choosing closes the panel. Default true; a date-time closes once its time is chosen.
firstDayOfWeeknumber—The day the week starts on, 0 for Sunday. Default the locale's.
weekNumbersboolean—ISO week numbers down the calendar's side.
sideAnchorSide'bottom'Which side of the field the panel opens on. Default 'bottom'.
alignAnchorAlign'start'Which of the field's edges it lines up with. Default 'start'.
clearablebooleanfalseA button inside the field that empties it.
triggerLabelstring'Choose dates'The calendar button's name. Default 'Choose date', or 'Choose time'.
clearLabelstring'Clear'The clear button's name. Default 'Clear'.
tabIndexnumber—Every segment's and button's tab index. Default 0; -1 keeps the picker out of the page's tab order, which a grid cell wants.

DateRangePicker keyboard

KeyResult
Enter / SpaceOn the button, opens the panel; in the calendar, anchors and then ends the range.
EscapeDrops a half-picked range, then closes the panel.

DateRangePicker accessibility

  • The two fields are named startLabel and endLabel and grouped under the picker's label; the calendar marks every day of the span aria-selected.
Swept with axe on every release, in this state: DateRangePicker. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.

DateRangePicker style tree

Every part the component draws is a node with a name, so a default can be restyled with Box.components() instead of a selector — and a variant is a name too.
datePicker
datePicker.part
datePicker.trigger
datePicker.clear
datePicker.presets
datePicker.presetvariants: selected
datePicker.times
datePicker.timevariants: selected

TimeZoneSelect props

Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on /box rather than repeated here.
PropTypeDefaultWhat it does
valuestring | null—The chosen IANA zone (Europe/Chisinau, UTC), or null. Controlled.
defaultValuestring | null—The zone it starts with when it owns its state. Default the reader's own.
onValueChangeChangeHandler<string | null, ComboboxValueReason>—Fires with the zone's IANA name.
zonesreadonly string[]—Only these zones, in IANA names. Default every zone the runtime knows, UTC first.
labelReact.ReactNode—The select's visible name.
placeholderstring—The field's placeholder. Not a name — it is gone on the first keystroke.
namestring—What the zone posts under in a form.
disabledboolean—Not editable and not openable, but still read out.
propsRecord<string, unknown>—Attributes for the <input> itself — an aria-label, an id.
emptyTextReact.ReactNode'No time zone matches'What the list says when nothing matches. Default 'No time zone matches'.

TimeZoneSelect keyboard

KeyResult
A printable characterSearches by place, region, IANA name or offset.
Down / UpOpens, or moves through what the search left.
EnterChooses the highlighted zone.
EscapeCloses the list, then clears what was typed.

TimeZoneSelect accessibility

  • The APG editable combobox: role="combobox" on the field, a role="listbox" of options, the active one named by aria-activedescendant. A thousand-odd options are windowed, so it opens in one frame.
Swept with axe on every release, in this state: TimeZoneSelect. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.

TimeZoneSelect style tree

Every part the component draws is a node with a name, so a default can be restyled with Box.components() instead of a selector — and a variant is a name too.
comboboxvariants: compact
combobox.label
combobox.field
combobox.chip
combobox.remove
combobox.icon
combobox.itemsvariants: closed, closedUp
combobox.window
combobox.itemvariants: highlighted, create
combobox.message