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.

TokenRepresents
--dt-bgBase background of a surface.
--dt-surfaceRaised surface (cards, controls).
--dt-textPrimary text color.
--dt-mutedSecondary / placeholder text.
--dt-accentBrand / selection color.
--dt-accent-softTranslucent accent for ranges & highlights.
--dt-on-accentText/icon color on top of the accent.
--dt-borderHairline borders & dividers.
--dt-hoverHover/active row background.
--dt-shadowElevation shadow for floating surfaces.
--dt-overlay-bgDim color behind modal sheets.
--dt-radius / --dt-radius-smCorner rounding.
--dt-gapBase spacing unit.
--dt-fontFont family stack.
--dt-z-popover / --dt-z-modalStacking 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) — reads prefers-color-scheme and 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:

scoped token override

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:

custom dark token set

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-soft and --dt-on-accent for 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.