Variables, mixins and JavaScript
css-pp variables do more than hold a value. A variable can be a plain value, a mixin with parameters, or a JavaScript expression evaluated in the page. Add a ! and it recalculates whenever the window is resized.
Defining and using a variable#
Define a variable on its own line, outside any rule, with a $, a name and the value in braces. Use it with $name. Definitions can go anywhere in the stylesheet, before or after the rules that use them.
$brand{#0ea5e9}
$gap{24px}
.button { background: $brand; margin-bottom: $gap; }Wrap the name in brackets when it touches other characters, so css-pp can tell where the name ends:
$size{1280}
.hero { background-image: url(/img/hero-$[size].jpg); }
.box { width: $[size]px; }Native CSS custom properties can't do this. A url() can't be assembled from var() pieces, but a css-pp variable is substituted as text before the browser sees it.
JavaScript expressions#
If a value is valid JavaScript, css-pp evaluates it; otherwise it is used as written. That makes calculations and logic one line long:
$half{css.vars.w() / 2} /* half the viewport width, as a number */
$cols{Math.max(1, Math.floor(css.vars.w() / 320))}
.panel { width: $[half]px; }
.grid { grid-template-columns: repeat($cols, 1fr); }A value can hold several statements. The value of the last expression is the result:
$img!{
if (css.vars.w() > 1600) "1920";
else if (css.vars.w() > 1024) "1600";
else "1024";
}
.hero { background-image: url(/img/hero-$[img!].jpg); }These helpers are available inside expressions:
| Helper | Returns |
|---|---|
css.vars.w() |
Viewport width in pixels |
css.vars.h() |
Viewport height in pixels |
css.vars.w('.selector', n) |
Width of the n-th element matching the selector (default 0) |
css.vars.h('.selector', n) |
Height of the n-th matching element |
Any global JavaScript works too, such as Math, Date, document or your own functions.
Security note. Expressions run with
eval(), just like code in a<script>tag. Only process stylesheets you control. If your Content Security Policy blocks'unsafe-eval', plain values, nesting and the state and content selectors still work, but expressions and built-ins such as$[index]do not.
Mixins with parameters#
A variable can hold a block of declarations. List default parameter values in parentheses and refer to them as &a, &b, &c and so on, in order:
$shadow(0, 8px){box-shadow: &a &b 24px rgb(0 0 0 / .18)}
$truncate(1){display: -webkit-box; -webkit-line-clamp: &a; -webkit-box-orient: vertical; overflow: hidden}
.card { $shadow(0, 12px); }
.excerpt { $truncate(3); }
.title { $truncate; } /* uses the default: 1 line */Arguments are positional and comma-separated. When you pass arguments, pass all of them; the defaults apply only when you call the mixin without parentheses.
The current selector: &self#
Pass &self as an argument and it becomes the selector of the rule that uses the variable, as a quoted string. That lets a variable measure or count the elements it is styling:
$count!(&self){document.querySelectorAll(&a).length}
/* split the row evenly, however many columns there are */
.cols > div { width: calc(100% / $[count!]); }The ! makes the count update after the page has loaded and when the window is resized (see below). This particular one is also built in as $[count].
Auto-updating variables#
End a variable's name with ! and css-pp recalculates it after the page finishes loading, whenever the window is resized and when the orientation changes. Every rule that uses it updates in place. Include the ! when you use it:
$vw!{css.vars.w()}
$vh!{css.vars.h()}
.full { height: $[vh!]px; }
.label::after { content: "$[vw!]px wide"; }Recalculation is throttled by css.defaults.resizing (100 ms by default).
Variables that reference variables#
A value can use other variables. css-pp resolves them before evaluating:
$base{16}
$large{$base * 1.5}
h1 { font-size: $[large]px; }Arrays: one value per element#
If a variable evaluates to an array, each matched element gets the next item. The values map to element positions, like :nth-child():
$palette{['#ef4444', '#f59e0b', '#10b981', '#3b82f6']}
.tags > li { background: $palette; }For values computed from an element's position, $[index] is usually simpler.
Changing variables at runtime#
Call css.set(sheet, name, value) to change a variable after the page has loaded. Every rule that uses it updates immediately, without reloading the stylesheet. sheet is the stylesheet name: the file name without its extension, or the id you passed to css.open('#id').
css.set('site', 'brand', '#e11d48'); // recolor every rule that uses $brand
css.get('site', 'brand'); // "#e11d48"Pass values as strings. css.set() ignores empty values such as 0 or '', so write '0' instead. More in the JavaScript API.
Comments#
Both comment styles are supported:
/* block comment */
.a { color: red; } // line comment, to the end of the lineA // counts as a comment only at the start of a line or after a space, ;, { or }. URLs such as url(https://cdn.example.com/a.png) and url(//cdn.example.com/a.png) are left alone.