# Date picker

A field you can type a date into, with a calendar that opens below it.

- **Import:** `import { DatePicker } from "@booleanpress/ui/date-picker"`
- **Also install:** `react-day-picker`
- **APG Date picker dialog:** <https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/>
- **Page:** <https://ui.booleanpress.com/components/date-picker> · @booleanpress/ui 0.2.0

## Usage

`DatePicker` is an `Input` with the package's `Calendar` in a `Popover`. It needs the `react-day-picker` peer package. Name it with a `Label` whose `htmlFor` is its `id`: `id`, `placeholder`, `aria-*` and the other input attributes go on the text field, and `className` on the root, which holds the field and its button.

```tsx
import { useState } from "react"
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export function SendOn() {
  const [date, setDate] = useState<Date | null>(null)
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="send-on">Send on</Label>
      <DatePicker id="send-on" value={date} onValueChange={setDate} />
    </div>
  )
}
```

It is uncontrolled with `defaultValue`, or controlled with `value` and `onValueChange`. The value is a `Date` or null; with `mode="multiple"` it is a `Date[]` and the field lists the dates with commas (with semicolons when a date's own text has a comma, as `"EEE, d MMM yyyy"` does). The field shows the date in the provider's `locale` ("10/14/2026" in en-US, "14.10.2026" in de-DE), or in `format`, a pattern of `yyyy` `yy` `MMMM` `MMM` `MM` `M` `dd` `d` `EEE` `HH` `hh` `mm` `a` and literal text.

**Typing.** The text is read when the field loses focus and on Enter: numbers in the locale's order, a month name, eight digits with no separators, or the `format` pattern with any separator. A year of one or two digits is the year nearest `today`, within 50 years (85 is 1985, 30 is 2030 in 2026); with `showTime`, AM or PM may stand before or after the time, as the locale writes it. Text the person has not changed leaves the value as it is. Text that is not a date, or a date outside `min` and `max`, stays in the field so it can be corrected, marks the field invalid (`aria-invalid`), and empties the value until it is fixed. The field never throws the typed text away, and the value never holds a date the field does not show.

**The calendar.** `trigger` chooses what opens it: `button` (the default) joins a calendar button to the field's end, `icon` puts the icon inside the field, and `field` opens it on a click in the field. Alt+ArrowDown opens it from the field in every case. `showButtonBar` adds Today and Clear under it; `showTime` adds hours and minutes (12 or 24 h by locale, or `hourCycle`); `view="month"` and `view="year"` choose a month or a year instead of a day; `numberOfMonths` shows several months; `inline` shows the calendar on the page with no field. `min` and `max` block the days outside them and stop the month arrows there; with `showTime`, a `min` or `max` that has a time (any but midnight) also bounds the time on its day, and a day or time chosen in the popup is brought inside it. Today chooses today's day, or its month's 1st with `view="month"`, or its 1 January with `view="year"`, and shows its month. `calendarProps` passes `modifiers`, `modifiersClassNames`, `disabled` days, `labels` and other props to the `Calendar`, for example to mark days with a dot.

**Time zones.** Values are plain `Date` instants. A day is midnight of that day in the provider's `timeZone` (the browser's when none is set), and the calendar, the field's text and the typed text all use that zone. On a site whose `timeZone` is "Europe/Berlin", a visitor in New York who picks 14 October gets the instant of 14 October, 00:00 in Berlin (13 October, 18:00 in New York): the date the site means, whatever the visitor's clock says. To send it to a server, give the picker a `name`: a hidden input carries `2026-10-14` (the full ISO instant with `showTime`), already in the site's zone. Read a `Date` back in the same zone. A day whose midnight the clocks skip (the change to summer time happens at midnight in some zones, such as America/Santiago) is the first instant of that day. On a page rendered on a server, set the provider's `locale` and `timeZone`, so the server and the browser write the same text.

`size` (`sm` 28 px, `default` 35 px, `lg` 42 px) and `variant="filled"` follow `Input`; `fluid` fills the column; `clearable` adds a ×. Pass `today` and `defaultMonth` for screenshots and tests that must not read the clock.

## Examples

### Basic

A named field with a date in the locale's format and the calendar button joined to its end.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerBasic() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="send-on">Send the newsletter on</Label>
      <DatePicker id="send-on" defaultValue={new Date(2026, 9, 14)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Format

`format="dd/MM/yyyy"` and `format="EEE, d MMM yyyy"`: typed text is read with the same pattern.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerFormat() {
  return (
    <div className="flex flex-wrap gap-6">
      <div className="flex flex-col gap-2">
        <Label htmlFor="invoice-date">Invoice date</Label>
        <DatePicker id="invoice-date" format="dd/MM/yyyy" defaultValue={new Date(2026, 9, 14)} today={new Date(2026, 9, 3)} />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="report-date">Report date</Label>
        <DatePicker
          id="report-date"
          format="EEE, d MMM yyyy"
          defaultValue={new Date(2026, 9, 14)}
          today={new Date(2026, 9, 3)}
          className="w-56"
        />
      </div>
    </div>
  )
}
```

### Icon trigger

`trigger="button"`, `trigger="icon"` with the calendar icon or your own `icon`, and `trigger="field"`, where a click in the field opens the calendar.

```tsx
import { ClockIcon } from "lucide-react"
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

const today = new Date(2026, 9, 3)

export default function DatePickerIcon() {
  return (
    <div className="grid w-full max-w-xl gap-6 sm:grid-cols-2">
      <div className="flex flex-col gap-2">
        <Label htmlFor="trigger-button">Button</Label>
        <DatePicker id="trigger-button" trigger="button" today={today} fluid />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="trigger-icon">Icon in the field</Label>
        <DatePicker id="trigger-icon" trigger="icon" today={today} fluid />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="trigger-custom">Custom icon</Label>
        <DatePicker id="trigger-custom" trigger="icon" icon={<ClockIcon aria-hidden="true" />} today={today} fluid />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="trigger-field">The field opens it</Label>
        <DatePicker id="trigger-field" trigger="field" today={today} fluid />
      </div>
    </div>
  )
}
```

### Min and max

`min` and `max` block the days outside 5–25 October and stop the month arrows; a typed date outside them is invalid.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerMinMax() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="campaign-start">Campaign start (5 to 25 October)</Label>
      <DatePicker
        id="campaign-start"
        min={new Date(2026, 9, 5)}
        max={new Date(2026, 9, 25)}
        defaultMonth={new Date(2026, 9)}
        today={new Date(2026, 9, 3)}
      />
    </div>
  )
}
```

### Multiple dates

`mode="multiple"` toggles days on and off; the field lists them, and typed dates are separated by commas.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerMultiple() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Label htmlFor="digest-days">Digest days</Label>
      <DatePicker
        id="digest-days"
        mode="multiple"
        defaultValue={[new Date(2026, 9, 6), new Date(2026, 9, 13), new Date(2026, 9, 20)]}
        today={new Date(2026, 9, 3)}
        fluid
      />
    </div>
  )
}
```

### Button bar

`showButtonBar` adds Today and Clear under the calendar.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerButtonBar() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="export-from">Export logs from</Label>
      <DatePicker id="export-from" showButtonBar today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Time

`showTime` adds hours, minutes and AM/PM under the calendar; `hourCycle` forces a 12- or 24-hour clock.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

const today = new Date(2026, 9, 3)

export default function DatePickerTime() {
  return (
    <div className="flex flex-wrap gap-6">
      <div className="flex flex-col gap-2">
        <Label htmlFor="send-at-12">Send at (12-hour)</Label>
        <DatePicker id="send-at-12" showTime hourCycle={12} defaultValue={new Date(2026, 9, 14, 9, 30)} today={today} className="w-60" />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="send-at-24">Send at (24-hour)</Label>
        <DatePicker id="send-at-24" showTime hourCycle={24} defaultValue={new Date(2026, 9, 14, 17, 45)} today={today} className="w-60" />
      </div>
    </div>
  )
}
```

### Month picker

`view="month"` chooses a month (the value is its 1st); the year in the header opens a year list.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerMonthPicker() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="billing-month">Billing month</Label>
      <DatePicker id="billing-month" view="month" defaultValue={new Date(2026, 9, 1)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Year picker

`view="year"` chooses a year from a decade (the value is its 1 January).

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerYearPicker() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="report-year">Annual report year</Label>
      <DatePicker id="report-year" view="year" defaultValue={new Date(2026, 0, 1)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Two months

`numberOfMonths={2}` shows October and November side by side, divided by a line.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerTwoMonths() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="renewal-date">Renewal date</Label>
      <DatePicker id="renewal-date" numberOfMonths={2} defaultMonth={new Date(2026, 9)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Date template

`calendarProps` adds a `delivery` modifier: a dot under the days with deliveries, and the same fact in each day's accessible name.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

// Days with scheduled deliveries get a dot, and say so to screen readers in the day's name.
const deliveries = [2, 6, 9, 13, 16, 22, 27, 29].map((day) => new Date(2026, 9, day))

export default function DatePickerDateTemplate() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="delivery-day">Delivery day</Label>
      <DatePicker
        id="delivery-day"
        defaultMonth={new Date(2026, 9)}
        today={new Date(2026, 9, 3)}
        calendarProps={{
          modifiers: { delivery: deliveries },
          modifiersClassNames: {
            delivery: "after:pointer-events-none after:absolute after:inset-x-0 after:bottom-1 after:mx-auto after:size-1 after:rounded-full after:bg-info",
          },
          // Your own day names replace the built-in ones, so they say "Today" and "selected" themselves.
          labels: {
            labelDayButton: (date, modifiers) =>
              [
                modifiers.today ? "Today" : "",
                date.toLocaleDateString("en-US", { weekday: "long", day: "numeric", month: "long", year: "numeric" }),
                modifiers.delivery ? "deliveries scheduled" : "",
                modifiers.selected ? "selected" : "",
              ]
                .filter(Boolean)
                .join(", "),
          },
        }}
      />
    </div>
  )
}
```

### Inline

`inline` shows the calendar on the page, named by `aria-label`, with no field or popup.

```tsx
import { useState } from "react"
import { DatePicker } from "@booleanpress/ui/date-picker"

export default function DatePickerInline() {
  const [date, setDate] = useState<Date | null>(new Date(2026, 9, 14))

  return (
    <div className="flex flex-col items-start gap-3">
      <DatePicker inline aria-label="Publish date" value={date} onValueChange={setDate} today={new Date(2026, 9, 3)} />
      <p className="text-sm text-muted-foreground">
        Publishes on {date ? date.toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric" }) : "no date yet"}.
      </p>
    </div>
  )
}
```

### Clear

`clearable` shows a × while there is a date; it empties the value and keeps the focus in the field.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerClear() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="expires-on">API key expires on</Label>
      <DatePicker id="expires-on" clearable defaultValue={new Date(2026, 11, 31)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Sizes

`sm` is 28 px tall with a 28 px button, `default` 35 px with 36 px, `lg` 42 px with 42 px.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"

const today = new Date(2026, 9, 3)

export default function DatePickerSizes() {
  return (
    <div className="flex flex-col items-center gap-3">
      <DatePicker size="sm" aria-label="Small" placeholder="Small" today={today} />
      <DatePicker aria-label="Default" placeholder="Default" today={today} />
      <DatePicker size="lg" aria-label="Large" placeholder="Large" today={today} />
    </div>
  )
}
```

### Filled

`variant="filled"` draws the grey `--field-filled` fill, which stays on hover and focus.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerFilled() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="ticket-due">Ticket due</Label>
      <DatePicker id="ticket-due" variant="filled" defaultValue={new Date(2026, 9, 21)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Fluid

`fluid` makes the field and its button fill the width of their container.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerFluid() {
  return (
    <div className="flex w-full flex-col gap-2">
      <Label htmlFor="trial-ends">Trial ends</Label>
      <DatePicker id="trial-ends" fluid defaultValue={new Date(2026, 9, 31)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Disabled

Neither the field nor the button can be used.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerDisabled() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="created-on">Created on</Label>
      <DatePicker id="created-on" disabled defaultValue={new Date(2026, 9, 1)} today={new Date(2026, 9, 3)} />
    </div>
  )
}
```

### Invalid

`aria-invalid` draws the error edge; `aria-describedby` reads the message. Text that is not a date sets the same state.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerInvalid() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="go-live">Go-live date</Label>
      <DatePicker id="go-live" aria-invalid aria-describedby="go-live-error" today={new Date(2026, 9, 3)} />
      <p id="go-live-error" className="text-sm text-destructive-strong">
        Choose the day the mailer goes live.
      </p>
    </div>
  )
}
```

### With a float label

Inside `FloatLabel`, the label rests in the empty field and moves above it once there is a date.

```tsx
import { DatePicker } from "@booleanpress/ui/date-picker"
import { FloatLabel } from "@booleanpress/ui/float-label"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerFloatLabel() {
  return (
    <div className="flex flex-col gap-8 pt-4.5">
      <FloatLabel>
        <DatePicker id="fl-renewal" today={new Date(2026, 9, 3)} />
        <Label htmlFor="fl-renewal">Renewal date</Label>
      </FloatLabel>
      <FloatLabel>
        <DatePicker id="fl-started" defaultValue={new Date(2026, 9, 1)} today={new Date(2026, 9, 3)} />
        <Label htmlFor="fl-started">Plan started</Label>
      </FloatLabel>
    </div>
  )
}
```

### With a form

The picker sits outside its form and belongs to it through `form`; the form's Reset puts `defaultValue` back, in the field's text too.

```tsx
import * as React from "react"
import { Button } from "@booleanpress/ui/button"
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerWithForm() {
  const [sent, setSent] = React.useState("")

  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      {/* The picker sits outside the form and belongs to it through `form`. */}
      <div className="flex flex-col gap-2">
        <Label htmlFor="schedule-day">Send on</Label>
        <DatePicker id="schedule-day" name="day" form="schedule" defaultValue={new Date(2026, 9, 5)} />
      </div>
      <form
        id="schedule"
        className="flex gap-2 rounded-md border p-3"
        onSubmit={(event) => {
          event.preventDefault()
          setSent([...new FormData(event.currentTarget)].map(([key, value]) => `${key} = ${value}`).join(", "))
        }}
      >
        <Button type="submit">Submit</Button>
        <Button type="reset" variant="outline">
          Reset
        </Button>
      </form>
      <pre className="overflow-x-auto rounded-md bg-muted px-3 py-2 font-mono text-xs whitespace-pre-wrap text-foreground">{sent ? `Sent: ${sent}` : "Press Submit to see what the form sends."}</pre>
    </div>
  )
}
```

### Another locale

`format="d MMMM yyyy"` in Bengali and Hindi: the month names the locale writes are read back as typed.

```tsx
import { BooleanUIProvider } from "@booleanpress/ui/provider"
import { DatePicker } from "@booleanpress/ui/date-picker"
import { Label } from "@booleanpress/ui/label"

export default function DatePickerAnotherLocale() {
  return (
    <div className="flex flex-col gap-4">
      <BooleanUIProvider locale="bn-BD">
        <div className="flex flex-col gap-2">
          <Label htmlFor="send-on-bn">পাঠানোর তারিখ</Label>
          <DatePicker id="send-on-bn" format="d MMMM yyyy" defaultValue={new Date(2026, 9, 5)} />
        </div>
      </BooleanUIProvider>
      <BooleanUIProvider locale="hi-IN">
        <div className="flex flex-col gap-2">
          <Label htmlFor="send-on-hi">भेजने की तारीख</Label>
          <DatePicker id="send-on-hi" format="d MMMM yyyy" defaultValue={new Date(2026, 9, 5)} />
        </div>
      </BooleanUIProvider>
    </div>
  )
}
```

## Accessibility

**Semantics.** A native text `input`, and a `button` named "Choose date" with `aria-haspopup="dialog"` and `aria-expanded`. The popup is a non-modal `dialog` named "Choose date" holding the `Calendar` grid (see Calendar), or a `grid` of month or year buttons with `aria-selected` on the chosen cell, and, with `showTime`, `spinbutton`s for hours, minutes and AM/PM. With `trigger="field"` the input itself is a `combobox` with `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls` (the APG date picker combobox).

**Labels.** Name the field with a `Label htmlFor` or `aria-label`. The button, the popup and the time spinbuttons are named by the provider strings `chooseDate`, `hour`, `minute` and `dayPeriod`; Today and Clear by `today` and `clear`; the year arrows by `previousYear`, `nextYear`, `previousYears` and `nextYears`. Give `inline` an `aria-label`.

**Focus.** The button (or Alt+ArrowDown) opens the popup and moves focus to the chosen day, else today, else the 1st. A click in the field with `trigger="field"` opens it and leaves focus in the field, so typing goes on. Tab moves through the popup and wraps at its ends. Choosing a day, Escape, Today and Clear close it and return focus to the field; a click outside closes it and leaves focus where the click put it.

**Known limits.**

- The calendar, like the field, follows the provider's `locale` (see Calendar); without one, the calendar's day names are react-day-picker's English.
- A typed date is read on blur and on Enter, not as each key lands; a screen reader hears the invalid state when focus returns to the field.
- `showTime` is for single dates in the day view.
- The dot of a day template is visual: put its meaning in the day's name with `calendarProps.labels.labelDayButton`, as the example does.

### Keyboard

| Key | Behaviour |
| --- | --- |
| Alt + ↓ | In the field: opens the calendar and moves focus to the chosen day, or today. |
| Enter | In the field: reads the typed text now. On the calendar button: opens the calendar. |
| Escape | Closes the calendar and returns focus to the field. |
| Tab | Moves to the next control of the popup; from the last it wraps to the first. |
| → | In the month or year grid: the next month or year (the previous in right-to-left). The day grid's keys are the Calendar's. |
| ↓ | In the month or year grid: the cell below. On an hour, minute or AM/PM spinbutton: one less. |
| ↑ | In the month or year grid: the cell above. On a time spinbutton: one more. |
| Page Down | In the month or year grid: the same cell a year (or a decade) later. |
| Home | In the month or year grid: the first cell of the page. |
| End | In the month or year grid: the last cell of the page. |

## API

### DatePicker

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `calendarProps` | `DatePickerCalendarProps` |  | Props for the Calendar inside: `modifiers` and `modifiersClassNames` for a day template, `disabled` days, and more. |
| `className` | `string` |  | Classes for the root, which holds the field and its button: set its width here. |
| `clearable` | `boolean` |  | Shows a button that empties the field while it has a value. |
| `defaultMonth` | `Date` |  | The month the calendar opens on when nothing is chosen. Defaults to today's. |
| `defaultOpen` | `boolean` |  |  |
| `defaultValue` | `Date \| Date[] \| null` |  | The date (or dates) it starts with, uncontrolled. |
| `fluid` | `boolean` |  | Fills the width of its container. |
| `format` | `string` |  | A pattern for the field's text, such as `"dd/MM/yyyy"` or `"d MMM yyyy"`. Defaults to the locale's numeric date. |
| `hourCycle` | `12 \| 24` |  | 12 (with AM/PM) or 24, for `showTime`. Defaults to the locale's. |
| `icon` | `ReactNode` |  | Replaces the calendar icon of the button. |
| `inline` | `boolean` |  | Shows the calendar on the page, with no field and no popup. |
| `max` | `Date` |  | The latest date. With `showTime`, a `max` that has a time (any but midnight) also bounds the time on its day. |
| `min` | `Date` |  | The earliest date: days before it cannot be chosen, and typing one marks the field invalid. With `showTime`, a `min` that has a time (any but midnight) also bounds the time on its day. |
| `mode` | `"single" \| "multiple"` |  | `single` chooses one date. `multiple` chooses several dates; the field lists them with commas (semicolons when a date's text has a comma). |
| `numberOfMonths` | `number` |  | How many months the calendar shows side by side. |
| `onOpenChange` | `((open: boolean) => void)` |  |  |
| `onValueChange` | `((value: Date \| null) => void) \| ((value: Date[]) => void)` |  | Called with the new date, or null when the field is emptied or its text is not a date. |
| `open` | `boolean` |  | Whether the calendar is open, when you control it. |
| `showButtonBar` | `boolean` |  | Shows Today and Clear buttons under the calendar. |
| `showTime` | `boolean` |  | Adds hours and minutes under the calendar. Single mode only. |
| `size` | `"default" \| "sm" \| "lg"` |  | 28, 35 or 42 px tall. Defaults to the provider's `controlSize`. |
| `today` | `Date` |  | The date marked as today, and the one the Today button chooses. Defaults to the system date. |
| `trigger` | `"button" \| "icon" \| "field"` |  | What opens the calendar: `button`, a button joined to the field's end; `icon`, a calendar icon inside the field; `field`, a click on the field itself (and Alt+ArrowDown). |
| `value` | `Date \| Date[] \| null` |  | The chosen date, when you control it. |
| `variant` | `"default" \| "filled"` |  | `filled` fills the field grey. Defaults to the provider's `fieldVariant`. |
| `view` | `"month" \| "day" \| "year"` |  | What is chosen: a day, a month (the 1st of it) or a year (1 January). |

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="date-picker-panel"` (DatePicker), and `data-view`, `data-selected`, `data-size`.

**Provider strings:** `previousYear`, `previousYears`, `nextYear`, `nextYears`, `chooseYear`, `hour`, `minute`, `dayPeriod`, `today`, `clear`, `chooseDate` (`BooleanUIProvider`'s `strings`).

## Theming

The field is `Input`'s look; the joined button is the `--secondary` fill with the `--control` edge, darkening to `--secondary-hover` and `--secondary-active`. The popup is the popover surface with a 6 px radius and 10 px padding; chosen days, months and years are `--primary`.

| Token | Used for |
| --- | --- |
| `--accent` | background |
| `--accent-foreground` | text |
| `--card-foreground` | text |
| `--control` | border |
| `--control-hover` | text |
| `--foreground` | text |
| `--muted-foreground` | text |
| `--primary` | background |
| `--primary-foreground` | text |
| `--ring` | outline |
| `--secondary` | background |
| `--secondary-active` | background |
| `--secondary-foreground` | text |
| `--secondary-hover` | background |
| `--secondary-hover-foreground` | text |
| `--subtle` | background |
