Skip to content

Date picker

A text field you can type a date into, with a calendar button for when you’d rather pick. Typing follows the page’s language; the calendar works from the keyboard.

CSS
css/components/datepicker.css
JS
js/components/datepicker.js

01 — Example

Date taken

Dates before 2020 and after today are struck through and can’t be picked.

For example, 10/5/2026

02 — Behavior

Script

NameDetail
TypingFollows the page’s language: lang="en" is month/day/year, lang="en-GB" day/month/year. ISO dates always work.
Calendar keysArrows move a day or a week, Home and End the week’s ends, Page Up and Page Down a month (with Shift, a year), Enter picks, Esc closes.
Optionsdata-min, data-max (a date or today), data-week-start, data-value.
ValueThe hidden input submits YYYY-MM-DD.
jewel:datechangeDetail { date, value }. API: el.jewelDatepicker.value, .open(), .close().

03 — Usage

Use it for

  • Dates people may not know by heart, or need in a range.

Avoid

  • Birthdays and other dates people know. Let them type.
  • Date ranges. It picks one date.

04 — Accessibility

Notes

  • The calendar is a grid people move through with the arrow keys; each day is read in full (“Monday 5 October 2026”).
  • An unreadable or out-of-range date shows as a field error.

05 — Without Jewel’s JS

What the script does

The script reads typed dates, builds the calendar, moves through it from the keyboard and writes the ISO date into the hidden input. The CSS reads only the markup below, so a framework can render the calendar from its own state and leave out datepicker.js and data-component.

StateSet thisNotes
Calendar<div class="datepicker__panel" role="dialog" aria-label="Choose date"> inside .datepickerInside it: .datepicker__head (.datepicker__nav--prev, .datepicker__month, .datepicker__nav--next), a table.datepicker__grid of .datepicker__day buttons, and .datepicker__foot.
OpenRemove hidden from the panel; aria-expanded="true" on .datepicker__toggleThe toggle turns accent. It also carries aria-haspopup="dialog" and aria-controls.
DaysOn .datepicker__day: aria-pressed="true" (chosen), aria-current="date" (today), disabled (out of range), .is-outside (another month)Give the focused day tabindex="0" and the rest tabindex="-1".
Placementdata-align="end" or data-side="top" on .datepickerCSS puts the panel under the field. The script lifts it into the top layer instead (popover, .is-floating, fixed top and left), so a content panel can’t clip it.
Value and errorsThe ISO date (YYYY-MM-DD) in the hidden input; setCustomValidity() on .datepicker__input for a date that can’t be read or is out of rangeShow it as a field error (see Text field).

Events and keyboard

  • The toggle, or Alt+↓ in the field, opens the calendar with focus on the chosen day, or today.
  • In the grid, ← → move a day, ↑ ↓ a week, Home and End to the week’s ends, Page Up and Page Down a month (with Shift, a year). Move focus and tabindex="0" together.
  • Picking a day, Today, or Esc closes it and returns focus to the toggle. A press outside, or focus leaving, closes it without moving focus. Clear closes it and focuses the field.
  • The script reads typed dates in the page language’s order (lang="en" month/day/year, lang="en-GB" day/month/year) or ISO, and tidies them on blur. jewel:datechange doesn’t fire without it.