Variable Utilities
Define CSS variables directly in class names. Variable utilities make tokens, themes, component variants, and interactive values available exactly where they are needed.
Syntax
A utility variable starts with a CSS custom property name and uses the equal sign to assign its value. Underscores become spaces inside the generated value, and bracket syntax can protect special characters.
--primary=blue--primary: blue--gap=1rem_2rem--gap: 1rem 2rem--curve=cubic-bezier(0.16,1,0.3,1)--curve: cubic-bezier(0.16,1,0.3,1)@dark:--primary=cyan@media (prefers-color-scheme: dark) { ... }Variables Are Regular Utilities
Scoped Tokens
Variables inherit through the DOM, so a token defined on a wrapper is available to every child inside it. This is the easiest way to create component-level themes without writing a separate CSS selector.
Coral Scope
Every primary utility class reads the local token.
Violet Scope
The markup stays the same; the token changes.
<div class="--primary=coral">
<button class="bgc-primary-100 br brc-primary-300 c-primary-700">Save</button>
</div>
<div class="--primary=violet">
<button class="bgc-primary-100 br brc-primary-300 c-primary-700">Save</button>
</div>
Global Tokens
Add variables to the html element when the entire application should share the same design tokens. Utilities then read those values through Maple's fallback chains.
<html class="
--primary=blue @dark:--primary=lightblue
--spacer=0.35
--rad-lg=12px
">
<body>
<button class="bgc-primary-100 c-primary-600 p-4 rad-lg">
Global tokens
</button>
</body>
</html>
SPAs and MPAs
Fallback Chains
Hyphen utilities resolve through variables before falling back to their raw value. This lets one token serve many utilities, while property-specific variables can override a token only where needed.
c-primary--c-primary -> --color-primary -> --primaryText color can have its own override. See Color Resolution
bgc-primary--bgc-primary -> --color-primary -> --primaryBackground color can share or override the same token. See Color Resolution
p-card--p-card -> --space-card -> --cardSpacing tokens can be semantic instead of numeric. See Number Resolution
ff-mono--ff-mono -> --monoAny token can be defined on any selector. See Custom Resolution
Base scope
All colors and spacing are based on primary and space-card scoped variables.
Property-specific overrides
Background color, text color, and padding are overridden with bgc-primary, c-primary, and p-card scoped variables.
<div class="--primary=olive --space-card=2.5rem">
<div class="bgc-primary-100 br brc-primary-300 c-primary-500 p-card">
Base scope
</div>
<div class="--bgc-primary=tomato --c-primary=peru --p-card=1.5rem">
<div class="bgc-primary-100 br brc-primary-300 c-primary-500 p-card">
Property-specific overrides
</div>
</div>
</div>
State and Media Variables
Change variables instead of repeating long sets of utilities. The child utilities stay stable, while parent, hover, breakpoint, or color-scheme selectors update the tokens they consume.
<!-- Self selector -->
<button class="--primary=gray &:hover:--primary=aqua bgc-primary-100 c-primary-700">
Hover me
</button>
<!-- Media query -->
<section class="--primary=coral @dark:--primary=aqua">
<div class="bgc-primary-100 c-primary-900">Theme-aware content</div>
</section>
<!-- Breakpoint -->
<div class="--space-card=1rem md:--space-card=2rem p-card">
Responsive padding
</div>
Dynamic Variables
Use the $$ prefix for values that change frequently, such as slider values, pointer coordinates, or scroll-driven values. Dynamic classes are written to Maple's ephemeral layer so the main stylesheet does not collect a new permanent rule for every value.
const slider = document.getElementById('slider');
const preview = document.getElementById('preview');
slider.addEventListener('input', (event) => {
const value = event.target.value;
preview.classList.add(`$$--p-spacer=${value}`, `$$--g-spacer=${value}`);
});
slider.addEventListener('change', (event) => {
const value = event.target.value;
preview.classList.add(`--p-spacer=${value}`, `--g-spacer=${value}`);
});