ReferenceAPI

Time, theme, and cursor

Reference for dd.time ticks, dd.theme tokens and accent helpers, and dd.cursor hover and movement events.

dd.time

Needs the time permission.

dd.time.now()

dd.time.now(): Promise<{ epochMs: number }>

dd.time.onTick()

dd.time.onTick(cb: (tick: { epochMs: number }) => void): () => void

A second-aligned wall-clock tick. Prefer it over your own setInterval for clock-like widgets: it stays aligned to the real second boundary.

dd.theme

Always available, no permission needed. Themes are applied by the host; the SDK re-applies the token map to :root automatically, so plain CSS using var(--dd-*) restyles itself. These methods are for JS that resolves colors manually (canvas, inline styles).

dd.theme.tokens()

dd.theme.tokens(): Promise<Record<string, string>>

The current --dd-* token map, as the host knows it. Note: manifest font pins are injected as CSS on top of this map, so tokens() does not see them; dd.ui.token() reads computed style and does. Resolve font faces through dd.ui.token().

dd.theme.onChange()

dd.theme.onChange(cb: (tokens: Record<string, string>) => void): () => void

Fires live on every theme change (theme picker, wallpaper auto-theme, user overrides). If you resolve token values in JS, do not cache them across this event: re-read and redraw.

dd.theme.accent()

dd.theme.accent(settings: Record<string, unknown>): string

Resolves the accentColor settings preset pair: the theme accent while useAccent is on (the default), the user's custom color otherwise. It reads the current token value, so re-call it inside dd.theme.onChange (or use bindAccent) to stay theme-reactive.

dd.theme.bindAccent()

dd.theme.bindAccent(cb: (color: string) => void): Promise<() => void>

settings.bind and theme.onChange in one: cb fires with the resolved accent immediately and again on every settings or theme change. The returned promise resolves (with the combined unsubscribe) after that first call.

dd.cursor

Always available, no permission needed, and it works for non-interactive widgets too: the window stays click-through, you get the signal without capturing input.

dd.cursor.onHover()

dd.cursor.onHover(cb: (state: { inside: boolean }) => void): () => void

Fires when the real cursor enters or leaves this widget's footprint. While a popout is open, the footprint is the bounding box of widget plus pane (crossing the gap between them never flickers a leave), and both documents receive the event.

dd.cursor.onMove()

dd.cursor.onMove(cb: (pos: { x: number; y: number }) => void): () => void

Cursor position at roughly 30 Hz while the cursor is inside this widget's (or its pane's) rect, as fractions (0..1) of the receiving document's viewport. For element-level hover effects without input capture. For the common case, skip the subscription entirely: mark elements data-dd-hover and the SDK toggles a dd-hover class on them for you.