Customize
Styling and theming
Every visual decision in the picker can be changed, at whichever level suits your project:
- CSS variables. Colours, sizes, radii and weights, a few lines per brand.
classNames. Your own classes on every part and state, for Tailwind, Bootstrap or utility CSS.- Plain CSS. Stable
dxd-*classes,is-*state classes and ARIA attributes. decorateandicons. Per-day badges, classes and labels; replacement SVGs.- Your own UI.
@devix-labs/date-picker/coregives you month grids, rules, parsing and presets without any markup.
The Theming demo shows five brands built from variables alone.
Stylesheets
| File | Use it when |
|---|---|
styles.css / date-picker.min.css |
The default: plain CSS that holds up on any page |
styles.layer.css |
Your app uses cascade layers. Everything sits in @layer devix, so your rules win without !important |
@import 'tailwindcss';
@import '@devix-labs/date-picker/styles.layer.css';
Variables
Set them on .dxd (the field wrapper) and .dxd-panel (the calendar). The calendar renders in the
browser's top layer, so target it directly rather than relying on inheritance.
.dxd,
.dxd-panel {
--dxd-accent: #0d6efd;
--dxd-cell-radius: 6px;
--dxd-panel-radius: 8px;
}
Size, shape and type
| Variable | Default | |
|---|---|---|
--dxd-height |
36px |
Minimum input height |
--dxd-radius |
8px |
Toggle button corners inside the field |
--dxd-font-size |
14px |
Input text |
--dxd-panel-font-size |
13px |
Calendar text |
--dxd-panel-radius |
12px |
Calendar corners |
--dxd-panel-padding |
8px |
Space inside the calendar |
--dxd-cell |
32px |
Day cell size (sized to the screen on a phone sheet; larger when days have badges) |
--dxd-cell-radius |
999px |
999px circles, 6px rounded squares, 0 squares |
--dxd-day-font-size |
13px |
Day numbers |
--dxd-font-weight |
400 |
Regular text |
--dxd-font-weight-strong |
500 |
Title, selected days, active preset |
Colour (light defaults, then dark)
| Variable | Light | Dark |
|---|---|---|
--dxd-bg |
#fff |
#111114 |
--dxd-text |
#18181b |
#ededf0 |
--dxd-muted |
#71717a |
#9b9ba6 |
--dxd-faint |
#a1a1aa |
#5f5f69 |
--dxd-border |
#e4e4e7 |
#2c2c33 |
--dxd-border-hover |
#c9c9d0 |
#3d3d46 |
--dxd-accent |
#ea4b71 |
same |
--dxd-accent-text |
#fff |
same |
--dxd-ring |
accent at 28% | same |
--dxd-band |
accent at 10% | accent at 18% |
--dxd-hover |
#f4f4f5 |
#232329 |
--dxd-danger |
#dc2645 |
same |
--dxd-panel-bg |
#fff |
#18181c |
--dxd-panel-shadow |
soft shadow | deeper shadow |
--dxd-divider |
#ececef |
#26262c |
Dark mode
theme: 'auto' is the default, and the picker looks at your page:
- The nearest theme marker on an ancestor:
.dark/.light,data-theme,data-bs-theme,data-modeordata-color-scheme. - Otherwise, the actual background colour behind the field, including
oklch()values from Tailwind v4.
It re-checks when those attributes change on <html> or <body>, when the OS setting changes, and each
time the calendar opens. Force it with theme: 'light' | 'dark', and style dark mode with:
.dxd-panel[data-theme='dark'] {
--dxd-panel-bg: #0f172a;
--dxd-border: #1e293b;
--dxd-hover: #1e293b;
}
Matching popular design systems
shadcn/ui:
.dxd, .dxd-panel {
--dxd-panel-bg: var(--popover);
--dxd-text: var(--popover-foreground);
--dxd-muted: var(--muted-foreground);
--dxd-border: var(--border);
--dxd-hover: var(--accent);
--dxd-accent: var(--primary);
--dxd-accent-text: var(--primary-foreground);
--dxd-band: var(--accent);
--dxd-cell-radius: calc(var(--radius) - 2px);
--dxd-panel-radius: var(--radius);
}
Bootstrap 5.3:
.dxd, .dxd-panel {
--dxd-panel-bg: var(--bs-body-bg);
--dxd-text: var(--bs-body-color);
--dxd-muted: var(--bs-secondary-color);
--dxd-border: var(--bs-border-color);
--dxd-accent: var(--bs-primary);
--dxd-band: rgba(var(--bs-primary-rgb), 0.12);
--dxd-cell-radius: var(--bs-border-radius);
--dxd-panel-radius: var(--bs-border-radius-lg);
}
Give the input Bootstrap's own form-control class too. The picker's dxd-input class only sets its
width, height and the space the toggle button needs.
Stripe-like:
.dxd, .dxd-panel { --dxd-accent: #635bff; --dxd-band: #efeeff; --dxd-panel-radius: 8px; --dxd-cell: 34px; }
Tailwind classes on every part
createDateRangePicker(input, {
classNames: {
panel: 'rounded-xl shadow-xl ring-1 ring-black/5',
title: 'font-semibold',
daySelected: 'font-semibold',
dayInRange: 'text-indigo-900 dark:text-indigo-100',
preset: 'rounded-md',
presetActive: 'bg-indigo-50 text-indigo-700',
actionPrimary: 'bg-indigo-600',
},
});
Parts:
- Field:
root,input,toggle,panel - Header:
header,title,nav - Grid:
months,month,monthTitle,grid,weekday,day - Day states:
daySelected,dayToday,dayDisabled,dayInRange,dayRangeStart,dayRangeEnd - Footer and presets:
footer,summary,action,actionPrimary,presets,preset,presetActive - Month and year views:
pick
State classes stay in sync while the pointer previews a range.
Classes and states
| Selector | |
|---|---|
.dxd, .dxd--open |
Field wrapper; open |
.dxd-input, .dxd-input--end, .dxd-toggle |
Inputs and the calendar button |
.dxd-panel |
The calendar. Modifiers: --inline, --sheet, --presets, --presets-top, --weeks |
.dxd-header, .dxd-nav[data-nav], .dxd-title |
Heading and navigation |
.dxd-months, .dxd-month, .dxd-month-title, .dxd-grid |
Month layout |
.dxd-day |
A day. States: .is-selected, .is-today, .is-disabled, .is-in-range, .is-range-start, .is-range-end, .is-preview, and .is-week-start / .is-week-end for the first and last column |
.dxd-num, .dxd-second, .dxd-badge |
Day number, secondary-calendar day, badge |
.dxd-picklist, .dxd-pick.is-current |
Month and year views |
.dxd-presets, .dxd-preset.is-active |
Preset list |
.dxd-footer, .dxd-summary.is-problem, .dxd-action, .dxd-action--primary |
Footer |
[data-theme='light' | 'dark'] |
Resolved theme on .dxd and .dxd-panel |
[aria-selected], [aria-disabled], [aria-current='date'] |
ARIA state on day cells |
For anything else — holidays, weekends in your business's calendar, booked days — add a class with
decorate:
.dxd-day.is-holiday .dxd-num { color: #dc2626; }
createDatePicker(input, { decorate: (iso) => holidays.has(iso) && { className: 'is-holiday', label: 'Public holiday' } });
Icons
createDatePicker(input, {
icons: {
calendar: '<svg viewBox="0 0 24 24">…</svg>', // the button in the field
previous: '<svg viewBox="0 0 24 24">…</svg>',
next: '<svg viewBox="0 0 24 24">…</svg>',
},
});
In right-to-left pages the previous and next icons swap sides automatically.
Density
The defaults are compact. For a roomier layout:
.dxd, .dxd-panel {
--dxd-height: 44px;
--dxd-font-size: 16px;
--dxd-cell: 40px;
--dxd-day-font-size: 14px;
--dxd-panel-padding: 12px;
}
Layout
- Side-by-side months:
months: 2or3. When the window is too narrow the picker shows fewer, and presets move above the grid before that happens. - Phones: a bottom sheet with thumb-sized days on touch screens. Use
sheet: falseortrueto force either way. - Position: the calendar opens below the field, or above when there's more room there, and never leaves the screen.
Your own UI (headless)
import { monthGrid, getCalendar, compileRules, parseDate, PRESETS } from '@devix-labs/date-picker/core';
const grid = monthGrid(getCalendar('gregory'), 2026, 9, 1); // six weeks of { day, label, outside }
Everything the widget knows — calendars, week starts, rules, typed dates, presets, comparison periods — works without its markup, so you can render the calendar with your own components.