timepicker-ui
4.6.0GitHub
timepicker-ui
Guides / Custom Styling

Date Picker Custom Styling

Restyle @pglejzer/datepicker-ui, the companion of timepicker-ui, with --dp-ui-* CSS variables, dp-ui__* classes and ui.cssClass.

How styling works

#

The picker is plain CSS. Colors, sizes, corners and type come from --dp-ui-* custom properties, and every part has a dp-ui__* class. ui.cssClass adds your own class to the surface root, so you can set variables or write rules for one picker.

LayerExamplesUse it to
System tokens--dp-ui-sys-color-*, --dp-ui-sys-shape-corner-*, --dp-ui-sys-typescale-*Recolor, reshape or retype everything that reads them.
Component tokens--dp-ui-container-*, --dp-ui-date-*, --dp-ui-text-field-*, --dp-ui-focus-ring-*Change one part, or one size, without touching the rest.
Classes.dp-ui__date-indicator, .dp-ui__text-button--confirmSet properties that have no token, such as a cell radius.

The system tokens follow the Material Design 3 roles and are set with light-dark(). Component tokens derive from them, so changing --dp-ui-sys-color-primary also recolors the selected day, the today outline, the focus ring and the text buttons.

guide
import DatepickerUI from "@pglejzer/datepicker-ui";
import "@pglejzer/datepicker-ui/main.css";
import "./guide.css";
new DatepickerUI("#date", { ui: { cssClass: "guide" } }).create();
guide.css
.guide {
--dp-ui-sys-color-primary: light-dark(#0b6b3a, #7fdba0);
--dp-ui-sys-color-on-primary: light-dark(#ffffff, #00391c);
--dp-ui-container-shape-docked: 8px;
}

Tokens or classes

#

Start with tokens. They survive library updates better than selectors, and they follow light and dark for free when you write them with light-dark(). A component token set on your class wins over the value derived from a system token:

index.css
.brand {
--dp-ui-sys-color-primary: #a04000;
--dp-ui-sys-color-on-primary: #ffffff;
}
.brand {
--dp-ui-date-selected-container-color: #7a2e00;
}

Use a class rule only for a property with no token. Write the selector with your class first, so it is more specific than the library rule:

index.css
.brand .dp-ui__date-indicator {
border-radius: 12px;
}
.brand .dp-ui__text-button--confirm {
text-transform: uppercase;
}

The full lists are on the CSS Variables and CSS Classes pages.

Where to set a variable

#

The picker declares every token again on the host, on the modal <dialog> and on each docked or inline container. A value set higher up is replaced by the default on those elements:

index.css
:root,
.dp-ui {
--dp-ui-sys-color-primary: #a04000;
}

Set tokens on the cssClass element

Set them on the class you pass in ui.cssClass. The picker adds it to the surface root, which is the .dp-ui__container element for a docked or inline picker and the <dialog> for a modal.

Scope one picker

#

A class only reaches the pickers that receive it. Give each picker its own name and the rules stay apart:

index.ts
new DatepickerUI("#start", { ui: { cssClass: "brand" } }).create();
new DatepickerUI("#end", { ui: { cssClass: "plain" } }).create();

To limit a class to one part of the page, put an ancestor selector in front of it:

index.css
.checkout :is(.dp-ui__container, .dp-ui__dialog).brand {
--dp-ui-sys-color-primary: #a04000;
}

This needs the picker inside that ancestor. A docked popover stays where the host is in the DOM. The modal dialog is appended to the host unless you set ui.appendTo, so with appendTo: document.body an ancestor selector no longer matches and the class alone has to carry the rules.

Docked popover and modal dialog

#

A docked or inline picker is a .dp-ui__container. A modal is a <dialog class="dp-ui__dialog">, and the container sits inside it. ui.cssClass lands on whichever one is the root. Name both in the selector, so one rule works in every display mode:

index.css
:is(.dp-ui__container, .dp-ui__dialog).brand {
--dp-ui-sys-color-primary: #a04000;
--dp-ui-sys-color-on-primary: #ffffff;
}

displayMode auto

With ui.displayMode: "auto" the same picker is a modal on a narrow or short screen and docked on a wide one. A rule for only .dp-ui__container would style one and miss the other.

Inside a CSS Module, a bare :global(.brand) fails the purity check. Keep one local class in the selector, for example .scope :global(:is(.dp-ui__container, .dp-ui__dialog).brand).

Dark mode and colorScheme

#

The system colors use light-dark(), which reads the color-scheme of the element. The picker sets it from ui.colorScheme, and "auto" detects it when the picker opens. Write each color as a pair and your style follows the scheme:

index.css
.brand {
--dp-ui-sys-color-primary: light-dark(#a04000, #ffb68a);
--dp-ui-sys-color-on-primary: light-dark(#ffffff, #4f2300);
}

If your design has one fixed look, force the scheme and use plain values. The scheme then also decides the native parts of the page, such as scrollbars:

index.ts
new DatepickerUI("#date", {
ui: { cssClass: "brand", colorScheme: "dark" },
}).create();

See Color scheme for how auto picks.

Start from a built-in theme or from main.css

#

main.css holds the core and the basic theme. It is the smallest base and the best choice for a fully custom look:

index.ts
import DatepickerUI from "@pglejzer/datepicker-ui";
import "@pglejzer/datepicker-ui/main.css";
import "./brand.css";
new DatepickerUI("#date", { ui: { cssClass: "brand" } }).create();

To adjust a built-in theme instead, load its file and add your class with ui.cssClass. A theme sets its values on [data-theme], which is more specific than a bare class. Add [data-theme] to your selector so your values win:

brand.css
:is(.dp-ui__container, .dp-ui__dialog).brand[data-theme] {
--dp-ui-sys-color-primary: #a04000;
--dp-ui-container-shape-docked: 8px;
}
index.ts
import DatepickerUI from "@pglejzer/datepicker-ui";
import "@pglejzer/datepicker-ui/theme-crane.css";
import "@pglejzer/datepicker-ui/main.css";
import "./brand.css";
new DatepickerUI("#date", {
ui: { theme: "crane", cssClass: "brand" },
}).create();

Only basic and blueprint follow ui.colorScheme. The other themes keep their own scheme, so a light-dark() pair will resolve to one side. See the Themes page for the list.

#
Esc