Text and fields
Date Picker
Type a date, step it with the arrow keys, or pick it from a calendar.
import { DatePicker } from "macbetter/date-picker"
import { Field, FieldLabel } from "macbetter/field"
export default function Example() {
return (
<Field name="start-date">
<FieldLabel>Start date</FieldLabel>
<DatePicker defaultValue="2026-11-14" />
</Field>
)
}When to use it
Use a date picker for a date people might type or might want to see in a month: a start date, a due date, a booking. It's the macOS date field: month, day and year are separate parts you type into or step with the arrow keys, and the calendar button opens the month in a popover.
For a date people know by heart, like a birthday, a DateField alone is quicker. For a month that should stay on screen, use a Calendar.
Usage
import { DateField, DatePicker, TimeField } from "macbetter/date-picker"The value is an ISO date string like "2026-11-14", or null. Typing a whole date or picking a day both report it through onValueChange, and name submits it with a form. min, max and unavailable limit what can be picked, in the field and the calendar alike.
Compact
variant="compact" shows the date as a gray button that opens the calendar, as iOS does in a form row. Pair it with a TimeField for a date and a time.
import { DatePicker, TimeField } from "macbetter/date-picker"
export default function Example() {
return (
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span>Starts</span>
<DatePicker variant="compact" aria-label="Start date" defaultValue="2026-11-14" />
<TimeField aria-label="Start time" defaultValue="09:41" variant="filled" size="sm" />
</div>
)
}Date and time fields
DateField and TimeField are the segments on their own, without a calendar. granularity="minute" adds the time to a DateField.
import { DateField, TimeField } from "macbetter/date-picker"
import { Field, FieldGroup, FieldLabel } from "macbetter/field"
export default function Example() {
return (
<FieldGroup>
<Field name="birthday">
<FieldLabel>Birthday</FieldLabel>
<DateField defaultValue="1994-03-08" />
</Field>
<Field name="reminder">
<FieldLabel>Remind me at</FieldLabel>
<TimeField defaultValue="08:30" />
</Field>
<Field name="departure">
<FieldLabel>Departure</FieldLabel>
<DateField granularity="minute" defaultValue="2026-12-19T17:45" />
</Field>
</FieldGroup>
)
}Platform behavior
- macOS. The field is a bordered text field holding the parts. The part you're editing fills with the accent, as a macOS date field selects it. The calendar opens in a translucent popover aligned with the field, scaling in from it, with compact 26-point days.
- iPadOS and iOS. The field is filled gray with 17-point text. The compact button turns its text accent while its calendar is open. On a small touch screen the calendar arrives in a sheet from the bottom edge, with a grabber, full-size days and a swipe to dismiss.
- Every platform. The calendar shows six weeks so the popover never changes height as you page through months.
- Right to left. The parts keep the direction of the locale that formats them, so a US date reads 11/14/2026 even on a right-to-left page, and Arrow Right moves to the next part on screen. The calendar mirrors with the page.
Accessibility
- Each part is its own small input, named by its unit; the Field label names the group. Arrow Up and Down step a part, and typing fills it and moves on.
- The calendar button is named "Show calendar" and reports whether the calendar is open. Option-Down Arrow in the field opens it too. In the calendar, arrow keys move by day and week, Page Up and Down by month, and Escape closes it and returns focus.
- The compact button reads its label and the chosen date, like "Start date, Nov 14, 2026".
API
DatePicker
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "field" | "compact" | "field" | Segments with a calendar button, or a button showing the date. |
size | "sm" | "md" | "lg" | "md" | The field's height and type size. |
value | string | null | — | The ISO date, for a controlled picker. |
defaultValue | string | null | null | The date it starts with. |
onValueChange | (value, details) => void | — | Runs when a day is picked or typing completes a date. |
min | string | — | The earliest date that can be picked. |
max | string | — | The latest date that can be picked. |
unavailable | DateMatchers | — | Dates that can't be picked, like weekends or booked days. |
presentation | "popover" | "sheet" | "auto" | "auto" | A sheet on small touch screens, a popover elsewhere. |
months | number | 1 | How many months the calendar shows. |
locale | string | — | The language that orders and names the date parts, and sets their direction. Defaults to the browser's. |
name | string | — | The name the ISO date submits under. |
aria-label | string | — | Names the field or button when there's no Field label. |
DateField and TimeField
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "md" | Height and type size. |
variant | "auto" | "bordered" | "filled" | "auto" | Bordered on macOS and filled on touch, or a fixed look. |
granularity | "day" | "hour" | "minute" | "second" | — | The smallest unit shown. A DateField with a time unit shows the time too. |
hourCycle | 12 | 24 | — | The clock. Defaults to the locale's. |
value | string | null | — | An ISO date, date and time, or time. |
DatePickerTrigger and DatePickerContent are exported for your own layout; the content draws the compact calendar unless it has children.
Data attributes
| Attribute | On | Values |
|---|---|---|
data-variant, data-size | DatePicker | As above |
data-open | DatePicker, trigger | Present while the calendar is open |
data-presentation | Content | popover, sheet |
data-invalid | DatePicker, DateField | Present when the date can't be picked |
data-type | Segment | month, day, year, hour, minute, dayPeriod, literal |
data-empty | Segment | Present while the part is empty |
CSS variables
| Variable | macOS | iOS | What it sets |
|---|---|---|---|
--mb-date-field-segment-radius | 3px | 6px | The selected part's corners |
--mb-date-picker-popover-pad | 10px | 14px | Popover padding |
--mb-date-picker-button-bg | Tertiary fill | Tertiary fill | Compact button fill |