- Components
Date picker
A date field you can type or pick: the text takes ISO or day-first, the button opens the calendar in a dialog.
npx shadcn add @klarhimmel/date-picker @klarhimmel/inputWhen to use
Use for
A date in a form: arrival, departure, a birthday, a due date.
Not for
A time of day or a range in one control. Use two Date pickers.
Pairs with
Field, Calendar, Button.
Preview
Type 2026-11-16 or 16/11/2026, or press the calendar button.
No date yet.
date-picker.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { DatePicker } from '@/components/klarhimmel/ui/date-picker';
import { Field } from '@/components/klarhimmel/ui/input';
export default function Example() {
return (
<Theme theme="brand">
<Field label="Arrival">
{(p) => <DatePicker {...p} defaultValue="2026-11-20" today="2026-11-16" />}
</Field>
</Theme>
);
}Install
Import
import { DatePicker } from '@/components/klarhimmel/ui/date-picker';- Lands in
- components/klarhimmel/ui/date-picker.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
- Render in
- Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
- Limit
- Overlays render into the Theme root with position: fixed. A transform on an ancestor re-parents fixed (a dialog centres in the wrapper) and overflow: hidden clips menus. Render <Theme> at the app root, outside any transformed wrapper.
Examples
Limits
The next six weeks. Try typing 2027-01-01.
import { DatePicker } from '@/components/klarhimmel/ui/date-picker'; import { Field } from '@/components/klarhimmel/ui/input'; export default function DatePickerLimits() { return ( <div className="flex min-h-96 w-[min(24rem,calc(100cqw-3rem))] flex-col gap-4"> <Field label="Check-in" hint="The next six weeks. Try typing 2027-01-01."> {(p) => <DatePicker {...p} defaultValue="2026-11-20" min="2026-11-16" max="2026-12-27" today="2026-11-16" />} </Field> <Field label="Birthday (disabled)"> {(p) => <DatePicker {...p} defaultValue="1988-05-04" disabled />} </Field> </div> ); }Form
import { useState } from 'react'; import { Button } from '@/components/klarhimmel/ui/button'; import { DatePicker } from '@/components/klarhimmel/ui/date-picker'; import { Field } from '@/components/klarhimmel/ui/input'; export default function DatePickerForm() { const [arrival, setArrival] = useState('2026-11-20'); const [departure, setDeparture] = useState(''); const [sent, setSent] = useState(''); return ( <form onSubmit={(e) => { e.preventDefault(); setSent(new URLSearchParams(new FormData(e.currentTarget) as unknown as Record<string, string>).toString()); }} className="flex min-h-[28rem] w-[min(24rem,calc(100cqw-3rem))] flex-col gap-4" > <Field label="Arrival"> {(p) => <DatePicker {...p} name="arrival" value={arrival} onValueChange={(v) => { setArrival(v); if (departure && v && departure <= v) setDeparture(''); }} today="2026-11-16" min="2026-11-16" />} </Field> <Field label="Departure" hint="Must be after arrival."> {(p) => <DatePicker {...p} name="departure" value={departure} onValueChange={setDeparture} today="2026-11-16" min={arrival ? new Date(Date.parse(`${arrival}T00:00:00Z`) + 86400000).toISOString().slice(0, 10) : '2026-11-17'} />} </Field> <Button type="submit" disabled={!arrival || !departure}>Search rooms</Button> <p role="status" className="break-all text-kh-small text-kh-muted">{sent ? `Sent: ${sent}` : 'The form posts both days as ISO text.'}</p> </form> ); }
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
value / defaultValue / onValueChange | string / (value: string) => void | - | The day as 'YYYY-MM-DD'; '' means none. Typed text is read on Enter and when leaving the field. |
min / max / isDisabled | string / string / (iso: string) => boolean | - | Days that cannot be chosen. Typing one is marked and explained in words. |
today / firstDay / locale | string / 'mon' | 'sun' / string | - / 'mon' / 'en' | Passed to the calendar. |
name | string | - | Submits the chosen day as ISO text with a form (a hidden input). |
placeholder | string | 'YYYY-MM-DD' | Shown while the field is empty. |
invalid / disabled | boolean | false | Error styling; switches it off. Unreadable text is marked invalid on its own. |
labels | { open; panel; previous; next; today; unreadable; outOfRange } | English words | The button, the dialog, the calendar's buttons and the two messages. Pass them translated. |
aria-label / aria-labelledby | string | - | Needed when no Field or <label htmlFor> names it. |
className | string | - | Extra classes, merged after the component's own. |
Accessibility
| Key | Does |
|---|---|
Type | 2026-11-16 or day first, 16/11/2026 |
Enter / leaving the field | Reads the text; wrong text is marked and explained |
Alt+Down on the field | Opens the calendar |
Enter / Space on the button | Opens the calendar on the chosen day |
Escape | Closes it and returns focus to the button |
Arrow keys, Page Up / Down | Move within the calendar (see Calendar) |
The WAI-ARIA date picker dialog. Choosing a day closes it and puts the ISO text in the field. A form gets the day as YYYY-MM-DD.
Accessibility