# DateField

> A typed DD/MM/YYYY field with a calendar popover, min/max and a date-of-birth preset.

Category: Forms. Docs page: https://rime.mjsons.net/components/date-field

## Installation

```bash
npx shadcn@latest add KezLahd/rime/date-field
```

DateField accepts typing (DD/MM/YYYY, day first) or picking from Calendar. preset="dob" opens on a sensible year with a year dropdown, for dates decades back.

## Usage

```tsx
import { DateField } from "@/components/ui";

<Field label="Due date"><DateField value={due} onChange={setDue} /></Field>
```

## Props: DateField

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `IsoDate \| null` |  |  |
| `onChange` (required) | `(value: IsoDate \| null) => void` |  | null when cleared or when the typed text is not a usable date. |
| `isDateDisabled` | `(date: IsoDate) => boolean` |  |  |
| `min` | `IsoDate` |  |  |
| `max` | `IsoDate` |  |  |
| `defaultMonth` | `IsoDate` |  | Month the calendar opens on when empty. For a date of birth, pass ~40 years ago (or use preset="dob"). |
| `preset` | `"dob"` |  | dob: a date of birth, set up as a sign-up form does it. Month and Year dropdowns in the calendar, years 1920 to this year, no future dates, and an empty field opens 40 years back. Any prop passed explicitly wins over the preset. |
| `captionLayout` | `"label" \| "dropdown"` |  | The calendar's caption. dropdown gives Month ▾ and Year ▾ quick pickers; see Calendar. |
| `fromYear` | `number` |  | Year span of the Year picker (dropdown caption only). |
| `toYear` | `number` |  |  |
| `invalid` | `boolean` |  |  |
| `disabled` | `boolean` |  |  |
| `required` | `boolean` |  |  |
| `id` | `string` |  |  |
| `name` | `string` |  |  |
| `placeholder` | `string` | `"DD/MM/YYYY"` |  |
| `aria-label` | `string` |  |  |
| `className` | `string` |  |  |
| `inline` | `boolean` | `false` | Show the calendar open, in place under the field: no portal, no focus move, no outside-click listener. For docs and thumbnails. |

## Props: Calendar

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `isDateDisabled` | `(date: IsoDate) => boolean` |  | Days that cannot be picked. Still focusable, so keyboard users can pass over them. |
| `min` | `IsoDate` |  |  |
| `max` | `IsoDate` |  |  |
| `defaultMonth` | `IsoDate` |  | Month shown first when there is no value. Defaults to today's month. |
| `autoFocus` | `boolean` |  | Moves focus into the grid on mount (used by DateField's popup). |
| `numberOfMonths` | `1 \| 2 \| 1 \| 2` | `1` | Months side by side. Navigation moves them together. |
| `captionLayout` | `"label" \| "dropdown" \| "label" \| "dropdown"` | `"label"` | label: the month and year as a heading, with month and year arrows. dropdown: Month ▾ and Year ▾ buttons that open quick pickers, for dates years away such as a date of birth (a sign-up form). Month arrows stay. Two-month calendars always use labels. |
| `fromYear` | `number` |  | First and last years the Year picker lists (dropdown only). Default to min's and max's years, else 100 years back and 10 on. They also bound the arrows and keyboard paging, so the grid never shows a year the picker lacks. |
| `toYear` | `number` |  |  |
| `className` | `string` |  |  |
| `value` (required) | `IsoDate \| null \| never` |  |  |
| `onChange` (required) | `(value: IsoDate) => void \| never` |  |  |
| `range` | `never \| DateRange` |  | Range mode. After a start is picked, hovering or arrowing previews the range. |
| `onRangeChange` | `never \| (range: DateRange) => void` |  |  |

## Examples

### Open

inline shows the calendar open under the field, in place.

```tsx
<DateField inline aria-label="Due date" value={due} onChange={setDue} />
```

### Default

```tsx
<DateField value={date} onChange={setDate} min="2026-01-01" />
```

## Theme tokens

Per-component tokens. Undeclared by default: the default below is the fallback the CSS uses. Set any of them at :root or on a wrapper to restyle every instance.

| Token | Default |
| --- | --- |
| `--field-focus-border` | `rgba(var(--rgb-brand), 0.7)` / `var(--focus-color)` |
| `--field-focus-ring` | `var(--focus-halo)` |

System tokens it reads: `--brand`, `--brand-gradient`, `--danger-ink`, `--dur`, `--dur-fast`, `--ease`, `--ease-out`, `--focus-color`, `--focus-halo`, `--focus-ring`, `--font-body`, `--glow-sm`, `--ink-body`, `--ink-brand`, `--ink-disabled`, `--ink-heading`, `--ink-inverse`, `--ink-muted`, `--line`, `--line-brand`, `--line-brand-strong`, `--r-xl`, `--r-xs`, `--rgb-brand`, `--rgb-brand-deep`, `--shadow-float`, `--surface-solid`, `--text-micro`, `--wash-active`, `--wash-hover`, `--wash-strong`, `--wash-subtle`, `--z-popover`.

## Accessibility

- The calendar is a grid with arrow-key navigation; Esc returns focus to the field.

## Do

- Always let people type a date of birth; never force a calendar for it.

## Don't

- Don't mix date orders within one product.
