xPulse
πŸ‡¬πŸ‡§ EN

API

init(options = {})

import theme from '@xpulse/theme';
await theme.init();
// With options
await theme.init({ root: process.cwd(), force: false });

Builds the CSS bundle, writes the cache, registers HTTP routes, and hooks into http:response:before β€” from that point on, the CSS bundle, anti-FOUC script, and theme.js are automatically injected into every HTML response. themeAssets() in templates is optional.

Option Type Description Default
root string Base directory β€” controls where src/themes/ is discovered process.cwd()
force boolean Ignore cache and rebuild the bundle false

init() is idempotent β€” subsequent calls are ignored.


href()

theme.href();
// β†’ '/_theme/css/xpulse.css'

URL of the bundled CSS (base + components + all themes, minified).

scriptHref()

theme.scriptHref();
// β†’ '/_theme/theme.js'

URL of the browser runtime script (theme toggle, token editor).


current()

theme.current();
// β†’ 'dark'

Current default theme from config (theme.default in xpulse.json).

available()

theme.available();
// β†’ ['dark', 'light']

All themes discovered in the bundle via [data-theme="..."] selectors.

allowed()

theme.allowed();
// β†’ ['dark', 'light']

Subset of available(), filtered by theme.allow in xpulse.json. When theme.allow is empty or not set, returns all available themes.

has(name)

theme.has('dark'); // β†’ true
theme.has('foo'); // β†’ false

Checks whether a theme is known in the bundle.


set(name)

theme.set('light'); // β†’ true
theme.set('foo'); // β†’ false (unknown or not allowed)

Sets the current theme. Returns false if the theme is unknown or not in allowed().

toggle()

theme.toggle();

Toggles between the two allowed themes. Returns false if allowed() does not have exactly 2 entries.


Registered Routes

Route Description
GET /_theme/css/xpulse.css Minified all-in-one CSS bundle
GET /_theme/theme.js Browser runtime script

Template Methods

`themeAssets()`

{% themeAssets() %}

Renders:

`themeSwitcher()`

{% themeSwitcher() %}

Renders nothing if allowed() contains only one theme. With exactly dark + light β†’ compact moon/sun toggle button. With more than 2 themes β†’ <select> dropdown.

`themeTokenEditor()`

{% themeTokenEditor() %}

Live editor for selected :root tokens. Sets CSS custom properties directly in the browser and persists overrides in localStorage.


Discovery

Theme names are extracted from the bundled CSS via regex:

[data-theme="dark"] β†’ 'dark'
[data-theme="light"] β†’ 'light'

CSS source files are bundled in this order:

1. public/css/base.css
2. public/css/components/*.css (via @import in components.css)
3. public/css/themes/*.css (built-in themes)
4. src/themes/*.css (app overrides, optional)

Caching

The bundle is cached at var/cache/theme/xpulse.css.

{
"theme": {
"cache": {
"enabled": true,
"ttl": 0
}
}
}

ttl: 0 means no expiry. ttl: 3600 invalidates the cache after 1 hour.


Events

Bundle build

Event Payload When
themes:load:start β€” Build starts
theme:basics:loaded β€” base.css loaded
theme:components:loaded β€” All component CSS loaded
theme:loaded { name } Single theme CSS file loaded
themes:concatenated β€” All parts joined
themes:minified β€” Bundle minified
themes:cached { path } Bundle written to disk
themes:load:end β€” Build complete
theme:ready { theme, available } init() complete

Cache

Event Payload When
themes:cache:hit { path } Valid cache file found
themes:cache:miss β€” No cache, bundle will be rebuilt
themes:cache:loaded { serveCount } Bundle request served

Runtime

Event Payload When
theme:changed { from, to } set() / toggle() succeeded
theme:warning { type, … } Invalid/disallowed theme, toggle not possible
theme:error { type } No themes found in bundle
en/api.md 2026-03-26