Skip to content
Devix Open Source

Customize

Styling and theming

Every visual decision in the picker can be changed, at whichever level suits your project:

  1. CSS variables. Colours, sizes, radii and weights, a few lines per brand.
  2. classNames. Your own classes on every part and state, for Tailwind, Bootstrap or utility CSS.
  3. Plain CSS. Stable dxd-* classes, is-* state classes and ARIA attributes.
  4. decorate and icons. Per-day badges, classes and labels; replacement SVGs.
  5. Your own UI. @devix-labs/date-picker/core gives 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:

  1. The nearest theme marker on an ancestor: .dark / .light, data-theme, data-bs-theme, data-mode or data-color-scheme.
  2. 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: 2 or 3. 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: false or true to 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.

Updated 15 Sep 2026