Flux 2.0

Component

Date picker

Single-date and date-range selection from a keyboard-operable calendar.

Plain-text source, for agents and clipboard use: /components/source/date-picker.txt

When to use it

Use when a user needs to choose a date and the surrounding days help them decide — departures, arrivals, deadlines and booking ranges. The trigger matches every other Flux field, while the calendar exposes the same ISO values an ordinary form expects.

Not this
For a date the user already knows and can type faster, use a labelled text input with explicit format help. For a month, quarter or recurring schedule, use a control designed for that unit rather than making users select an arbitrary day.

Examples

Single date

Selecting a date updates the hidden ISO value and closes the calendar. The visible value follows the familiar day / month / year order.

Departure date
html
<div class="flux-field">
  <span class="flux-field__label" id="departure-date-label">Departure date</span>
  <div class="flux-date-picker" data-flux-date-picker data-placeholder="DD / MM / YYYY">
    <button class="flux-date-picker__control" id="departure-date-trigger" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="departure-date-calendar" aria-labelledby="departure-date-label departure-date-value">
      <svg class="flux-date-picker__icon" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
        <rect x="1.5" y="2.5" width="13" height="12" rx="2" stroke="currentColor" stroke-width="1.25" />
        <path d="M5 1V4M11 1V4M1.5 6.5H14.5" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" />
      </svg>
      <span class="flux-date-picker__value" id="departure-date-value">DD / MM / YYYY</span>
      <svg class="flux-date-picker__chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
        <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
      </svg>
    </button>
    <input type="hidden" name="departureDate" value="" data-flux-date-input="start" />
    <div class="flux-date-picker__panel" id="departure-date-calendar" role="dialog" aria-labelledby="departure-date-label departure-date-month" hidden>
      <p class="flux-date-picker__status" aria-live="polite">Choose a date</p>
      <div class="flux-date-picker__header">
        <button class="flux-date-picker__nav" type="button" data-date-action="previous" aria-label="Previous month">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M10 13L5 8L10 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
        </button>
        <h3 class="flux-date-picker__month" id="departure-date-month" aria-live="polite"></h3>
        <button class="flux-date-picker__nav" type="button" data-date-action="next" aria-label="Next month">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M6 3L11 8L6 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
        </button>
      </div>
      <div class="flux-date-picker__weekdays" aria-hidden="true">
        <span class="flux-date-picker__weekday">Mo</span><span class="flux-date-picker__weekday">Tu</span><span class="flux-date-picker__weekday">We</span><span class="flux-date-picker__weekday">Th</span><span class="flux-date-picker__weekday">Fr</span><span class="flux-date-picker__weekday">Sa</span><span class="flux-date-picker__weekday">Su</span>
      </div>
      <div class="flux-date-picker__grid" role="grid" aria-labelledby="departure-date-month"></div>
      <div class="flux-date-picker__footer">
        <button class="flux-date-picker__clear" type="button" data-date-action="clear" hidden>Clear</button>
      </div>
    </div>
  </div>
</div>

Date range

The first selection sets the start. The second completes and orders the range, then closes the calendar.

Travel dates
html
<div class="flux-field">
  <span class="flux-field__label" id="travel-dates-label">Travel dates</span>
  <div class="flux-date-picker" data-flux-date-picker="range" data-placeholder="Select dates">
    <button class="flux-date-picker__control" id="travel-dates-trigger" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="travel-dates-calendar" aria-labelledby="travel-dates-label travel-dates-value">
      <svg class="flux-date-picker__icon" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
        <rect x="1.5" y="2.5" width="13" height="12" rx="2" stroke="currentColor" stroke-width="1.25" />
        <path d="M5 1V4M11 1V4M1.5 6.5H14.5" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" />
      </svg>
      <span class="flux-date-picker__value" id="travel-dates-value">Select dates</span>
      <svg class="flux-date-picker__chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
        <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
      </svg>
    </button>
    <input type="hidden" name="travelStart" value="" data-flux-date-input="start" />
    <input type="hidden" name="travelEnd" value="" data-flux-date-input="end" />
    <div class="flux-date-picker__panel" id="travel-dates-calendar" role="dialog" aria-labelledby="travel-dates-label travel-dates-month" hidden>
      <p class="flux-date-picker__status" aria-live="polite">Choose a start date</p>
      <div class="flux-date-picker__header">
        <button class="flux-date-picker__nav" type="button" data-date-action="previous" aria-label="Previous month">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M10 13L5 8L10 3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
        </button>
        <h3 class="flux-date-picker__month" id="travel-dates-month" aria-live="polite"></h3>
        <button class="flux-date-picker__nav" type="button" data-date-action="next" aria-label="Next month">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M6 3L11 8L6 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
        </button>
      </div>
      <div class="flux-date-picker__weekdays" aria-hidden="true">
        <span class="flux-date-picker__weekday">Mo</span><span class="flux-date-picker__weekday">Tu</span><span class="flux-date-picker__weekday">We</span><span class="flux-date-picker__weekday">Th</span><span class="flux-date-picker__weekday">Fr</span><span class="flux-date-picker__weekday">Sa</span><span class="flux-date-picker__weekday">Su</span>
      </div>
      <div class="flux-date-picker__grid" role="grid" aria-labelledby="travel-dates-month"></div>
      <div class="flux-date-picker__footer">
        <button class="flux-date-picker__clear" type="button" data-date-action="clear" hidden>Clear</button>
      </div>
    </div>
  </div>
</div>

Anatomy

PartDescription
LabelVisible above the trigger and included in its accessible name.
TriggerField-height button showing the current value or format placeholder.
Calendar dialogA floating overlay with month navigation, weekday headings and a date grid.
Date gridRoving-tabindex grid. One date is tabbable; arrow keys move by day or week.
Form valueOne hidden ISO date for single selection; start and end ISO dates for a range.

API

NameKindDescription
flux-date-pickerclassPositions the field trigger and floating calendar.
data-flux-date-pickerattributeInitialises a single picker. Set it to "range" for start-and-end selection.
flux-date-picker__controlclassButton trigger with standard Flux field geometry.
flux-date-picker__panelclassCalendar dialog surface. Hidden until the trigger opens it.
flux-date-picker__gridclassGenerated calendar grid with roving keyboard focus.
data-flux-date-inputattributeMarks hidden start and optional end inputs that receive YYYY-MM-DD values.
data-placeholderattributeVisible trigger text before a date has been selected.
aria-invalidattributeError state on the date-picker wrapper. Bind an adjacent message with aria-describedby.

States

StateTreatment
EmptyFormat or selection prompt shown with text-placeholder.
OpenCalendar dialog visible; focus moves to the selected date, today or the first date.
Selectedsurface-brand date with text-on-color; hidden input carries its ISO value.
RangeStart and end use the selected treatment; days between use surface-selected.
Todayborder-interactive outline, independent of selection.
Invalidborder-error on the trigger plus a bound error message.

Keyboard

KeysAction
Enter, Space, DownOpens the calendar from the trigger.
Left, RightMoves focus by one day.
Up, DownMoves focus by one week.
Home, EndMoves to the first or last day of the current week.
Page Up, Page DownMoves to the same day in the previous or next month.
Shift + Page Up, Page DownMoves to the same day in the previous or next year.
Enter, SpaceSelects the focused date.
EscapeCloses the calendar and returns focus to the trigger.

Accessibility

  • The trigger exposes aria-haspopup="dialog", aria-expanded and aria-controls. Its accessible name combines the visible field label and value.
  • The month heading labels the grid and announces month changes through aria-live.
  • Today uses aria-current="date"; selected endpoints use aria-selected. Range colour is supplementary and never the only programmatic selection state.
  • Only one date has tabindex="0". Arrow, Home, End and Page keys follow the WAI-ARIA date-picker keyboard pattern.
  • Hidden values use ISO YYYY-MM-DD so display formatting never changes submitted data.
  • Include flux-components.js. Without it the grid cannot be generated or operated.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-field-height
  • --flux-field-border
  • --flux-datepicker-panel-width
  • --flux-datepicker-panel-bg
  • --flux-datepicker-day-size
  • --flux-datepicker-selected-bg
  • --flux-datepicker-selected-text
  • --flux-datepicker-range-bg
  • --flux-datepicker-today-border

Do and don’t

Do

  • Use a single picker for one deadline or departure date, and range only when both endpoints belong to one decision.
  • Keep submitted values in ISO format even when visible dates use a local format.
  • Provide nearby constraints such as earliest departure or maximum stay before the user opens the calendar.

Don’t

  • Do not use colour alone to identify today, selection or errors.
  • Do not close a range picker after the first date; announce that the end date is now required.
  • Do not omit flux-components.js or replace the visible label with the placeholder.