CSS Token Hoisting #

CSS Token Hoisting is a build-time optimization that discovers which CSS custom properties (design tokens) your application actually uses, and includes only those in the protocol output. This enables host runtimes to resolve design tokens efficiently without shipping unused variables.

How It Works #

During the build process, WebUI extracts CSS custom property usages - names referenced via var(--name) - from two sources:

  1. Component CSS files - tokens are extracted when components are registered and cached in the component registry.
  2. Inline <style> tags - tokens are extracted from <style> elements in the entry HTML file and component templates.

The resulting set of tokens is sorted, deduplicated, and included in the protocol's tokens field.

What Gets Hoisted #

Only var() usages are hoisted - not custom property definitions:

/* โœ… HOISTED - usage via var() */
.button {
  color: var(--colorPrimary);           /* โ†’ "colorPrimary" */
  background: var(--bgColor);           /* โ†’ "bgColor" */
}

/* โŒ NOT hoisted - this is a definition */
:root {
  --colorPrimary: #0078d4;
}

Nested Fallbacks #

Fallback variables in var() calls are also extracted:

.card {
  /* All three tokens are extracted: "a", "b", "c" */
  color: var(--a, var(--b, var(--c)));
}

Literal fallback values (like 16px) are ignored - only variable references are extracted.

Local Definition Exclusion #

If a custom property is both defined and used in the same CSS file, it is excluded from the token set. This prevents locally-scoped variables from being hoisted:

:host {
  --internal-spacing: 8px;             /* definition */
  padding: var(--internal-spacing);     /* usage */
  color: var(--designSystemColor);      /* usage only โ†’ HOISTED */
}
/* Result: only "designSystemColor" is hoisted */

Comment-Based Token Bindings #

To create a placeholder where token values will be injected at render time, use an HTML comment with handlebars syntax:

<style>
  :root {
    <!--{{tokens}}-->
  }
</style>

The &lt;!--{{tokens}}--&gt; comment is parsed as a Signal fragment with value "tokens". At render time, the host can replace this signal with actual CSS variable declarations based on the hoisted token list and the active theme.

Binding Syntax #

SyntaxResult
&lt;!--{{tokens}}--&gt;Signal fragment (escaped)
&lt;!--{{{tokens}}}--&gt;Signal fragment (raw/unescaped)
&lt;!--{{tokens.light}}--&gt;Signal fragment with dotted path
<!-- regular comment -->Preserved as raw content

This mechanism is general-purpose - any {{identifier}} inside an HTML comment becomes a signal fragment.

Protocol Output #

The hoisted tokens appear in the protocol's tokens field:

{
  "fragments": { ... },
  "tokens": [
    "bgColor",
    "borderRadiusSmall",
    "colorBrandBackground",
    "colorPrimary",
    "fontFamilyBase",
    "lineHeightBase400",
    "spacingHorizontalM"
  ]
}

The list is always sorted alphabetically and deduplicated across all components and inline styles.

CLI Output #

When tokens are discovered, the build command reports the count:

โœ” Registered 5 components
โœ” Parsed index.html (23 fragments)
โœ” Discovered 12 CSS tokens
โœ” Build complete (3 files written) in 42ms