KKUONIDesign System

Components

Date Picker

Beta

A calendar popover for selecting single dates. Used in holiday search widgets, booking forms, and departure date fields throughout the booking funnel.

Purpose

The DatePicker provides a visual calendar for date selection — more discoverable than a native <input type="date"> and consistent across browsers. It appears wherever a single date must be chosen:

  • Search widgets — departure date panel in the holiday SearchBar
  • Booking forms — specific departure date, flight date selection
  • Availability checks — "Check availability from" fields

The calendar highlights today in Wow Yellow and selected dates in Site Teal, creating clear visual hierarchy. Keyboard and mouse users can navigate month-by-month. Disabled dates (via minDate / maxDate) are shown in grey and are not selectable.

Usage

Reach for DatePicker whenever the value being captured is a calendar date — it is far more discoverable and consistent across browsers than a native <input type="date">. For a fixed, exhaustive list of non-date choices (country, room type), use Input with as="select" instead.

DatePicker typically sits inside the holiday SearchBar's departure/return date panels, in booking forms further down the funnel, and in availability-check fields — anywhere a single specific date must be chosen.

Anatomy

  1. 1
    Trigger buttonFull-width h-11 button with rounded-sm border. Shows selected date or placeholder. Calendar icon on the right.
  2. 2
    LabelSits above the trigger. 14px medium weight. Required asterisk in danger colour when required=true.
  3. 3
    Month headerDisplays "Month Year" with left/right chevron buttons to navigate. Centred between arrows.
  4. 4
    Day header rowSu Mo Tu We Th Fr Sa — 7 columns. 12px medium grey text.
  5. 5
    Day grid7-column grid. Each day is a button. Today = yellow background. Selected = teal background + white text. Disabled = grey text, not clickable.
  6. 6
    Calendar popoverAbsolute-positioned 288px wide card with rounded-md border and shadow-kuoni-md. Appears below the trigger.

Variants

Default — open

The calendar popover is toggled by clicking the trigger button. Click on a date to select it and close the calendar.


With pre-selected date

Pass an ISO date string (YYYY-MM-DD) as defaultValue to show an initial selection. Use value for a controlled component where the parent manages state.


With min/max constraints

Use minDate and maxDate (ISO strings) to disable dates outside a valid window — for example, limiting departure dates to the next 18 months.


Error state


Disabled

States

Trigger states

StateAppearance
DefaultGrey border, placeholder text in grey-400
FocusedTeal border + teal focus ring
SelectedDark text showing formatted date (e.g. "15 Jul 2025")
ErrorDanger border, error message below
DisabledGrey-50 background, 60% opacity, cursor-not-allowed

Calendar day states

StateAppearance
DefaultKuoni dark text, transparent background
HoverTeal-50 background
TodayWow Yellow background, dark text, semibold
SelectedTeal background, white text, semibold
DisabledGrey-300 text, not clickable

Best practices

Do

Always pair DatePicker with a clear label explaining what date is required — 'Departure date', 'Return date', 'Check-in date'.

ISO date in defaultValue and the label provides clear context.

Don't

Render DatePicker without a label or placeholder in a dense form where users must guess what it's for.

An unlabelled trigger gives no context about what date is being selected.

Do

Set minDate to today (or the earliest valid date) to prevent users from selecting invalid past dates.

minDate prevents selecting dates in the past, reducing booking errors.

Don't

Leave minDate unset on departure date fields where past dates are never valid.

Allowing past dates forces users to notice and correct an error at a later validation step.

Do

Use defaultValue (uncontrolled) when the field starts with a logical default based on prior selections.

defaultValue of '2025-08-10' shows the current selection immediately on mount.

Don't

Show an empty DatePicker next to a pre-filled destination — pre-fill related fields together when context allows.

Without a default the calendar is empty — force users to always open and pick.

Do

Pair required= with a descriptive error prop for form validation contexts.

A required asterisk and clear error message guides users to fix the missing date.

Don't

Silently block form submission without surfacing a DatePicker error message.

Silent failure leaves users confused about why they cannot proceed.

Props

PropTypeDefaultDescription
idstringID for the trigger button element. Auto-generated as "datepicker" if not provided.
labelstringVisible label above the trigger button. Strongly recommended for accessibility.
valuestringControlled selected date in ISO format (YYYY-MM-DD). When provided, the parent controls the selection.
defaultValuestringInitial selected date for uncontrolled usage. ISO format (YYYY-MM-DD).
onChange(iso: string, date: Date) => voidCalled when a date is selected. Receives ISO string and Date object.
minDatestringEarliest selectable date in ISO format. Dates before this are disabled.
maxDatestringLatest selectable date in ISO format. Dates after this are disabled.
disabledbooleanfalseDisables the entire picker. Trigger button gets bg-grey-50 and cursor-not-allowed.
requiredbooleanfalseMarks the field as required with a red asterisk and aria-required.
placeholderstring'Select a date'Text shown in the trigger when no date is selected.
errorstringValidation error message shown below the trigger. Switches border to danger colour.
helpTextstringHelp text shown below the trigger. Hidden when error is shown.
classNamestringAdditional Tailwind classes on the root container element.

Accessibility

Trigger button

The trigger has aria-expanded={open} and aria-haspopup="dialog" to signal that activation opens a dialog. aria-required={required} and aria-invalid={error ? 'true' : undefined} connect to WCAG form requirements.

Calendar dialog

The popover has role="dialog" and aria-modal="true". Its label is aria-label="Choose a date".

Day buttons

Each day button has aria-label="14 July 2025" (full written date). Selected days have aria-pressed={true}. Disabled days have the native disabled attribute.

Keyboard interaction

KeyAction
Enter / SpaceOpen/close the calendar
EscapeClose the calendar
TabNavigate: trigger → prev month → next month → day grid buttons
Arrow keysNavigate between day buttons within the grid
Enter on a daySelect the day and close the calendar

Date formatting

The displayed date uses en-GB locale: "14 Jul 2025". The aria-valuetext on the day buttons uses the full month name for unambiguous screen reader announcement.

  • Input — use as="select" for fixed non-date choices; DatePicker shares the same label/error/helpText pattern.
  • CounterInput — the neighbouring control in most search widgets, for traveller and room counts.