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): () => voidA 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): () => voidFires 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>): stringResolves 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): () => voidFires 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): () => voidCursor 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.