# DatePicker

**Maturity:** experimental

Let a person choose one calendar date from a month grid — the "pick a due date" / "pick a birthday" control.

## Import

```tsx
import { DatePicker } from "@liquidify/react/date-picker"
```

## Props

- `calendar`: `string | undefined`; default `derived from `locale`, else `"gregory"``. `Intl` calendar id for name/numeral formatting only (the grid stays Gregorian in v1; alternate-calendar arithmetic is out of scope). When omitted, derived from `locale`, else `"gregory"`.
- `className`: `string | undefined`. Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean.
- `defaultValue`: `Date | undefined`; default `today's date, resolved once at mount`. Uncontrolled seed (ignored once {@link DatePickerProps.value} is supplied). For deterministic SSR, supply this; otherwise the seed resolves to the client's "today" at mount.
- `disabled`: `boolean | undefined`. When `true`, dims the control, sets `aria-disabled`/`data-disabled` on the root, removes every day cell from the tab order, and natively disables both chevron `<button>`s.
- `firstDayOfWeek`: `0 | 3 | 2 | 5 | 1 | 4 | 6 | undefined`; default `derived from `locale`, else `0` (Sunday)`. First weekday column, `0` (Sunday) … `6` (Saturday) — reorders the weekday headers and the leading-pad offset. When omitted, derived from `locale` (`Intl.Locale#getWeekInfo`), else `0`. The analogue of `Calendar.firstWeekday`.
- `labelProps`: `LabelProps | undefined`. Per-instance typography overrides for the header's month-year label, threaded verbatim into the internal `DisclosureGroup`'s own `labelProps` (ADR-0028). Omitted → DisclosureGroup's headline/bold/primary defaults.
- `locale`: `string | undefined`; default ``"en-US"``. BCP-47 locale for `Intl` month/weekday names and day numerals. Omitting it reproduces the historic `en-US` output exactly.
- `max`: `Date | undefined`. Latest selectable day (inclusive) — the upper-bound counterpart of {@link DatePickerProps.min}; `max`'s year raises the year wheel's range.
- `min`: `Date | undefined`. Earliest selectable day (inclusive). Days before `min` are `aria-disabled` + `data-disabled`, non-selectable, and excluded from the roving tabindex; `min`'s year lowers the year wheel's range. Invalid → ignored (dev warning). The web analogue of `DatePicker(_:in:)`'s lower bound.
- `onChange`: `((next: Date) => void) | undefined`. Fires with the next selected `Date` whenever a day is committed (`Enter`/`Space` or tap/click), in both modes. Never fires for `visibleMonth` navigation, nor for a disabled or out-of-`[min,max]` day.
- `ref`: `Ref<HTMLDivElement> | undefined`. Forwarded ref to the rendered element — the component's native root (or the merged child element on a component that opts into {@link SlottableProps}). Generic over `E` (charter `docs/api-conventions.md` §4 rule 1, default `HTMLElement`) so a component with a polymorphic root (Link's navigation / action mode union, Text's `as`) can instantiate the concrete element per variant instead of leaving every consumer with a widened `Ref<HTMLElement>`. A component that does not narrow simply inherits the `HTMLElement` default — source-compatible with every pre-existing non-generic `extends ControlProps`.
- `value`: `Date | undefined`. Controlled selection. Passing this switches DatePicker to controlled mode (consumer owns it via {@link DatePickerProps.onChange}). Continuous-domain `Date`, not the catalog's usual `string` — a documented narrowing of the ADR-0015 shape. An Invalid Date is rejected (dev warning) and the control falls back to its deterministic default.
- `variant`: `"graphical" | undefined`; default ``"graphical"``. Visual style — a single-member, forward-compatible axis. `"graphical"` is the default and only member in v1; `.compact`/`.wheel` are Out of scope.

## SwiftUI mapping

- ``init(_ titleKey:, selection: Binding<Date>)` / `init<S>(_ title:, selection: Binding<Date>)`` → ``value?: Date` + `onChange?: (next: Date) => void`` (direct): `Binding<Date>` splits into controlled `value` + `onChange` (ADR-0015 shape). **Deviates from ADR-0015's string-keyed convention**: dates are a continuous domain, not an enumerable option set (unlike Picker/Segmented's `value: string`), so the controlled value is a `Date` object, not a string key. The hidden `titleKey`/`title` becomes the accessible name via `aria-label`/`aria-labelledby` (no visible title renders in `.graphical`).
- ``@State private var date = Date()` seed` → ``defaultValue?: Date`` (direct): Uncontrolled seed. When neither `value` nor `defaultValue` is supplied, the default is today's date at runtime; Storybook stories pin a fixed epoch (September 23, 2021) for deterministic visual-parity grading against the reference screenshot.
- ``.datePickerStyle(.graphical)`` → ``variant?: "graphical"` (default, only member)` (direct): The whole variant axis in v1.
- ``.datePickerStyle(.compact)`` → `— (Out of scope)` (direct): Compact text-field-with-popover style; deferred.
- ``.datePickerStyle(.wheel)`` → `— (Out of scope)` (direct): Scrolling wheel style; deferred.
- ``init(_:selection:in: ClosedRange<Date>, displayedComponents:)`` → ``min?: Date` / `max?: Date`` (direct): **In scope** (Phase 3). Days outside `[min,max]` are `aria-disabled` + `data-disabled`, non-selectable, and excluded from the roving tabindex (the reference screenshot's greyed 24–30 behaviour). The year wheel's range derives from `[min,max]` (default `1900–2100`). An Invalid Date bound is ignored with a dev warning.
- ``displayedComponents: .date` (default)` → `(implicit — no prop)` (direct): Date-only; the only supported shape in v1.
- ``displayedComponents: .hourAndMinute` / `[.date, .hourAndMinute]`` → `— (Out of scope)` (direct): No time-of-day wheel in v1.
- ``Calendar.firstWeekday`` → ``firstDayOfWeek?: 0..6`` (direct): First weekday column (0 = Sunday … 6 = Saturday); reorders the weekday headers and the leading-pad offset. Default derived from `locale` (`Intl.Locale#getWeekInfo`), else `0` (Sunday).
- ``Text.environment(\.locale)` / `Environment(\.locale)`` → ``locale?: string`` (direct): BCP-47 locale for `Intl` month/weekday names + day numerals. Default `"en-US"` (omitting it reproduces the historic English output byte-for-byte).
- ``environment(\.calendar)`` → ``calendar?: string`` (direct): `Intl` calendar id for name/numeral **formatting only**; the grid stays Gregorian in v1 (alternate-calendar month arithmetic is a scoped follow-up — see Out of scope). Default derived from `locale`, else `"gregory"`.
- ``.datePickerStyle`'s label-view typography` → ``labelProps?: LabelProps`` (direct): Header month/year label typography overrides (ADR-0028), threaded verbatim into the internal `DisclosureGroup`'s own `labelProps`; omitted → DisclosureGroup's headline/bold/primary defaults govern.
- ``.disabled(true)`` → ``disabled?: boolean`` (direct): Whole-control disable: `aria-disabled` + `data-disabled` + `pointer-events: none` + every day cell removed from the tab order (mirrors Segmented/Picker).
- ``@ViewBuilder label` (hidden in `.graphical`)` → ``aria-label` / `aria-labelledby` (required)` (direct): SwiftUI's `.graphical` style never shows the label visibly; this component renders no group text of its own either — an accessible name **must** be supplied by the caller.
- `*(no analogue — web-only)*` → ``ref?: Ref<HTMLDivElement>`` (web-only): `DatePickerProps extends ControlProps<HTMLDivElement>` — the concrete `role="group"` root element (charter §4 rule 1/2; `useForwardedRef<HTMLDivElement>`), not a bare `Ref<HTMLElement>`.
- `*(no analogue — web-only)*` → `no `size` / `material` / `perf` prop` (web-only): Not sizable (ADR-0018/0023); the frosted card material is fixed internally, never a consumer knob.
- `*(no analogue — web-only)*` → ``visibleMonth` — **internal state, not a prop**` (web-only): The displayed year+month. Prev/next chevrons and `PageUp`/`PageDown` move it without changing `value`; selecting a day sets `value` and, if the day is outside `visibleMonth`, syncs `visibleMonth` to match. See Interaction.
- ``GraphicalDatePickerStyle`'s header disclosure (month/year → wheel morph)` → ``DisclosureGroup` (`../disclosure-group`) wrapping two `Picker variant="wheel"` selectors (`../picker`)` (renamed): **In scope** — a composition of two already-built catalog components, not a new prop: `DatePickerProps` gains no disclosure-open / month / year prop. The disclosure's expanded/collapsed state is internal UI state, exactly like `visibleMonth`.
