Guides

Extending the system

The shared core is exported, so you can build new overlay-based components on the same foundation the suite uses — and they inherit theming, focus management, and positioning for free.

Building a new component

Extend BaseComponent and implement the lifecycle hooks. The base class gives you option merging, a namespaced uid, an event surface, tracked listeners, a ThemeEngine, and the open()/close() scaffolding. You implement onMount(), onUnmount(), and optionally onDestroy().

import { BaseComponent, OverlayManager } from "./dist/ui-suite.esm.js";
import { createEl } from "./src/core/dom.js";

const DEFAULTS = { theme: "auto", onChange: null };

export default class ColorPicker extends BaseComponent {
  constructor(el, options = {}) {
    super(el, DEFAULTS, options, { prefix: "dt" }); // dt- keeps tokens applying
    this.listen(this.el, "click", () => this.open());
  }

  onMount() {
    this.body = createEl("div", { className: "dt-colorpicker-body" });
    this.overlay = new OverlayManager({
      anchor: this.el,
      mode: "popover",
      component: "colorpicker",
      panelClass: "dt-colorpicker",
      onRequestClose: () => this.close(),
    });
    const panel = this.overlay.open(this.body);
    this.theme.register(panel);     // tokens + light/dark for free
    this._render();
  }

  onUnmount() { this.overlay.close(); }

  _render() {
    // build DOM from createEl(...) using --dt-* tokens for all colors
  }
}

You get the hard parts for free

Portaling, viewport-aware positioning, outside-click and Escape handling, focus trap/restore, the overlay stack, and theme stamping all come from the core. Your component only owns its domain.

Naming conventions

  • Class names are PascalCase (DateTimePicker, SelectPicker).
  • CSS classes and attributes use the dt- prefix (dt-scope, dt-panel, dt-option) so they share the token namespace.
  • Pass { prefix: "dt" } to super(...) so your uid and data attributes line up with the suite and tokens apply.
  • Design tokens are always --dt-*; introduce new ones sparingly and document them.
  • Events are lowercase (change, open, close); paired option callbacks are onChange, onOpen, onClose.

Where logic belongs

The guiding rule: infrastructure goes in the core; domain logic stays in the component.

ConcernLives in
Overlay rendering, positioning, dismissalCore — OverlayManager, PositioningEngine
Focus trap & restoreCore — FocusManager
Theming & light/darkCore — ThemeEngine
Form binding & hidden valuesCore — InputAdapter
Large-list windowingCore — VirtualList
Value model, parsing, formattingComponent
Domain UI (calendar grid, option rows)Component
Selection rules & keyboard semanticsComponent

If you find yourself re-implementing positioning, focus, or theming inside a component, that logic likely belongs in (or already exists in) the core. Reuse the primitive instead of duplicating it — that is exactly how DateTimePicker and SelectPicker stay small and consistent.

Available core exports

import {
  BaseComponent, OverlayManager, PositioningEngine,
  FocusManager, ThemeEngine, InputAdapter, VirtualList,
  EventEmitter, InertialWheel, resolveTouch,
} from "./dist/ui-suite.esm.js";