DatePicker

DatePicker combines a date-field trigger with Calendar. Choose it when one local date belongs in a compact form; choose Calendar when the date grid should remain visible in the layout.

Examples

A selected start date

Use an ISO YYYY-MM-DD string for a date-only initial value. The field formats the selected local date with its locale, and Calendar uses that same locale.

A delivery window

min and max pass through to the composed Calendar to prevent selection outside the permitted local-date window. Add hint when the constraint needs a short explanation.

Dates outside this delivery window are unavailable.

When to use

Use DatePicker for a single local date that should occupy one compact form field.

Reach for something else when:

  • Both a start and end date are required — use DateRangePicker, which selects the range in one Calendar and displays it on one trigger.
  • The date grid needs to remain visible — use Calendar directly.
  • A date is part of a time-slot choice — pair DatePicker with TimePicker rather than asking a date control to represent a time.

Accessibility

  • The trigger is a native button with its visible label associated through htmlFor. On desktop, it is passed to Popover, which supplies its dialog trigger semantics, Escape handling and focus return.
  • The composed Calendar supplies roving day focus, keyboard date navigation and locale-formatted date labels. Its selected date is passed back to the field and closes the picker.
  • error replaces hint and applies error styling to the trigger. disabled disables the trigger.
  • At viewports of 640px or less, the desktop Popover is replaced with a BottomSheet containing the Calendar. Its title comes from label or labels.sheetTitle; labels.close names its close button. This path is documented only because the stage cannot resize its viewport.
  • labels.calendar forwards translated Calendar controls, while locale formats both the trigger and Calendar dates.

Alpine mobile return focus

Configure the existing mobile BottomSheet with a return resolver. It resolves on close from that sheet's enclosing DatePicker root, so each currently rendered trigger remains local when multiple pickers share a page. This explicit destination uses focus({ preventScroll: true }) after a native pointer selection. Without it, BottomSheet only falls back to an eligible opener captured before a keyboard opening; an unprepared WebKit pointer opening must not rely on that fallback.

html
<div class="lyra-datepicker-root" x-data="lyraDatePicker({ locale: 'en-US' })">
  <div class="lyra-datepicker">
    <button class="lyra-input lyra-datepicker__btn" type="button" @click="open = true">
      Select date
    </button>
  </div>

  <div x-data="{ get pickerOpen() { return open }, set pickerOpen(v) { open = v } }">
    <div
      x-data="lyraBottomSheet({
        returnFocusTo: () => $el.closest('.lyra-datepicker-root')?.querySelector('.lyra-datepicker__btn') ?? null,
      })"
      x-modelable="open"
      x-model="pickerOpen"
    >
      <!-- Serve the existing BottomSheet overlay, panel, and Calendar markup here. -->
    </div>
  </div>
</div>

API and code

NameTypeRequiredDescription
labelstring—Label rendered above the picker trigger.
hintstring—Helper text rendered below the picker. Replaced by `error` when set.
errorstring—Error message that enables error styling and replaces `hint`.
valueDate | string | null—Controlled local date or ISO `YYYY-MM-DD` date-only string, or `null` with no date selected.
defaultValueDate | string—Initial local date or ISO `YYYY-MM-DD` date-only string in uncontrolled mode.
onChange(date: Date) => void—Called after the user selects a local date.
placeholderstring—Trigger text when no date is selected. Defaults to `labels.placeholder`.
minDate | string—Inclusive lower selection limit as a local date or ISO `YYYY-MM-DD` string.
maxDate | string—Inclusive upper selection limit as a local date or ISO `YYYY-MM-DD` string.
localestring—BCP 47 locale used to format the selected date and compose Calendar. Default: `"en-US"`.
labelsDatePickerLabels—Translatable visible and accessible labels, merged over the English defaults.
disabledboolean—Disables the picker trigger.

x-data="lyraDatePicker({ … })"

OptionTypeRequiredDescription
defaultValuestring—Initial ISO `YYYY-MM-DD` date.
localestring—BCP 47 locale used for the trigger's selected-date text. Default: `"en-US"`.
placeholderstring—Trigger text when no date is selected. Default: `"Select date"`.

Without React, compose the trigger, Popover and Calendar classes and own the local-date, dialog and keyboard behavior:

html
<div class="lyra-field">
  <label class="lyra-label" for="start-date">Start date</label>
  <span class="lyra-datepicker lyra-popover-anchor">
    <button
      class="lyra-input lyra-datepicker__btn"
      id="start-date"
      type="button"
      aria-haspopup="dialog"
      aria-expanded="true"
    >
      <span>08/04/2026</span>
    </button>
    <div
      class="lyra-popover lyra-popover--bottom lyra-popover--align-start"
      role="dialog"
      aria-label="Date picker"
    >
      <div class="lyra-cal">
        <div class="lyra-cal__head">
          <button class="lyra-cal__nav" type="button">‹</button
          ><button class="lyra-cal__label" type="button">August 2026</button
          ><button class="lyra-cal__nav" type="button">›</button>
        </div>
        <button class="lyra-cal__day lyra-cal__day--selected" type="button" aria-pressed="true">
          4
        </button>
      </div>
    </div>
  </span>
</div>

<lyra:date-picker> Generated from lyra-ds/blade v0.10.0.

The behavior comes from lyraDatePicker() — install @lyra-ds/alpine and see the HTML + Alpine tab.

PropDefaultRequiredExample values
labelnull—0 Date
hintnull——
errornull——
defaultValuenull——
placeholdernull——
minnull——
maxnull——
locale'en-US'——
labels[]——
disabledfalse——
namenull——
blade
<lyra:date-picker
    name="due_date"
    label="Due date"
    hint="Pick a date within the current quarter."
    default-value="2026-03-17"
    min="2026-01-01"
    max="2026-12-31"
    locale="en-US"
/>