Actual CSS

Date picker

A JavaScript date picker is not shipped by the framework — the native <input type="date"> stays the baseline. Use data-mask for a fixed text shape, or theme an external picker with Actual tokens.

The native control is the default: a real calendar, no JavaScript, and an ISO yyyy-mm-dd value whatever the platform displays. Reach for an external picker only when the product needs a shape the native control cannot express — a month grid for a range, a typed constraint, a styled calendar.

Related terms: date field, datepicker, calendar, native date, date input, masked date, input mask, date range.

Native baseline

<label class="field">
  <span class="field-label">Start date</span>
  <input class="input" type="date" />
</label>

Masked text field

data-mask structures a plain text field to a fixed shape and the date rule validates meaning. The mask enforces shape, not validity, so 2026-13-40 needs the rule (or server validation).

<label class="field">
  <span class="field-label">Date</span>
  <input class="input" data-mask="9999-99-99" inputmode="numeric" autocomplete="off" placeholder="yyyy-mm-dd" />
</label>

Theme bridge

The date fields template puts the native control, the masked field and @lekoala/date-picker side by side, the picker skinned entirely with Actual tokens.

<label class="field">
  <span class="field-label">Booking date</span>
  <date-picker value="2026-09-25" open-on-focus="false">
    <input class="input" name="date">
  </date-picker>
</label>
date-picker,
date-calendar {
  --dp-bg: var(--surface-raised);
  --dp-fg: var(--text);
  --dp-muted: var(--text-muted);
  --dp-border: var(--border);
  --dp-hover: var(--surface-subtle);
  --dp-accent: var(--primary);
  --dp-accent-fg: var(--primary-fg);
  --dp-radius: var(--radius);
  --dp-focus: var(--focus);
  --dp-focus-width: var(--focus-ring-width);
  /* Trigger glyph on the select chevron axis. */
  --dp-picker-button-size: calc(var(--control-pad-x) * 2 + 1em + var(--border-width) * 2);
}

.dp-picker-panel[popover] {
  box-shadow: var(--shadow-popout);
}

The picker draws focus as an inset line, like the core, so --dp-focus and --dp-focus-width are the whole focus mapping; the field itself is a plain .input and keeps the core outline. The panel has no shadow token, so it takes --shadow-popout directly. It opens as a popover, in the top layer, so inside a modal, even in a scrolling body, it is neither clipped nor stacked under the dialog. The calendar popup is its own element, so the --dp-* map is declared on date-picker/date-calendar rather than inherited from a wrapper.

The header month, year and arrows take host classes through controlClasses, so they render as real Actual controls:

picker.controlClasses = { month: "select sm", year: "input sm", nav: "btn outline sm icon-only" };

Set the defaults once for every picker and inline calendar. With the CDN script, call the registered constructor after the deferred script has loaded:

customElements.get("date-picker").setDefaultControlClasses({
  month: "select sm",
  year: "input sm",
  nav: "btn outline sm icon-only",
});

With a bundler, import setDefaultControlClasses from @lekoala/date-picker. An instance's controlClasses overrides only the supplied roles; omitted roles follow the current defaults. Day cells and the field trigger stay on the --dp-* tokens.

As with the combobox, the bridge stays in docs/demo — it is a recipe, not package API, and pins no widget version.

Which one?

  • A plain date the platform can collect: native <input type="date">.
  • A fixed text format that must survive as typed text: data-mask plus the date rule.
  • A calendar, range, or constraint the native control cannot express: an external picker on the token bridge.
Actual CSS

Search documentation