# Date picker API

> Props, slots, labels and styling of the slotsmith Date picker.

## Slots

`DatePicker` has 15 slots. Pass a replacement for any of them in `components`; every slot you leave out keeps its fallback. Element slots receive DOM props, so a library's own element fits as it is; widget slots receive named props and usually need a few lines of adapter.

| Slot | Kind | Props type | Description |
| --- | --- | --- | --- |
| `Root` | element | `DpRootProps` | Element part: the box around the whole control. Carries `data-mode`, `data-open`, `data-disabled`, `data-invalid` and `data-empty`, and is the element the outside-click check measures against. |
| `Trigger` | element | `DpTriggerProps` | Element part: the control that opens the calendar. It carries `role="combobox"` with `aria-haspopup="dialog"`, and is a `<div>` rather than a `<button>` because the clear control inside it is a button and HTML forbids nesting them. With `invalid` set it carries `aria-invalid="true"` and `data-invalid`. A skin built on a library component with an error prop (MUI's `error`, Ant Design's `status`) reads either attribute and passes the prop on. |
| `Input` | element | `DpInputProps` | Element part: the text field of the `"input"` trigger, inside the `Trigger` frame. It carries `role="combobox"` with `aria-haspopup="dialog"`, its `value` and `placeholder` (the locale's date order, such as `MM/DD/YYYY`), and the typing and keyboard handlers, so render a real `<input>` and forward every prop and the `ref`. The frame holds the border, so a skin usually renders its library's bare input here (MUI's `InputBase`, a borderless Ant Design `Input`). |
| `Toggle` | element | `DpToggleProps` | Element part: the calendar button beside the text field. It carries `aria-haspopup="dialog"`, `aria-expanded`, its accessible name (`labels.openCalendar`) and `data-open`; its child is the `Icon`. |
| `Value` | widget | `DpValueProps` | Widget part: the trigger's text. |
| `Icon` | widget | `DpIconProps` | Widget part: the calendar affordance on the trigger. |
| `Clear` | widget | `DpClearProps` | Widget part: empties the selection. Render a real button, and stop the click from reaching the trigger, or clearing will also open the calendar. |
| `Popup` | element | `DpPopupProps` | Element part: the floating surface holding the calendar. It carries `role="dialog"` and the position style. Replace it to use a library's own popover, in which case its positioning replaces the built-in one. |
| `Calendar` | element | `DpCalendarProps` | Element part: the month grid, `role="grid"`. It owns the keyboard path, so it must forward `onKeyDown`. |
| `Caption` | widget | `DpCaptionProps` | Widget part: the month and year above the grid, and the way to change them. Changes are kept inside `minDate` / `maxDate`. |
| `Nav` | widget | `DpNavProps` | Widget part: a previous or next control. |
| `Weekday` | widget | `DpWeekdayProps` | Widget part: one column header. It is wrapped in a `role="columnheader"` element named with the full weekday, so a one-letter label is still read out as a day. |
| `Day` | element | `DpDayProps` | Element part: one day cell. Carries `data-date`, `data-today`, `data-outside`, `data-weekend`, `data-selected`, `data-disabled`, `data-focused`, `data-in-range`, `data-in-preview`, `data-range-start` and `data-range-end`, and a roving `tabIndex`. Button attributes rather than the generic ones, because a day is a button. A blocked day gets `aria-disabled` and `data-disabled` but never the `disabled` attribute: the WAI-ARIA pattern keeps it focusable, so the arrow keys can walk across it and a screen reader can announce it. |
| `DayContent` | widget | `DpDayContentProps` | Widget part: what a day cell contains. This is where prices, dots or badges belong: the `Day` part is an element part and only receives DOM props, so the day itself is handed to this part instead. |
| `Footer` | widget | `DpFooterProps` | Widget part: the row under the grid. |

## Props

Every prop of `DatePicker`, grouped as in the library's documentation. Controlled state comes in pairs: pass a value with its change handler, or only the default to leave the state inside the component.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | `"single"` |  | One date. The default mode. |
| `mode` | `"multiple"` |  | Any number of dates, toggled one click at a time. |
| `mode` | `"range"` |  | A start and an end: the first click sets the start, the second the end. |
| `value` | `ISODate \| null` |  | The picked date, or `null` for nothing. |
| `value` | `ISODate[]` |  | The picked dates. |
| `value` | `DateRange \| null` |  | The picked range. Either end may be missing while picking. |
| `defaultValue` | `ISODate \| null` |  | The picked date while uncontrolled. |
| `defaultValue` | `ISODate[]` |  | The picked dates while uncontrolled. |
| `defaultValue` | `DateRange \| null` |  | The picked range while uncontrolled. |
| `onChange` | `(value: ISODate \| null) => void` |  | The new date, or `null` when cleared. |
| `onChange` | `(value: ISODate[]) => void` |  | The new dates, sorted, always an array. |
| `onChange` | `(value: DateRange \| null) => void` |  | The new range, or `null` when cleared. |
| `clearable` | `boolean` | `true` | Whether the clear control is offered. Defaults to `true`. |
| `closeOnSelect` | `boolean` | ``true` in single mode and `false` otherwise` | Whether picking closes the popup. Defaults to `true` in single mode and `false` otherwise. |
| `valueFormat` | `DateValueFormat` | ``"iso-datetime"`; `"YYYY-MM-DD"` keeps the calendar day alone` | How the days the picker exchanges with you are written: `value`, `defaultValue`, `onChange`, `minDate`, `maxDate`, the presets and the day `disabledDates` is asked about. A pattern such as `"DD/MM/YYYY"`, `"iso-datetime"` for the instant the day starts in `timeZone`, or a codec of your own. Defaults to `"iso-datetime"`; `"YYYY-MM-DD"` keeps the calendar day alone. |
| `timeZone` | `string` | `the browser's own` | The time zone the calendar works in: an IANA name such as `"Asia/Tokyo"`, `"UTC"`, or an offset such as `"+09:00"`. Today is today in that zone, and with `valueFormat: "iso-datetime"` (the default) a picked day is the instant it starts there and an instant is read as the day it falls on there. Defaults to the browser's own. |
| `name` | `string` |  | The form field name. The root then holds a hidden input of that name per picked day, written as `valueFormat` says, so a plain HTML form submits the value: one day, each of several days, or a range's start then end. |
| `locale` | `LocaleInput` | `the provider's, then `en-US`` | The language: a locale object, or a BCP 47 tag. A tag formats dates and names with `Intl` as always, and takes its labels from the pack of that language given to `SlotsmithProvider`, if there is one. Defaults to the provider's, then `en-US`. |
| `weekStartsOn` | `WeekStart` | `the locale's own first day` | The first column of the grid. Defaults to the locale's own first day. |
| `weekdayFormat` | `"narrow" \| "short" \| "long"` | `narrow` | How long the column headers are. `narrow` is a single character in every language, and is the only form that reliably fits a square day column. Defaults to `narrow`. |
| `presets` | `DatePickerPreset[]` |  | Shortcuts shown under the grid. A date preset in range mode picks that one day. |
| `minDate` | `ISODate` |  | Nothing before this date can be picked, and the calendar does not page past it. |
| `maxDate` | `ISODate` |  | Nothing after this date can be picked, and the calendar does not page past it. |
| `disabledDates` | `(date: ISODate) => boolean` |  | Blocks individual dates — weekends, holidays, days already booked. They stay focusable. |
| `open` | `boolean` |  | Whether the popup is open. |
| `defaultOpen` | `boolean` |  | Whether the popup starts open. |
| `onOpenChange` | `(open: boolean) => void` |  | Told when the popup opens or closes. |
| `disabled` | `boolean` |  | Disables the whole control. Separate from `disabledDates`. |
| `invalid` | `boolean` |  | The value failed validation. Sets `aria-invalid` on the combobox and `data-invalid` on the root and trigger, so a skin can show its error style. Pair it with `aria-errormessage` or `aria-describedby` pointing at the message. Default `false`. |
| `onBlur` | `(event: FocusEvent<HTMLElement>) => void` |  | Told when focus leaves the whole control, for form libraries. |
| `trigger` | `DatePickerTriggerKind` |  | What opens the calendar. `"input"`, the default, is a text field the date can be typed into, in the locale's numeric order (`MM/DD/YYYY` in en-US), with a calendar button beside it. `"button"` is one control that shows the formatted value and opens the calendar when pressed. |
| `openOnFocus` | `boolean` | `false` | With the `"input"` trigger, focusing or clicking the text field opens the calendar too. Defaults to `false`. |
| `placeholder` | `string` |  | Shown when nothing is picked. With the `"button"` trigger it defaults to `labels.placeholder`; the `"input"` trigger's text field shows the date order to type in, such as `MM/DD/YYYY`. |
| `format` | `Intl.DateTimeFormatOptions` | `{ day: "numeric", month: "short", year: "numeric" }` | How the trigger formats a date. Defaults to `{ day: "numeric", month: "short", year: "numeric" }`. |
| `placement` | `PopupPlacement` | `bottom` | Which side of the trigger the popup prefers. Defaults to `bottom`. |
| `popupOffset` | `number` | `4` | Gap between trigger and popup, in pixels. Defaults to 4. |
| `portal` | `PortalTarget` | `false` | Renders the popup in a portal: `true` for `document.body`, or an element of your own. It then escapes any ancestor's `overflow`, `transform` or stacking context, and still counts as part of the control. Set the `--ss-*` tokens and the dark class on `:root` or on that element, which the popup then inherits from. Defaults to `false`. |
| `components` | `Partial<DatePickerComponents>` |  | Replace any part; the rest fall back to `SlotsmithProvider`'s `components.datePicker`, then to the built-in ones. |
| `labels` | `Partial<DatePickerLabels>` |  | Override any string. Wins over `locale`. |
| `slotProps` | `DatePickerSlotProps` |  | Extra DOM props for the element parts. |

## Labels & locales

Every string `DatePicker` shows or announces, with its English default. Pass `labels` to change some of them for one component, or `locale` to switch every string to another language. The demo below does both.

| Label | Type | Default | Description |
| --- | --- | --- | --- |
| `placeholder` | `string` | `"Pick a date"` | Trigger text when nothing is picked, and the trigger's default accessible name. |
| `clear` | `string` | `"Clear date"` | Accessible name for the clear control. |
| `openCalendar` | `string` | `"Open calendar"` | Accessible name for the calendar button beside the text field. |
| `previous` | `string` | `"Previous month"` | Accessible name for the previous-month control. |
| `next` | `string` | `"Next month"` | Accessible name for the next-month control. |
| `today` | `string` | `"Go to today"` | Label for the control that jumps to today's month without picking it — a navigation verb, not the bare word "today", so it reads apart from the presets. |
| `month` | `string` | `"Month"` | Accessible name for the month dropdown. |
| `year` | `string` | `"Year"` | Accessible name for the year dropdown. |
| `dialog` | `string` | `"Choose a date"` | Accessible name for the popup. |
| `count` | `(count: number) => string` | `(count) => `${count} dates selected`` | Multiple mode: how the trigger summarises more than two dates. |
| `rangeStart` | `(from: string) => string` | `(from) => `${from} — …`` | Range mode, when only the start is picked. Receives the formatted start. |
| `range` | `(from: string, to: string) => string` | `(from, to) => `${from} — ${to}`` | Range mode, when both ends are picked. Receives both formatted ends. |

## Styling

The class names the fallbacks of `DatePicker` put on their elements, and the tokens its stylesheet reads. Set a token on the component, or on any element around it, to restyle it; target a class only for what no token covers.

Classes: `sdp`, `sdp__header`, `sdp__cell`, `sdp__weekdays`, `sdp__columnheader`, `sdp__week`, `sdp__chevron`, `sdp__icon`, `sdp__today-icon`, `sdp__clear`, `sdp__caption`, `sdp__select`, `sdp__nav`, `sdp__weekday`, `sdp__footer`, `sdp__preset`, `sdp__preset--today`, `sdp__today`, `sdp__trigger`, `sdp__input`, `sdp__toggle`, `sdp__value`, `sdp__value--empty`, `sdp__popup`, `sdp__calendar`, `sdp__day`

Tokens: `--sdp-accent`, `--sdp-border`, `--sdp-cell`, `--sdp-danger`, `--sdp-font-size`, `--sdp-gap`, `--sdp-hover`, `--sdp-muted`, `--sdp-on-accent`, `--sdp-radius`, `--sdp-surface`, `--sdp-text`, `--ss-accent`, `--ss-border`, `--ss-danger`, `--ss-font-size`, `--ss-hover`, `--ss-muted`, `--ss-on-accent`, `--ss-radius`, `--ss-surface`, `--ss-text`
