Datepicker overview
Headless Datepicker gives you the same calendar logic as the wrapper without owning the field markup. Use it when the product needs a custom shell, a custom input rhythm, or a more directed drill-down flow than the stock component surface.
Imports
Headless imports
ts
import {
bindTngDatepicker,
createDatepickerController,
TngDatepickerDayCell,
TngDatepickerDayGrid,
TngDatepickerHost,
TngDatepickerInput,
TngDatepickerMonthGrid,
TngDatepickerMonthOption,
TngDatepickerNextButton,
TngDatepickerOverlay,
TngDatepickerPeriodButton,
TngDatepickerPrevButton,
TngDatepickerTrigger,
TngDatepickerYearOption,
TngDatepickerYearGrid,
} from '@tailng-ui/primitives';
import { TngIcon } from '@tailng-ui/icons';
Usage baseline
Start with one controller, one registered trigger, and one overlay anchor. The controller owns date math, input parsing, bounds, and keyboard state. Your template only mirrors its outputs.
Controller baseline
ts
readonly controller = createDatepickerController<Date>({
ownerDocument: document,
value: '2024-04-22',
today: '2024-04-18',
minDate: '2024-04-01',
maxDate: '2026-03-31',
closeOnSelect: true,
trapFocus: true,
showOutsideDays: true,
});
readonly datepicker = bindTngDatepicker(controller);
// Use the host/input/trigger/nav/grid/cell directives to forward
// the common field, calendar, and keyboard wiring into the primitive.
Datepicker variants
Compare the same controller-driven Datepicker flow across plain CSS and Tailwind CSS shells while keeping the primitive in charge of calendar behavior.
Datepicker (Plain-CSS)
Datepicker (Tailwind CSS)
Invoice date
Behavior baseline
- The controller owns manual input parsing, bounds, and selected-value commits.
- The overlay directive portals the popup while preserving Datepicker slot attrs and tokens.
- Outside-month days can stay visible with
showOutsideDays, but remain clearly disabled when blocked. - Register the same trigger element you render so focus restoration and combobox semantics stay aligned.