Skip to content
kerfjsGitHub

DateField

Type a date or pick one from a calendar in a popover.

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { state } from '@kerf-js/core'import { DateField, Stack, Text } from '@kerf-js/veneer'import type { IsoDate } from '@kerf-js/veneer'export function Release() {  const date = state<IsoDate | null>('2026-11-14')  return (    <Stack gap="related">      <DateField        label="Release date"        today={TODAY}        value={date}      />      <Text        subtle        role="caption"      >        {date.value          ? `Saved as ${date.value}`          : 'No date set'}      </Text>    </Stack>  )}const TODAY: IsoDate = '2026-11-02'

Installation

npm install @kerf-js/veneer

Try it

Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.

Live preview
Controls
Codeupdates as you change the controls
import { DateField } from '@kerf-js/veneer'<DateField label="Release date" />

Usage

tsx
import { DateField } from '@kerf-js/veneer'

Limits

min, max and isDateUnavailable turn days off in the calendar.

tsx
import { DateField } from '@kerf-js/veneer'import type { IsoDate } from '@kerf-js/veneer'export function Limits() {  return (  <DateField    label="Maintenance day"    hint="Weekdays, within the next 60 days"    defaultValue="2026-11-10"    today={TODAY}    min="2026-11-02"    max="2026-12-31"    isDateUnavailable={isWeekend}  />  )}const min = 60_000const TODAY: IsoDate = '2026-11-02'const isWeekend = (d: IsoDate) =>  [0, 6].includes(new Date(`${d}T12:00Z`).getUTCDay())

Week numbers

Set the week's first day and show its number.

tsx
import { DateField } from '@kerf-js/veneer'import type { IsoDate } from '@kerf-js/veneer'export function Weeks() {  return (  <DateField    label="Sprint starts"    defaultValue="2026-11-16"    today={TODAY}    firstDayOfWeek={1}    weekNumbers  />  )}const TODAY: IsoDate = '2026-11-02'

Hint, error, read-only, disabled

A date field can show a hint or an error, and can be readonly or disabled.

tsx
import { DateField, Stack } from '@kerf-js/veneer'import type { IsoDate } from '@kerf-js/veneer'export function States() {  return (  <Stack gap="group">    <DateField      label="Release date"      today={TODAY}      hint="Optional"    />    <DateField      label="Release date"      today={TODAY}      error="Pick a date after the freeze ends"      defaultValue="2026-12-24"    />    <DateField      label="Release date"      today={TODAY}      defaultValue="2026-11-14"      readonly    />    <DateField      label="Release date"      today={TODAY}      defaultValue="2026-11-14"      disabled    />  </Stack>  )}const TODAY: IsoDate = '2026-11-02'

Schedule a deploy

A date and a time in one small form.

tsx
import { state } from '@kerf-js/core'import {  Button,  DateField,  Row,  Stack,  Text,  TimeField} from '@kerf-js/veneer'import type { IsoDate, IsoTime } from '@kerf-js/veneer'export function Schedule() {  const date = state<IsoDate | null>('2026-11-05')  const time = state<IsoTime | null>('14:00')  const planned = state('')  const plan = () =>    (planned.value =      `checkout goes out on ${date.value} ` +      `at ${time.value}`)  return (    <Stack gap="group">      <Text role="heading">Schedule a deploy</Text>      <Row        gap="related"        wrap={false}      >        <DateField          label="Date"          today={TODAY}          value={date}          min={TODAY}        />        <TimeField          label="Time"          today={TODAY}          value={time}          step={30}        />      </Row>      <Row        gap="related"        justify="between"      >        <Text          subtle          role="caption"        >          {planned.value || 'Nothing planned yet'}        </Text>        <Button          aria-disabled={            date.value && time.value ? undefined : 'true'          }          onClick={plan}        >          Schedule        </Button>      </Row>    </Stack>  )}const min = 60_000const TODAY: IsoDate = '2026-11-02'

When to use

Use it for

  • One date in a form
  • People may know the date and want to type it

Reach for something else

Accessibility

Up/Down
Changes the focused part of the date
Left/Right
Moves between month, day and year
Arrowsinthecalendar
Move by day or week
PageUp/PageDown
Change the month
Enter
Picks the day
Escape
Closes the calendar
  • Each part of the date is its own spin button, and every calendar day is named in full.
  • On phones the calendar opens as a sheet.

Theming

Restyle it app-wide from veneer() in your kerf config. Every key takes colour, spacing, size, radius, shadow, type and motion; its own keys are listed. A state nests inside: hover: { bg: 'ink' }. Text on a fill you set is contrast-checked at build. How theming works

KeyOwn keysStates
parts.fieldbg, radiushover, focus, press, active, open, disabled
parts.field.labeltexthover, focus
ts
veneer({  colors: { brand: '#e8590c', ink: '#1b1b1f' },  parts: {    field: { radius: 4, label: { focus: { text: 'brand' } } },  },})

API Reference

PropTypeDefaultDescription
labelstring—The accessible name, drawn as the floating border label (or above, classic). Required.
hintunknown—One line of help under the control.
errorunknown—Shown instead of the hint; marks the field invalid.
iconIconRef—Leading icon: it sweeps with the border.
prefixunknown—Text or an element inside the field before the value, such as $.
suffixunknown—Text or an element inside the field after the value, such as kg.
variant"floating" | "classic"—floating (default, or the app's veneer({ fields })) · classic: the label above.
appearance"line"—line: only a bottom line under the value, no box or fill. The boxed field otherwise.
size"sm" | "md" | "lg"'md'Control size. md by default.
clearablebooleanfalseA clear button (shown once there is a value).
counternumber—Maximum length, shown as a counter.
idstring—The element id. One is made for you if you leave it out.
disabledbooleanfalseTurns it off, and takes it out of the tab order.
readonlybooleanfalseShows the value and lets it be selected and copied, but not changed.
requiredbooleanfalseThe form will not submit without it.
namestring—The name it posts under in a form.
localestring—The language and region used to format values.
todaystring—Fixes what today is, for tests and server rendering.
presentationPresentation—auto opens a popover, or a sheet when the viewport is under 600px wide; popover and sheet force one.
value`${number}-${number}-${number}` | State<`${number}-${number}-${number}` …—The current value, when you own the state.
defaultValue`${number}-${number}-${number}` | null—The starting value, when the component owns the state.
openboolean | State<boolean>—A state box that holds whether it is open.
defaultOpenbooleanfalseStarts open, when the component keeps its own open state.
min`${number}-${number}-${number}`—The lowest allowed value.
max`${number}-${number}-${number}`—The highest allowed value.
isDateUnavailable((d: `${number}-${number}-${number}`) => boolean)—Return true to turn a day off.
firstDayOfWeek0 | 2 | 3 | 4 | 6 | 1 | 5—The day each calendar row starts on, 0 for Sunday to 6. The locale's otherwise.
weekNumbersbooleanfalseShows the week number at the start of each calendar row.
parse((text: string, today: `${number}-${number}-${number}`) => `${number}-${…—Your own reader for pasted or typed text.
EventTypeDefaultDescription
onInput((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen…—Called on each keystroke.
onValueChange((v: `${number}-${number}-${number}` | null, reason: ChangeReason) => vo…—Called with each new value, and whether a pointer, a key or code made the change.
onOpenChange((o: boolean) => void)—Called with true when it opens and false when it closes.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="date-field"] [data-part="segment"]

segment

RelatedDate range fieldDate time fieldInline calendar