System
Theming
doticca-ui is themed entirely with CSS custom properties. There are no theme props to thread through components and no JavaScript styling — you override tokens, and every surface the suite renders updates in lockstep.
1 · The design-token system
A design token is a named CSS variable that represents one
decision in the visual language — a color, a radius, a shadow. The suite defines
its tokens with the --dt- prefix on the .dt-scope class,
which is the single source of truth for the look of every component.
Components never hardcode colors. Internally they reference tokens
(background: var(--dt-surface), color: var(--dt-text)),
so changing a token re-skins every place it is used. Every panel the suite renders —
inline, popover, or portaled modal — carries the dt-scope class, so
tokens cascade identically no matter where the DOM ends up.
| Token | Represents |
|---|---|
--dt-bg | Base background of a surface. |
--dt-surface | Raised surface (cards, controls). |
--dt-text | Primary text color. |
--dt-muted | Secondary / placeholder text. |
--dt-accent | Brand / selection color. |
--dt-accent-soft | Translucent accent for ranges & highlights. |
--dt-on-accent | Text/icon color on top of the accent. |
--dt-border | Hairline borders & dividers. |
--dt-hover | Hover/active row background. |
--dt-shadow | Elevation shadow for floating surfaces. |
--dt-overlay-bg | Dim color behind modal sheets. |
--dt-radius / --dt-radius-sm | Corner rounding. |
--dt-gap | Base spacing unit. |
--dt-font | Font family stack. |
--dt-z-popover / --dt-z-modal | Stacking order for surfaces. |
2 · Theme layers
The system is layered so that each concern overrides only what it needs:
- Base tokens — defined once on
.dt-scope. This is the default light theme and the contract every component reads from. - Surface & background — structural styles (overlay shells, the inertial wheel, virtual list) live in a base layer that consumes tokens but adds no colors of its own.
- Component-level styling — each component’s stylesheet styles only its own internals (calendar grid, option rows), again purely through tokens.
Because color decisions live exclusively in the token layer, you retheme the whole suite without touching component or structural CSS.
3 · Light & dark behavior
The suite ships a dark token set under
.dt-scope[data-dt-theme="dark"]. Which set applies is decided by the
ThemeEngine, driven by each component’s theme option:
"auto"(default) — readsprefers-color-schemeand live-updates when the OS theme changes."light"/"dark"— pins the theme regardless of the system.
The engine resolves the mode and stamps data-dt-theme on the scoped
nodes (control, panel, overlay). In auto mode it subscribes to the
media query, so themes switch automatically — no remount, no flicker.
.dt-scope { --dt-bg: #fff; --dt-text: #1c1c1e; /* …light… */ }
.dt-scope[data-dt-theme="dark"] { --dt-bg: #1c1c1e; --dt-text: #f5f5f7; /* …dark… */ }
Try it
The theme toggle in the top bar of these docs drives the exact same mechanism. Flip it and watch every example — including portaled popovers — follow.
4 · How to customize the theme
Override tokens globally
Redefine any token on .dt-scope (or :root alongside it) to change the whole suite.
.dt-scope {
--dt-accent: #0f766e; /* brand teal */
--dt-accent-soft: rgba(15, 118, 110, 0.14);
--dt-radius: 10px;
}
Scope a theme to one container
Tokens cascade, so overriding them on a wrapper retheme only that subtree. This lets different sections of an app carry different brands.
.checkout {
--dt-accent: #0f766e;
--dt-radius: 10px;
}
/* Any picker rendered for inputs inside .checkout uses these values. */
The picker below lives in a container with the teal overrides above — only this demo is affected:
Force a custom dark palette
You can define your own dark values rather than the defaults. Here a forced-dark container uses an amber accent:
Switch themes at runtime
Call setTheme() on any instance to repaint it immediately.
const picker = new UISuite.SelectPicker("#country", { options });
picker.setTheme("dark"); // pin dark
picker.setTheme("light"); // pin light
picker.setTheme("auto"); // follow the OS again
5 · Best practices
- Never hardcode colors near the components — change tokens instead, so light/dark and brand variants stay correct.
- Always pair related tokens. When you change
--dt-accent, also set--dt-accent-softand--dt-on-accentfor correct contrast. - Override at the smallest sensible scope. Global for app-wide branding, a wrapper for section-specific theming.
- Define both light and dark when pinning a custom palette, so the experience holds in either system mode.
- Keep one token source. Maintain overrides in a single place to avoid drift across components.