Date time picker
A day and a start time as two fields side by side: the date picker and the time picker. Meetings use it, with the length beside it; times are in the agency's time zone.
Playground
Change the controls to try each prop of the date field. The code under the preview updates as you go and lists only the props you changed; copy it into your page.
import { DatePicker } from '@/ui/date-picker';<DatePicker label="Start date" />Default
The meeting composer's When row: the day, the start time from a 15-minute list, and the length after them. Days before today are off, and on today the time list starts from now. Picking a day moves focus to the time.
import { DateTimePicker } from '@/ui/date-time-picker';
import { Field, FieldLabel } from '@/ui/field';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/ui/select';
const lengths = { '15': '15 min', '30': '30 min', '45': '45 min', '60': '1 hour' };
// The meeting composer's When row. Today and now come from the agency's time zone.
export default function MeetingStart() {
return (
<Field className="w-fit">
<FieldLabel id="meeting-when">When</FieldLabel>
<DateTimePicker aria-labelledby="meeting-when" today="2026-10-16" now="14:10" defaultValue="2026-10-16T14:30">
<Select items={lengths} defaultValue="30">
<SelectTrigger aria-label="Length" className="w-28">
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(lengths).map(([value, label]) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
</DateTimePicker>
</Field>
);
}Date picker
A day on its own, such as a start date. Type it in any common shape ("3 nov", "3/11", "2026-11-03") or press Down to open the calendar. Today, In a week and Clear sit under the calendar.
The candidate’s first day at Halcyon Bank.
import { DatePicker } from '@/ui/date-picker';
import { Field, FieldDescription, FieldLabel } from '@/ui/field';
// Type "3 nov", "3/11" or "2026-11-03", or press Down for the calendar.
export default function StartDate() {
return (
<Field className="w-fit">
<FieldLabel htmlFor="start-date">Start date</FieldLabel>
<DatePicker id="start-date" label="Start date" today="2026-10-16" defaultValue="2026-11-03" aria-describedby="start-date-help" />
<FieldDescription id="start-date-help">The candidate’s first day at Halcyon Bank.</FieldDescription>
</Field>
);
}Calendar
The month grid inside the date picker (Decided 9): « and » step a year, ‹ and › a month. The picked day sits on the selected fill in brand green 600; today is brand green 600 with a 2px underline. Weeks start on Monday.
| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|---|---|---|---|---|---|---|
import { useState } from 'react';
import { localDay } from '@/lib/dates';
import { Calendar } from '@/ui/calendar';
// Decided 9: the picked day on the selected fill, today underlined in brand green, weeks from Monday.
export default function CalendarExample() {
const [day, setDay] = useState<Date | undefined>(localDay('2026-11-03'));
return <Calendar mode="single" selected={day} onSelect={setDay} today={localDay('2026-10-16')} defaultMonth={localDay('2026-11-03')} />;
}States
Empty shows today's date as the placeholder. min and max turn off the days outside them. Disabled fades both fields while the meeting saves.
import { DatePicker } from '@/ui/date-picker';
import { Field, FieldLabel } from '@/ui/field';
import { TimePicker } from '@/ui/time-picker';
export default function States() {
return (
<div className="flex flex-wrap items-start gap-200">
<Field className="w-fit">
<FieldLabel htmlFor="state-empty-day">Empty</FieldLabel>
<DatePicker id="state-empty-day" label="Follow-up day" today="2026-10-16" />
</Field>
<Field className="w-fit">
<FieldLabel htmlFor="state-min-day">From today</FieldLabel>
<DatePicker id="state-min-day" label="Snooze until" today="2026-10-16" min="2026-10-17" defaultValue="2026-10-19" />
</Field>
<Field className="w-fit" disabled>
<FieldLabel htmlFor="state-disabled-day">Disabled while saving</FieldLabel>
<div className="flex gap-100">
<DatePicker id="state-disabled-day" label="Day" today="2026-10-16" defaultValue="2026-10-16" disabled />
<TimePicker label="Start time" defaultValue="14:30" disabled />
</div>
</Field>
</div>
);
}Wrong input
End before start
The end field turns red and names the start date before anything is submitted.
import { useState } from 'react';
import { calendarDay } from '@/lib/dates';
import { DatePicker } from '@/ui/date-picker';
import { Field, FieldError, FieldLabel } from '@/ui/field';
// End before start is caught here, before submit, naming the other date. The database check stays as the backstop.
export default function RangeError() {
const [start, setStart] = useState('2026-10-20');
const [end, setEnd] = useState('2026-10-15');
const backwards = start && end && end < start;
return (
<div className="flex flex-wrap items-start gap-200">
<Field className="w-fit">
<FieldLabel htmlFor="contract-start">Contract start</FieldLabel>
<DatePicker id="contract-start" label="Contract start" today="2026-10-16" value={start} onValueChange={setStart} />
</Field>
<Field className="w-fit" invalid={!!backwards}>
<FieldLabel htmlFor="contract-end">Contract end</FieldLabel>
<DatePicker
id="contract-end"
label="Contract end"
today="2026-10-16"
value={end}
onValueChange={setEnd}
invalid={!!backwards}
aria-describedby={backwards ? 'contract-end-error' : undefined}
/>
<FieldError id="contract-end-error">{backwards ? `End date is before the start date (${calendarDay(start)}).` : null}</FieldError>
</Field>
</div>
);
}Unusual dates and unreadable text
A year before 1900 or more than 50 years ahead is allowed but asks "Is 2091 right?". Text that doesn't read as a date says how to fix it when you leave the field, and the last good day stays.
import { DatePicker } from '@/ui/date-picker';
import { Field, FieldLabel } from '@/ui/field';
// Allowed, but usually a typo: a year before 1900 or more than 50 years ahead asks before it is saved. Type a date that
// doesn't read, such as "31 Feb", and leave the field: it says how to fix it and keeps the last good day.
export default function Unusual() {
return (
<div className="flex flex-wrap items-start gap-200">
<Field className="w-fit">
<FieldLabel htmlFor="guarantee-end">Guarantee end</FieldLabel>
<DatePicker id="guarantee-end" label="Guarantee end" today="2026-10-16" defaultValue="2091-01-01" />
</Field>
<Field className="w-fit">
<FieldLabel htmlFor="interview-day">Interview day</FieldLabel>
<DatePicker id="interview-day" label="Interview day" today="2026-10-16" defaultValue="2026-10-21" />
</Field>
</div>
);
}Color swatch picker
Picks a colour from a fixed set, for a mail tag or a select option. People pick a hue to tell things apart; they never type a hex.
Field types
Every value type a record can hold, as it reads in a 36px table cell, in the details rail and in a form. A type looks the same everywhere (C463). Each opens one of six shared editors, drawn below.