Custom Styling
Customize the timepicker appearance to match your brand
.tp-ui-select-timeHeader label..tp-ui-hourHour field..tp-ui-clock-faceClock face with the hour and minute tips..tp-ui-footerFooter with the buttons..tp-ui-wrapperThe picker's root element..tp-ui-type-modeAM/PM switch in 12-hour mode..tp-ui-minutesMinute field..tp-ui-clock-handClock hand.
CSS Variables
#Timepicker-UI uses CSS custom properties (variables) for easy customization. Override these variables to change colors and styles:
Color and Shape Tokens
Defaults of the basic theme. Each built-in theme sets its own values.
:root {/* Surfaces */--tp-bg: #ece6ee;--tp-surface: #e6e0e9;--tp-surface-hover: #c8b3fd;--tp-input-bg: #e6e0e9;/* Text */--tp-text: #1d1b20;--tp-text-secondary: #49454f;--tp-text-disabled: rgba(156, 155, 155, 0.6);--tp-text-type-time: #49454f;--tp-text-icon: #4e545a;/* Primary and containers (Material Design 3 roles) */--tp-primary: #6750a4;--tp-on-primary: #ffffff;--tp-primary-container: #eaddff;--tp-on-primary-container: #21005d;/* AM/PM */--tp-am-pm-active: #ffd8e4;--tp-am-pm-text-selected: #633b48;--tp-am-pm-text-unselected: #49454f;/* Borders and shape */--tp-border: #000;--tp-outline: #6750a4;--tp-shadow: unset;--tp-border-radius: 28px;/* Font (see "Font" below) *//* --tp-font-family: not set, falls back to 'Roboto', sans-serif *//* Focus ring (see "Focus Ring" below) *//* --tp-focus-ring: not set, falls back to --tp-primary *//* --tp-shadow-focus-ring: not set, defaults to none *//* Timezone dropdown */--tp-dropdown-option-bg: #ffffff;--tp-dropdown-option-text: #000000;/* --tp-dropdown-option-hover-text: not set, falls back to--tp-dropdown-option-text. Text color of a hovered orkeyboard-focused option. */}
Themes other than basic set their values on .tp-ui-modal[data-theme], which wins over :root. To override a token for every theme, set it on .tp-ui-wrapper. For one picker, use the class you pass in ui.cssClass (.tp-ui-wrapper.my-theme).
Tokens that do nothing
No rule reads --tp-tertiary-container, --tp-on-tertiary-container, --tp-hover-bg or --tp-radius. Setting them changes nothing, and 5.0 removes them.
Font
To change the font, set --tp-font-family. .tp-ui-wrapper reads it with font-family: var(--tp-font-family, 'Roboto', sans-serif), and the time fields and buttons in the modal inherit it. Set the variable on :root or .tp-ui-modal for every picker, or on the class you pass in ui.cssClass for one picker. The picker adds that class to .tp-ui-wrapper.
/* ui: { cssClass: 'my-theme' } */.tp-ui-wrapper.my-theme {--tp-font-family: 'Inter', system-ui, sans-serif;}
Setting font-family directly on .tp-ui-wrapper.my-theme also works.
Seconds Sizes
Used when clock.showSeconds is on, so three fields fit in the header, the wheel and the range tabs.
:root {/* Header fields, 12h */--tp-size-input-seconds-12h: 64px;--tp-size-input-seconds-12h-narrow: 58px; /* screens 330px wide or less */--tp-size-colon-seconds-12h: 12px;--tp-font-size-seconds-12h: 36px;/* Header fields, 24h */--tp-size-input-seconds-24h: 80px;--tp-size-input-seconds-24h-narrow: 74px; /* 330px or less, and landscape up to 480px tall */--tp-size-colon-seconds-24h: 20px;--tp-font-size-seconds-24h: 44px;/* Wheel and range tabs */--tp-wheel-column-width-seconds: 64px;--tp-wheel-separator-width-seconds: 16px;--tp-range-tab-value-font-size-seconds: 14px;}
Keyboard View in Landscape
Size of the keyboard input view on landscape screens up to 480px tall.
:root {--tp-mobile-landscape-width: 328px;--tp-mobile-landscape-height: 258px;--tp-mobile-landscape-height-range: 287px; /* range plugin */--tp-mobile-landscape-height-timezone: 326px; /* timezone selector */}
Compact-Wheel Popover
Used when compact-wheel opens as a popover next to the input (wheel.placement is set). The popover is at least as wide as the input and --tp-popover-min-width, at most --tp-popover-max-width, and never closer to the viewport edge than --tp-popover-edge-margin.
:root {--tp-popover-gap: 4px; /* space between the input and the popover */--tp-popover-edge-margin: 4px; /* smallest space to the viewport edge */--tp-popover-min-width: 260px;--tp-popover-max-width: 328px;--tp-popover-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);/* --tp-popover-radius: not set, falls back to --tp-border-radius */}
The gap, edge margin and widths are read from .tp-ui-modal, so set them on :root or .tp-ui-modal, not on .tp-ui-wrapper. Any CSS length works, for example px, rem or calc().
Scrollbar Gap
--tp-scrollbar-gap is set by the picker, not by you. While a picker locks page scroll (ui.allowPageScroll: false, the default), it is set on <html> to the width of the removed scrollbar, or 0px when there was none. Read it in your own CSS to keep fixed elements in place. See Scroll lock and fixed headers.
Embedded card surface
#An embedded inline picker (ui.inline.embedded) takes its card background, corner radius and backdrop filter from three variables. They fall back to --tp-bg, --tp-radius-md and --tp-backdrop-filter. New in 4.7.0.
.my-dialog .tp-ui--embedded {--tp-embedded-bg: transparent;--tp-embedded-radius: 0;--tp-embedded-backdrop-filter: none;}
Compact keyboard view
#Shrink the time fields of the keyboard view with three variables. --tp-size-input is the field width, --tp-size-input-mobile the height on narrow screens and --tp-time-input-font-size the size of the numbers. New in 4.7.0.
.tp-ui-wrapper {--tp-size-input: 72px;--tp-size-input-mobile: 56px;--tp-time-input-font-size: 36px;}
Focus Ring
#Keyboard focus shows one 3px ring in --tp-focus-ring, 2px outside the control. When --tp-focus-ring is not set, the ring uses --tp-primary. The width and offset come from --tp-outline-width-thick and --tp-outline-offset.
- AM/PM and the time fields draw the ring inside their edge. On the selected field it uses
--tp-on-primary-container, on the selected AM/PM button--tp-am-pm-text-selected. - Range tabs and wheel columns use the same 3px ring. Only the keyboard-focused timezone option gets a thinner 2px ring inside its edge, set by
--tp-outline-width. - With
prefers-contrast: morethe ring is 4px. In forced-colors mode it uses the systemHighlightcolor. --tp-shadow-focus-ringadds abox-shadowto the ring. It defaults tonone.- There is no
--tp-focus-ring-halotoken any more. Setting it has no effect.
.tp-ui-wrapper.my-theme {--tp-focus-ring: #0b57d0;/* Optional, off by default */--tp-shadow-focus-ring: 0 0 0 6px rgba(11, 87, 208, 0.25);}
Creating Custom Themes
#Timepicker-UI comes with several built-in themes:
Available Themes
import { TimepickerUI } from 'timepicker-ui';// Material Design 2 theme (purple/magenta)const picker1 = new TimepickerUI(input1, { ui: { theme: 'm2' } });// Material Design 3 theme (green)const picker2 = new TimepickerUI(input2, { ui: { theme: 'm3-green' } });// Dark themeconst picker3 = new TimepickerUI(input3, { ui: { theme: 'dark' } });// Crane theme with rounded cornersconst picker4 = new TimepickerUI(input4, { ui: { theme: 'crane' } });// Crane-straight theme (sharp edges)const picker5 = new TimepickerUI(input5, { ui: { theme: 'crane-straight' } });
To make your own theme, give the picker a class with ui.cssClass and set the CSS variables under .tp-ui-wrapper.your-class. The picker adds the class to its .tp-ui-wrapper when it opens. ui.theme accepts only the 12 built-in names.
Custom Theme Example
.tp-ui-wrapper.my-theme {--tp-bg: #ffffff;--tp-text: #000000;--tp-primary: #6200ee;--tp-on-primary: #ffffff;--tp-surface: #e0e0e0;--tp-surface-hover: #ece0fd;--tp-input-bg: #e4e4e4;--tp-border: #d6d6d6;--tp-text-secondary: #a9a9a9;--tp-text-type-time: #787878;--tp-text-icon: #4e545a;--tp-text-disabled: rgba(156, 155, 155, 0.6);--tp-shadow: 0 10px 35px 0 rgba(0, 0, 0, 0.25);--tp-border-radius: 4px;}
Using Your Custom Theme
import { TimepickerUI } from 'timepicker-ui';const picker = new TimepickerUI('#timepicker', {ui: { cssClass: 'my-theme' }});picker.create();
You can also start from a built-in theme: ui: { theme: 'dark', cssClass: 'my-theme' }. Your values win because the wrapper sits inside the themed modal.
Switching Themes at Runtime
There is no setTheme() method. Change ui.theme or ui.cssClass with setOptions(). The picker builds its modal when it opens, so the new theme shows the next time it opens. A picker that is open re-renders right away. Load the CSS file of every theme you switch to.
import 'timepicker-ui/main.css';import 'timepicker-ui/theme-dark.css';const picker = new TimepickerUI('#timepicker', {ui: { theme: 'basic' }});picker.create();picker.setOptions({ ui: { theme: 'dark' } });
Material Design 3 Ripple Effect
#Pressing a control shows a ripple that grows from the press point. Hover, keyboard focus and press also show a state layer, a faint overlay in the control's text color. Keyboard activation does not start a ripple. With prefers-reduced-motion, a press shows only the pressed layer, without the ripple.
Where It Applies
- OK, Cancel and Clear buttons
- AM/PM buttons
- Hour, minute and second fields
- The view-switch icon
- Range tabs (range plugin) and the timezone trigger (timezone plugin)
Customizing the Ripple
Controls with the effect carry the data-md3-ripple attribute. Their ::before is the state layer and ::after is the ripple. Both use the control's text color (currentColor). On the selected time field that is --tp-on-primary-container. Strength and timing come from these tokens (defaults shown):
.tp-ui-wrapper.my-theme {--tp-state-hover-opacity: 0.08;--tp-state-focus-opacity: 0.1;--tp-state-pressed-opacity: 0.1;--tp-ripple-opacity: 1; /* multiplies the pressed opacity for the ripple */--tp-duration-ripple: 700ms;--tp-duration-ripple-fade: 500ms;}/* Turn the ripple off and keep the state layers */.tp-ui-wrapper.no-ripple {--tp-ripple-opacity: 0;}
Complete Custom Theme Example
#Here's a complete example of a custom purple theme:
.tp-ui-wrapper.purple {--tp-bg: #faf5ff;--tp-text: #1e1b4b;--tp-primary: #8b5cf6;--tp-on-primary: #ffffff;--tp-surface: #ede9fe;--tp-surface-hover: #ddd6fe;--tp-input-bg: #f3e8ff;--tp-border: #c4b5fd;--tp-text-secondary: #7c3aed;--tp-text-type-time: #6d28d9;--tp-text-icon: #7c3aed;--tp-text-disabled: #a78bfa;--tp-shadow: 0 10px 35px 0 rgba(139, 92, 246, 0.3);--tp-border-radius: 16px;--tp-font-family: 'Inter', system-ui, sans-serif;}
import { TimepickerUI } from 'timepicker-ui';import 'timepicker-ui/main.css';import './theme-purple.css';const picker = new TimepickerUI('#timepicker', {ui: { cssClass: 'purple' }});picker.create();
- Browser Compatibility
- CSS custom properties are supported in all modern browsers. For older browsers, provide fallback values or use a PostCSS plugin to generate static CSS. Test your custom themes across different browsers to ensure consistent appearance.
- View built-in themes