Plugin system
Plugins transform computed tokens into concrete outputs (CSS, JSON, etc.). They are initialized once, and their onLoad() hook runs when api.load() is called. defineConfig wires two plugins for the standard Tailwind setup: TailwindPlugin and FontPlugin.
Architecture
Each plugin is a pair of classes:
PluginAbstract— configuration and lifecycle, instantiated by the consumerPluginImplAbstract— execution logic, instantiated by the API duringinit()
ts
// packages/theme — base class
abstract class PluginAbstract<Plugin, Options> {
options: Options;
// Returns options without functions (safe for postMessage / structuredClone)
toSerializable(): Options;
// Called by the API — creates the internal instance
init(api: API): this;
// Returns the internal instance (after init)
getInstance(): Plugin;
}
abstract class PluginImplAbstract<Options> {
protected api: API;
protected options: Options;
onInit?(): void; // called on initialization
onLoad?(): void; // called on every api.load()
}Execution order
Plugins execute in the order declared in plugins: []. If a plugin declares dependencies, the API ensures those are initialized first.
ts
plugins: [
new FontPlugin(), // initialized first
new TailwindPlugin(), // can access FontPlugin via this.api.plugins
];toSerializable()
Available on every PluginAbstract. Performs a JSON round-trip that strips functions — required to transfer options to a Web Worker via postMessage.
ts
const opts = api.plugins.getPlugin(TailwindPlugin).toSerializable();
// safe for structuredClone / postMessage
worker.postMessage({ tailwindOptions: opts });Creating a custom plugin
ts
import { PluginAbstract, PluginImplAbstract } from '@udixio/theme';
interface MyPluginOptions {
prefix?: string;
}
class MyPluginImpl extends PluginImplAbstract<MyPluginOptions> {
output = '';
onLoad() {
const prefix = this.options.prefix ?? 'my';
const primary = this.api.colors.get('primary').hex;
this.output = `--${prefix}-primary: ${primary};`;
}
}
export class MyPlugin extends PluginAbstract<MyPluginImpl, MyPluginOptions> {
dependencies = [];
name = 'myPlugin';
pluginClass = MyPluginImpl;
}ts
import { loader } from '@udixio/theme';
const api = await loader({
sourceColor: '#6750A4',
plugins: [new MyPlugin({ prefix: 'app' })],
});
await api.load();
console.log(api.plugins.getPlugin(MyPlugin).getInstance().output);
// "--app-primary: #6750A4;"Available API in onLoad
| Property | Type | Description |
|---|---|---|
this.api.context | Context | isDark, contrastLevel, variant, sourceColor |
this.api.palettes | PaletteApi | Computed palettes (get, add, override, sync) |
this.api.colors | ColorApi | Semantic tokens (get, getAll, addColor, addColors) |
this.api.plugins | PluginApi | Access other plugins (getPlugin) |