import { applyTheme } from 'uxdsl/runtime'
// Once, with the override the project was built with ({} for none):
applyTheme(projectOverride, { replace: true, styleId: 'uxdsl-ssr-theme' })
// Then any patch, any time:
const result = applyTheme({ palette: { primary: { main: 'color(green.700)' } } })
if (!result.ok) console.error(result.error.code, result.error.message)
applyTheme validates the patch, generates the theme CSS and compares its structure with what the build compiled, all before it touches the page. ok: true means the managed stylesheet already holds the new values; ok: false means nothing moved — no CSS, no state, no notification. The rest of the browser API is getAppliedTheme, resetTheme, subscribeTheme and loadPersistedTheme({ key }). uxdsl/runtime does not load PostCSS.
It swaps custom properties; it cannot rewrite the rules a build compiled into your components. So a patch that would change which declarations a directive emits is refused with UXD_THEME_STRUCTURE and the instruction to rebuild:
{ breakpoints: { md: 900 } }) — every component's media queries carry it;typography_details field, or a Button or Input state the role does not emit;main/dark/contrast from a palette family used as a tone.Token values, responsive expressions over the same breakpoints, dark-mode colors and new tokens apply. Before the first call, every other call is UXD_THEME_NOT_INITIALIZED.
There is no state to share: generateThemeCss(theme) from uxdsl/theme is pure and returns the same string the build emits for that theme, so a server can render it per request and the browser can adopt it.
import { generateThemeCss } from 'uxdsl/theme'
const css = generateThemeCss(effectiveTheme) // render as <style id="uxdsl-ssr-theme">
Dark mode is part of the compiled theme, not a runtime patch: data-theme="dark" or "light" on <html> pins a mode, and with no attribute the OS preference decides. See Theme.
This site calls applyTheme(themes.default, { replace: true, styleId: 'uxdsl-ssr-theme' }) once, then applies the header's other themes the same way. These panels only read that state.
defaultgetAppliedTheme() — families in the applied overridegetAppliedTheme().palette.primary.maingetComputedStyle(document.documentElement).getPropertyValue('--uxdsl__palette__primary-main') — the computed custom propertyThe two can differ: the override holds the light value, and in dark mode the stylesheet applyTheme generated switches primary-main to modes.dark's — which is what the computed style reads.
subscribeTheme(listener)No application yet since this page loaded. Switch the theme in the header: each successful applyTheme notifies this listener.
On this page they would compete with the site's own theme, so they run in the frame below: a page compiled with this site's default theme, with its own document and its own copy of the runtime. Run the steps in order; applyTheme({ breakpoints: { md: 900 } }) is refused with UXD_THEME_STRUCTURE. Reload the sandbox to undo everything.
subscribeTheme heardRun a step. Each call's return value is shown as it came back.