# DatePicker

_Box Kite 2.2.6 · a markdown copy of https://www.box-kite.dev/date-picker/_

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**

```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'`.

```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.

```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.

```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](https://www.box-kite.dev/box.md) rather than repeated here.

| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `type` | `'date' \| 'time' \| 'datetime'` | `'date'` | A day (`'date'`, default), a time of day (`'time'`), or both (`'datetime'`). |
| `value` | `string \| null` | — | Controlled value, an ISO string of the picker's kind — see `DateField`. |
| `defaultValue` | `string \| null` | `null` | The value it starts with when the picker owns its state. |
| `onValueChange` | `ChangeHandler<string \| null, DateChangeReason, DateChangeDetails>` | — | Fires with the value and its readings — `utc`, the reader's `local` wall clock, a `Date`. |
| `zone` | `ZoneInput \| 'none'` | — | Whose wall clock a date-time is on. Default `'local'`; `'none'` for no zone at all. |
| `hourCycle` | `12 \| 24` | — | A 12- or 24-hour clock, in the field and the list of times. Default the locale's. |
| `granularity` | `FieldGranularity` | — | Whether a time is edited to the minute (default) or the second. |
| `disambiguation` | `Disambiguation` | — | What a wall-clock time a zone skips or repeats means. Default `'compatible'`. |
| `name` | `string` | — | What the value posts under in a form. |
| `step` | `number` | `30` | Minutes between the times the list offers. Default 30. |
| `defaultTime` | `string` | `'00:00'` | The time a day picked before any time takes. Default `'00:00'`. |
| `presets` | `readonly DatePreset[]` | — | Shortcuts beside the calendar — `{ label: 'Today', value: today() }`. |
| `showZone` | `boolean` | — | Whether a date-time names its zone after the time. Default: when the zone is not the reader's own. |
| `locale` | `string` | — | The language of the field, the calendar and the times. Default the reader's. |
| `min` | `string \| null` | — | The earliest value that is valid, and the first day the calendar offers. |
| `max` | `string \| null` | — | The latest. |
| `isDateUnavailable` | `(date: string) => boolean` | — | Days nobody can choose. |
| `disabled` | `boolean` | `false` | Not editable, not focusable and not openable. |
| `readOnly` | `boolean` | `false` | Focusable and read out, but not editable, and the button does not open. |
| `required` | `boolean` | — | The form is not valid until it is filled in — the browser's own check. |
| `invalid` | `boolean` | — | Marks the value invalid whatever the bounds say. Default: invalid when out of bounds or unavailable. |
| `label` | `React.ReactNode` | — | The picker's visible name, above the field. |
| `labelledBy` | `string` | — | Names the field after an element already on the page, instead of `label`. |
| `describedBy` | `string` | — | An element that describes the field — a hint, an error. |
| `open` | `boolean` | — | Whether the panel is open. Controlled. |
| `defaultOpen` | `boolean` | — | Whether it starts open, when the picker owns that itself. |
| `onOpenChange` | `ChangeHandler<boolean, DatePickerOpenReason>` | — | Fires with the new state and why — the layer's four reasons, and `'select'` when choosing closed it. |
| `closeOnSelect` | `boolean` | `true` | Whether choosing closes the panel. Default `true`; a date-time closes once its time is chosen. |
| `firstDayOfWeek` | `number` | — | The day the week starts on, 0 for Sunday. Default the locale's. |
| `weekNumbers` | `boolean` | — | ISO week numbers down the calendar's side. |
| `side` | `AnchorSide` | `'bottom'` | Which side of the field the panel opens on. Default `'bottom'`. |
| `align` | `AnchorAlign` | `'start'` | Which of the field's edges it lines up with. Default `'start'`. |
| `clearable` | `boolean` | `false` | A button inside the field that empties it. |
| `triggerLabel` | `string` | — | The calendar button's name. Default `'Choose date'`, or `'Choose time'`. |
| `clearLabel` | `string` | `'Clear'` | The clear button's name. Default `'Clear'`. |
| `tabIndex` | `number` | — | 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

| Key | Result |
| --- | --- |
| `Enter / Space` | On the button, opens the panel. |
| `Escape` | Closes it and returns focus to the button. |
| `Up / Down` | In the list of times, the time before or after. |

## DatePicker accessibility

Implements [the WAI-ARIA APG pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).

- 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.preset`variants: selected

`datePicker.times`

`datePicker.time`variants: 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](https://www.box-kite.dev/box.md) rather than repeated here.

| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `value` | `DateRange \| null` | — | Controlled value: both ends, `YYYY-MM-DD`, or `null`. |
| `defaultValue` | `DateRange \| null` | `null` | The span it starts with when the picker owns its value. |
| `onValueChange` | `ChangeHandler<DateRange \| null, DateChangeReason>` | — | Fires once both ends are filled in and in order, and with `null` when either is emptied. |
| `startName` | `string` | — | What the start posts under in a form. |
| `endName` | `string` | — | What the end posts under. |
| `startLabel` | `string` | `'Start date'` | The names a screen reader gives the two fields. Default `'Start date'` and `'End date'`. |
| `endLabel` | `string` | `'End date'` | The end field's name. Default `'End date'`. |
| `months` | `1 \| 2 \| 3` | `2` | Months side by side in the panel. Default 2. |
| `presets` | `readonly DatePreset<DateRange>[]` | — | Shortcuts beside the calendar — `{ label: 'This week', value: { start, end } }`. |
| `locale` | `string` | — | The language of the field, the calendar and the times. Default the reader's. |
| `min` | `string \| null` | — | The earliest value that is valid, and the first day the calendar offers. |
| `max` | `string \| null` | — | The latest. |
| `isDateUnavailable` | `(date: string) => boolean` | — | Days nobody can choose. |
| `disabled` | `boolean` | `false` | Not editable, not focusable and not openable. |
| `readOnly` | `boolean` | `false` | Focusable and read out, but not editable, and the button does not open. |
| `required` | `boolean` | — | The form is not valid until it is filled in — the browser's own check. |
| `invalid` | `boolean` | — | Marks the value invalid whatever the bounds say. Default: invalid when out of bounds or unavailable. |
| `label` | `React.ReactNode` | — | The picker's visible name, above the field. |
| `labelledBy` | `string` | — | Names the field after an element already on the page, instead of `label`. |
| `describedBy` | `string` | — | An element that describes the field — a hint, an error. |
| `open` | `boolean` | — | Whether the panel is open. Controlled. |
| `defaultOpen` | `boolean` | — | Whether it starts open, when the picker owns that itself. |
| `onOpenChange` | `ChangeHandler<boolean, DatePickerOpenReason>` | — | Fires with the new state and why — the layer's four reasons, and `'select'` when choosing closed it. |
| `closeOnSelect` | `boolean` | `true` | Whether choosing closes the panel. Default `true`; a date-time closes once its time is chosen. |
| `firstDayOfWeek` | `number` | — | The day the week starts on, 0 for Sunday. Default the locale's. |
| `weekNumbers` | `boolean` | — | ISO week numbers down the calendar's side. |
| `side` | `AnchorSide` | `'bottom'` | Which side of the field the panel opens on. Default `'bottom'`. |
| `align` | `AnchorAlign` | `'start'` | Which of the field's edges it lines up with. Default `'start'`. |
| `clearable` | `boolean` | `false` | A button inside the field that empties it. |
| `triggerLabel` | `string` | `'Choose dates'` | The calendar button's name. Default `'Choose date'`, or `'Choose time'`. |
| `clearLabel` | `string` | `'Clear'` | The clear button's name. Default `'Clear'`. |
| `tabIndex` | `number` | — | 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

| Key | Result |
| --- | --- |
| `Enter / Space` | On the button, opens the panel; in the calendar, anchors and then ends the range. |
| `Escape` | Drops a half-picked range, then closes the panel. |

## DateRangePicker accessibility

Implements [the WAI-ARIA APG pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).

- 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.preset`variants: selected

`datePicker.times`

`datePicker.time`variants: 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](https://www.box-kite.dev/box.md) rather than repeated here.

| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `value` | `string \| null` | — | The chosen IANA zone (`Europe/Chisinau`, `UTC`), or `null`. Controlled. |
| `defaultValue` | `string \| null` | — | The zone it starts with when it owns its state. Default the reader's own. |
| `onValueChange` | `ChangeHandler<string \| null, ComboboxValueReason>` | — | Fires with the zone's IANA name. |
| `zones` | `readonly string[]` | — | Only these zones, in IANA names. Default every zone the runtime knows, UTC first. |
| `label` | `React.ReactNode` | — | The select's visible name. |
| `placeholder` | `string` | — | The field's placeholder. Not a name — it is gone on the first keystroke. |
| `name` | `string` | — | What the zone posts under in a form. |
| `disabled` | `boolean` | — | Not editable and not openable, but still read out. |
| `props` | `Record<string, unknown>` | — | Attributes for the `<input>` itself — an `aria-label`, an `id`. |
| `emptyText` | `React.ReactNode` | `'No time zone matches'` | What the list says when nothing matches. Default `'No time zone matches'`. |

## TimeZoneSelect keyboard

| Key | Result |
| --- | --- |
| `A printable character` | Searches by place, region, IANA name or offset. |
| `Down / Up` | Opens, or moves through what the search left. |
| `Enter` | Chooses the highlighted zone. |
| `Escape` | Closes the list, then clears what was typed. |

## TimeZoneSelect accessibility

Implements [the WAI-ARIA APG pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).

- 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.

`combobox`variants: compact

`combobox.label`

`combobox.field`

`combobox.chip`

`combobox.remove`

`combobox.icon`

`combobox.items`variants: closed, closedUp

`combobox.window`

`combobox.item`variants: highlighted, create

`combobox.message`

---

_Every page: https://www.box-kite.dev/llms.txt · every prop, measured: https://www.box-kite.dev/props.md_

