# Date field

> A single calendar date, typed in whatever form comes to hand or stepped with the keys.

A date field takes a single calendar date, typed in whatever form comes to hand,
stepped with the keys, or chosen from a calendar.

## When to use

Use a date field when people know the date — a birthday, a deadline they were
given — and typing is quickest. Typing stays the first way to give a date; the
calendar beside the field is for when the choice depends on the calendar itself:
which day of the week, how far from another date.

## Best practices

**Accept dates as people write them** rather than demanding a format. A field
that reads `10/20`, `oct 20` and `2026-10-20` alike needs no instructions; see
[Dates](https://ui.publictheta.com/foundations/date-type.md) for what should be read.

**Show the date in full once it is committed**, in the locale's form, so a partial
or ambiguous entry is confirmed back to the person who typed it. Where the day
of the week matters, show it with the date.

**State a range only where there is one.** A date outside the range stays as
typed and is marked invalid, with whether it is too early or too late, so people
see the limit and no date is chosen for them.

**Label the field with what the date is**, not with a format.

**Offer a calendar, and keep it out of the way.** A button at the end of the
field opens it; nothing opens by itself when the field takes focus, so typing is
never interrupted. Name the button for the field it belongs to — a form with two
dates has two such buttons.

**Open the calendar where the field is.** On the field's date; on a date that is
typed and not yet committed; with neither, on today. See
[Calendars](https://ui.publictheta.com/foundations/calendar.md) for how the month is laid out and moved
through.

**Choosing a day is giving the date.** It commits at once, as Enter does on a
typed date, and closes the calendar; whatever was in the field — text half
typed, text that could not be read — is replaced. Moving through the days
chooses nothing, and Escape or a press outside leaves the field as it was.

**Put people back where the date is.** After a day is chosen, focus returns to
the field, which now reads the date out and can be adjusted with the keys. After
the calendar is dismissed, focus returns to the button that opened it.

**A field that cannot be changed offers no calendar.** Read-only or disabled, its
button is unavailable and is passed over by Tab.

**Send a form the date, not the text.** A named field submits the date in the
one form every system shares, `2026-10-13`, whatever the locale shows. The
calendar's button never submits the form.

## Interaction

| Intent                 | Pointer                        | Keyboard                                                   | Touch                        |
| ---------------------- | ------------------------------ | ---------------------------------------------------------- | ---------------------------- |
| Give a date            | —                              | Type it; Return, or leaving the field, commits             | Type it                      |
| Step the date          | —                              | Up and Down Arrow; Shift for a week; Page Up and Down      | —                            |
| Open the calendar      | Click the button               | Alt-Down Arrow in the field; Return or Space on the button | Tap the button               |
| Move through the days  | —                              | Arrow keys, Home, End, Page Up and Down, with Shift        | —                            |
| Show another month     | Click the buttons by the title | Page Up, Page Down                                         | Tap the buttons by the title |
| Choose a day           | Click it                       | Return or Space                                            | Tap it                       |
| Close without choosing | Click outside, or the button   | Escape; Tab also moves on                                  | Tap outside                  |

## Platform considerations

**Keyboard.** Up and Down Arrow move the date by a day, or move the month or the
year when the caret is in that part; Shift moves by a week; Page Up and Page Down
by a month. With a part selected, Left and Right Arrow move to the part beside
it. An empty field's first step is to today. Alt with Down Arrow opens the
calendar, as it opens a platform's own date fields. Editing follows
[Editing values](https://ui.publictheta.com/foundations/use-field.md).

**Pointer.** The calendar opens beneath the field as a popover, aligned with the
field's start, and a press outside it both closes it and acts on what was
pressed.

**Touch.** The field and its button are at least 44 CSS pixels tall. The button
opens the platform's own date picker, given the field's date and its range; the
date chosen there becomes the field's date. Where the platform has no picker to
show, the calendar opens instead — on a phone, as a sheet along the bottom edge.
After a day is chosen focus goes to the button, not the field, so the keyboard
does not rise over the date just chosen.

**Assistive technology.** The field is a text field with its name; the date is
its text, and an entry that is not a date marks it invalid. The button states
that it opens a dialog and whether it is open; the calendar is a dialog of the
same name, holding a grid of days named by its month. The platform's picker is
reached only through the button: it is not a second field to tab to or to have
read out.

See also [Calendars](https://ui.publictheta.com/foundations/calendar.md), [Dates](https://ui.publictheta.com/foundations/date-type.md) and
[Overlays](https://ui.publictheta.com/foundations/overlay.md).

## Behaviour

Each statement is verified by a test in Chromium, Chromium (touch), Firefox, WebKit, WebKit (touch), unless it names the environments it applies to or is marked open.

### Semantics

- Is a text field with its name, showing the date as the locale writes it.
- Has no accessibility violations, valid or invalid.
- Beside the field is a button named for it, which states that it opens a dialog and whether it is open.
- A field named by a label gives its button the label's name.
- The calendar is a dialog with the button's name, holding a grid of days named for its month.
- Each day is named in full, with its day of the week.
- The day chosen is marked as selected, and today as the current date.
- A change of month is said where focus does not say it: from a month button, not from a day, whose name has its month.
- The calendar has no accessibility violations.

### Typing and committing

- A date typed in any of the forms the type reads commits as that date.
- A Japanese field reads dates as Japanese writes them, full-width or not.
- A date beyond the range stays as typed, marked invalid, with which end it is beyond.
- A date that does not exist stays as typed, marked invalid.
- The field follows the document's language when given none.

### Stepping

- Up and Down Arrow move the date by a day.
- With the caret in the month or the year, they move that part.
- With Shift they move by a week; Page Up and Page Down move by a month.
- An empty field's first step is to today.
- With a part selected, the arrows to either side move from part to part.
- A caret at the very end of the text is in no part: the arrows step the day.

### Opening the calendar

- It opens on the field's date, with focus on that day.
- With no date in the field, it opens on today, with no day chosen.
- With a date typed and not yet committed, it opens on the date typed, which is then committed.
- A date beyond the range opens it on the end of the range nearest that date.
- Alt with Down Arrow in the field opens it, and does not step the date.
- Pressing the button again closes it. *(Chromium, Firefox, WebKit)*
- Escape closes it and chooses nothing; focus returns to the button.

### The month shown

- The days of the week head the columns, the locale's first day first.
- The days of the weekend are marked, as the locale counts its weekend.
- The days of the months either side fill out the weeks, marked as outside the month, and can be chosen.
- The buttons either side of the title show the month before and the month after.
- Where text reads right to left, the days and the month buttons run from the right.
- With a mouse, each day is at least 32 pixels square. *(Chromium, Firefox, WebKit)*

### Moving through the days

- The arrow keys move focus by a day and by a week, choosing nothing.
- Where text reads right to left, the arrows to either side are mirrored.
- Moving past the end of a month shows the month moved into.
- Home and End move to the first and last days of the week.
- Page Down and Page Up move by a month; with Shift, by a year.
- The grid is one stop in the Tab order: the day in focus, and no other. *(Chromium, Firefox, WebKit)*

### Choosing a day

- A click on a day chooses it: the field shows it, and the calendar closes.
- Enter chooses the day in focus, closes the calendar, and returns focus to the field.
- Space chooses the day in focus, as Enter does.
- Days outside the range are marked unavailable, and a click on one chooses nothing.
- The keys do not leave the range: a move past its end stops at the end.
- A month with no day in the range cannot be gone to: the button towards it is unavailable.
- Text that could not be read as a date is replaced by the day chosen, even the day the field held.

### Language

- In a Japanese field the calendar is in Japanese throughout, in Japanese word order.
- Days are numbered in the digits the locale writes.

### Forms

- With a name, a form is sent the date in the form all share, whatever the locale shows.
- A day chosen from the calendar is the date a form is sent.
- Neither the calendar's button nor choosing a day submits a form.
- A field that is disabled offers no calendar: its button cannot be pressed, and Tab passes it by.
- A field that is read-only offers no calendar either, by its button or by the keyboard.
- A calendar that is open closes when its field is disabled.

### Touch

- On a touchscreen, the field is at least 44 pixels tall. *(Chromium (touch), WebKit (touch))*
- On a touchscreen, the button is at least 44 pixels square. *(Chromium (touch), WebKit (touch))*
- On a touchscreen, the button opens the platform's own date picker, and no calendar. *(Chromium (touch), WebKit (touch))*
- The platform's picker is given the field's date and its range, and is kept from sight, from the keyboard and from assistive technology. *(Chromium (touch), WebKit (touch))*
- A date chosen in the platform's picker becomes the field's date, in place of anything typed. *(Chromium (touch), WebKit (touch))*
- What was being typed is read before the platform's picker is shown, so the picker opens on it. *(Chromium (touch), WebKit (touch))*
- The platform's picker is no field of a form the date field is in. *(Chromium (touch), WebKit (touch))*
- A field that is disabled or read-only has no platform picker. *(Chromium (touch), WebKit (touch))*
- Where the platform has no picker to show, the calendar opens instead, as a sheet along the bottom edge. *(Chromium (touch), WebKit (touch))*
- On a touchscreen, each day of the calendar is at least 44 pixels square, and a tap chooses it. *(Chromium (touch), WebKit (touch))*
- With a mouse there is no platform picker: the button opens the calendar. *(Chromium, Firefox, WebKit)*

## Reference implementation

React: `npx shadcn@latest add https://ui.publictheta.com/r/date-field.json`
