Skip to content

Date picker

hc-datepicker styles a native <input> whose type is one of date, datetime-local, month, or time. The native input retains every accessible behaviour — keyboard navigation, locale-aware rendering, the OS-native calendar / time picker on mobile, form submission, min / max / step validation — and only the closed-state chrome is replaced via appearance: none and an embedded SVG icon.

If you need a fully-bespoke calendar UI (custom highlighting, preset ranges, multi-month view), reach for hc-calendar — and for a date form field with a styled dropdown grid, use the blessed input + popover + calendar pattern. The native input is the right default for the vast majority of business forms.

Also known as: date picker, calendar input.

type="time" swaps the trailing icon to a clock; the other types all use a calendar glyph.

Same axes as hc-input / hc-select — data-variant accepts success, warning, and error for border-color cues.

Set aria-invalid="true" together with data-variant="error" so assistive tech also recognises the field as invalid.

data-size accepts sm, md (default), and lg. Sizes follow the same --hc-control-* scale as hc-button / hc-input / hc-select, so data-density="compact" shrinks consistently.

Use the native disabled attribute. The control dims and stops accepting input; the value still submits if you instead use readonly.

A native <input type="date"> is single-value. For a range, place two inputs in a group carrying data-hc-range="<param>", named <param>-from and <param>-to — the kit’s installRangeValue() behavior owns the pair from there, with no page script:

<fieldset class="hc-field" data-hc-range="ship">
<legend>Ship date</legend>
<input class="hc-datepicker" type="date" name="ship-from" aria-label="From">
<span aria-hidden="true">–</span>
<input class="hc-datepicker" type="date" name="ship-to" aria-label="To">
</fieldset>
  • One condition on the wire. The pair serialises as a single ship=2026-07-01..2026-07-31 entry (open ends stay open: A.., ..B; both empty sends nothing), so a period is one chip to remove and one value a saved view stores. The join happens on the formdata event, which htmx and a native submit both fire.
  • from > to is refused, never swapped or clamped. The behavior sets a native validity message on the To control, so the browser blocks the submit and no request is issued — reordering silently would run a different condition from the one written.
  • No-JS path: the inputs keep their real names, so without the behavior the form submits ship-from / ship-to; servers accept both shapes.

Static bounds still belong on the inputs as min / max. Relative ends (@month-start-1m) and the applied-conditions bar are covered in the datagrid-filter recipe → Date ranges.

The input participates in form submissions like any native input. For instant filtering on date change:

<input class="hc-datepicker"
type="date"
name="due"
data-hx-get="/tasks"
data-hx-trigger="change"
data-hx-target="#tasks">
  • Always associate the input with a label — wrap it in hc-field with a <label for>, use aria-label, or use aria-labelledby.
  • The native input handles keyboard fully: ← / → moves between year / month / day spinners, ↑ / ↓ changes the active spinner. Do not add custom keyboard handlers; they conflict with the built-in behaviour.
  • The icon is purely decorative — screen readers ignore the background-image.
  • For business apps with localisation, the browser handles date rendering in the user’s locale automatically. The submitted value is always ISO 8601 (yyyy-mm-dd) regardless of display locale.

Component tokens (in component.tokens.json):

Token path Purpose
datepicker.height / padding-x Sizing (inherits density via --hc-control-*).
datepicker.radius / font-size / bg / fg / border Surface, text, resting border.
datepicker.focus-border Border when focused.
datepicker.error-border / success-border / warning-border Variant borders.
datepicker.disabled-bg Background when disabled.
datepicker.icon-size Calendar / clock icon dimension.
datepicker.sm.* / lg.* Dedicated sm / lg overrides.
Show the generated CSS variables
--hc-datepicker-height | -padding-x | -radius | -font-size
--hc-datepicker-bg | -fg | -border
--hc-datepicker-focus-border | -error-border | -success-border | -warning-border
--hc-datepicker-disabled-bg
--hc-datepicker-icon-size
--hc-datepicker-sm-height | -sm-padding-x | -sm-font-size
--hc-datepicker-lg-height | -lg-padding-x | -lg-font-size
--hc-color-focus-ring (inherited from data-color)
  • Input — text and textarea sibling sharing the same control scale.
  • Select — styled native <select> with the same chevron architecture.
  • Field — label + helper text wrapper.