Tokens and vars
Author a nested value tree. createDistillery derives CssToken / ScaleToken leaves and the themeVars registry. Branded values stringify through Symbol.toPrimitive, which is why they interpolate into tagged templates.
const distillery = createDistillery({
prefix: 'eui',
themeScope: '.eui-view',
theme: {
colors: {
ink: lightDark('#111', '#eee'),
warning: '#FACB3D',
},
},
});
const { ink } = distillery.tokens.colors;
String(ink);
ink.cssVar;
ink.path;
- Interpolates as
var(--eui-colors-ink). This is what tagged templates splice in. - Custom-property name:
--eui-colors-ink. - Slash path matching
themeVarskeys:colors/ink.
A string leaf is scheme-invariant (light === dark). lightDark(light, dark) is scheme-varying and both sides must be CSS <color> values. Emission uses light-dark(light, dark) when the two values differ. Unread theme tokens are pruned from the theme block.
zipSchemes(light, dark) folds two per-scheme trees into this form: equal strings stay bare, differing strings become lightDark, and ScaleToken leaves must agree.
Named variations go on variations in createDistillery. Declaring a variation does not emit it; name it at renderStyles with { flatten } or { alternates }. Variations extend the base only. ScaleToken leaves must match the base because they inline and cannot vary. See declare and select variations.
themeToken(path, cssVar) remains the constructor derivation calls. Paths are slash-delimited (colors/ink), matching themeVars keys.
A surface that cannot resolve var(--x) (a headless SVG rasterizer, for example) should call distillery.resolveValues(scheme, variation?) instead of walking themeVars by hand. That walk returns nested literal strings for one scheme, including ScaleToken.value. A surface that still wants the stylesheet, but has no color scheme for light-dark() to resolve against, passes { scheme } to renderStyles. See read values outside CSS.
The chip module below uses tokens.colors.surface from the quick start token tree.
const gap = cq('8px', '2cqi');
String(gap);
gap.cq;
- Inlines as
8px. No custom property, nothing to prune. - Container-relative value:
2cqi. Use this when the declaration should track container size.
cq is an alias of scaleToken.
const foreground = contextualVar(
'vars/app/tone/foreground',
'--eui-app-tone-foreground'
);
String(foreground);
String(foreground.name);
- Interpolating the var reads it:
var(--eui-app-tone-foreground). - Writing
.namedeclares the property:--eui-app-tone-foreground.
Register the path in sharedVars so two modules can share the name without either owning it.
const chip = distillery.createStyleModule('chip', ({ css, tokens, vars }) => {
const look = vars('look', {
bg: tokens.colors.surface,
unusedBorder: tokens.colors.accent,
});
return {
root: css`
${look}
background: ${look.bg};
`,
loud: css`
${look.set({ bg: tokens.colors.accent })}
`,
};
});
- Never referenced, so this key is pruned — and so is the
colors.accentdefault it would have pulled in. - Emits default declarations (
--eui-chip-look-bg: var(--eui-colors-surface)). - Reads the var.
- Override on this handle. Override entries emit after defaults, so stacking
rootand the modifier class wins.
Local-var names are cssVarName(prefix, path) (--${prefix}-${module}-${group}-${key}). A collision with a theme or shared var throws at module construction. See name collisions.