Skip to main content

Themes

A theme is a token set the host applies across the whole app.

import { function defineMod<const TMod extends ModDefinition>(definition: TMod & ValidateModHooks<TMod>): Mod
The entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.
@example```ts export default defineMod({ metadata: { displayName: 'Review', description: 'Review helpers for the current change.', }, commands: [ command({ id: 'review.open', title: 'Review: Open', run: openPanel }), slashCommand({ id: 'review', description: 'Review this change', run: review }), ], ai: { tools: [reviewTool], }, }); ```@categoryAuthoring
defineMod
, function theme(input: ThemeInput): Theme
Register a theme so it appears in the workbench theme picker. Purely declarative. There is no handler and nothing runs at author time. The token values themselves live in the module you point at, written with `defineModularTheme`; this constructor only gives them an id, a label, and a place in the picker.
@categoryAuthoring
theme
} from '@modular/sdk';
export default
defineMod<{
    readonly metadata: {
        readonly displayName: "Midnight";
        readonly description: "A dark theme";
    };
    readonly theme: readonly [Theme];
}>(definition: {
    readonly metadata: {
        readonly displayName: "Midnight";
        readonly description: "A dark theme";
    };
    readonly theme: readonly [Theme];
}): Mod
The entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.
@example```ts export default defineMod({ metadata: { displayName: 'Review', description: 'Review helpers for the current change.', }, commands: [ command({ id: 'review.open', title: 'Review: Open', run: openPanel }), slashCommand({ id: 'review', description: 'Review this change', run: review }), ], ai: { tools: [reviewTool], }, }); ```@categoryAuthoring
defineMod
({
metadata: {
    readonly displayName: "Midnight";
    readonly description: "A dark theme";
}
metadata
: { displayName: "Midnight"displayName: 'Midnight', description: "A dark theme"description: 'A dark theme' },
theme: readonly [Theme]theme: [function theme(input: ThemeInput): Theme
Register a theme so it appears in the workbench theme picker. Purely declarative. There is no handler and nothing runs at author time. The token values themselves live in the module you point at, written with `defineModularTheme`; this constructor only gives them an id, a label, and a place in the picker.
@categoryAuthoring
theme
({ ThemeInput.id: stringid: 'midnight', ThemeInput.label: stringlabel: 'Midnight', ThemeInput.module: string
Path to the module holding the colors, which default-exports a `defineModularTheme({...})` value.
module
: './theme' })],
});

A mod may contribute more than one theme, so the slot is a list even when it holds a single entry.

Two functions, two jobs

FunctionProducesUsed for
themeThemeid, label, description, modulethe contribution — registering a theme with the host
defineThemeModularThemetokensthe content — the tokens themselves

theme() names a theme and points at the module that defines it. defineTheme(definition, cssPolicy) is what that module exports.

The split means the host can list available themes without loading every one of them.

Tokens

ModularTheme has a single member: tokens. Everything a theme controls goes through it, and tokenAccessor is the exported helper for reading tokens back.

To build token values from other token values, see Colors.

The cssPolicy argument

defineTheme takes a second argument alongside the definition. It governs what CSS the theme is permitted to emit — a theme is not an arbitrary stylesheet, and that boundary is enforced rather than documented.