Date field

The single-date input of the design system. React <DateField> (packages/ui/src/components/DateField.tsx) emits .dp-field + .dp-input + .dp-open from the engine apps/web/src/ds/date-picker.css. It replaces every native <input type="date"> in the app (canon since 21 Aug 2026): the date can be typed ("26.8.", "dnes", "pá", "+7", "za 14 dní") and the calendar is the second way in, not the only one.

When to use

  • Any field that holds exactly one date: task due date, meeting date, invoice date, date of birth.
  • "Termín" is always a single date — the second date of an event is a consequence handled by Date-time range, never a second Date field next to it.
  • Inline editing in the record rail (autoFocus after the row is activated).
  • Dates that usually lie ahead (due dates, deadlines): pass futureBias so "1.9." typed in December means next year.

When not to use

  • A from–to span in a filter or report — use Date range.
  • Date plus time of an event — use Date-time range.
  • Time only — use Time field.
  • A picker embedded in a menu or rail without an input — use Calendar directly inside the host menu.
  • Native <input type="date">, type="month" or type="datetime-local" anywhere in the app; tests (rail-date-field.test.tsx, advance-settle-month.test.tsx) assert their absence.

Anatomy

  • Wrapper .dp-fieldset: column, gap --sp-1; holds the field, the optional echo line and the portal anchor.
  • Field .dp-field: flex, height 32px, padding 0 var(--sp-2) 0 var(--sp-3), background --surface-100, 1px border --border-strong, radius --radius-sm; flat at rest (no shadow).
  • Input .dp-input: width 168px, 400 13px var(--font-sans), colour --ink-900, placeholder --fg-muted; shows the value through formatDate (year only when needed).
  • Calendar trigger .dp-open: 24×24 quiet button, icon --ico-sm, aria-haspopup="dialog", opens Picker panel with Calendar (singleDatePresets: Dnes / Zítra / Příští týden) and a .dp-foot "Vymazat" once a value exists.
  • Echo .dp-echo: live interpretation of typed text under the field ("→ pátek 26. 8."); .bad turns --critical with the parser note. Exists only while typing; collapses when empty.
  • States: :focus-within accent border + 3px color-mix(--accent 16%) ring; .err (error prop or unparsable commit) --critical border and ring; .disabled background --surface-75, border --border, muted text.
  • Value contract: ISO YYYY-MM-DD or null in both directions; min/max limit both the calendar and the typed value — a typed date outside the range is not committed, the echo shows „Nejdříve …" / „Nejpozději …" and the field turns .err (30 Sep 2026). today anchors the relative grammar for tests.

Best practices

Do

  • One <DateField> for "Termín"; let the user type ("zítra", "+7") and keep the calendar as the alternative.
  • Wrap it in <Field label="Termín"> and put the validation message in Field.error, not in the placeholder.
  • Set futureBias on due dates and min/max where the business rule really exists (booking window).
  • Keep the default width: 168px input plus trigger — width is content, the field is not stretched to the form column.

Don't

  • Two Date fields side by side for an event start/end — Date-time range owns the second date.
  • Opening the calendar on focus — six dated fields in a form must remain tabbable; only the icon or ↓ opens it.
  • A custom label inside the field ("Datum:") or a hard-coded format like ${d.getDate()}. ${d.getMonth()+1}..
  • Showing dates with tabular-nums or with a year when the date is in the current year.

Content

  • Display format comes from formatDate (packages/ui/src/lib/date-core.ts): current year "26. 8.", another year of this century "14. 11. 27", another century "11. 2. 1988"; non-breaking spaces in cs-CZ.
  • Placeholder default "Doplnit…"; keep it as a verb hint, never an example format.
  • Echo text is generated: "→ pátek 26. 8." on success; parser notes on failure ("Nerozumím — zkus 26.8. nebo „za 14 dní“", "Který měsíc? …", "únor 2027 má 28 dní").
  • ariaLabel default "Datum"; the visible label lives in <Field label> in sentence case.
  • Never send formatDate output to the API — the wire value is ISO.
  • calendar — the month grid the field opens.
  • picker-panel — the floating layer that hosts it.
  • time-field — companion for the time part.
  • date-range, date-time-range — spans built from this field.
  • Legacy card legacy/dates.html: year convention still valid; its implementation path (apps/web/src/i18n/date-core.ts) and the mention of <input type="date"> are superseded — the source is packages/ui/src/lib/date-core.ts and native date inputs are gone.

Source: docs/date-time/date-field.usage.md · GitLab