Why these two helpers
num() and count() are the foundation for advanced loops, keyframe generators, and chart-style calculations (including st-core@v2). They run at compile time (or in the browser runtime) and emit plain numbers into CSS.
num(expression)
+ - * / with optional unit after )
count(limit[, step])
Sequences for index arrays and loops
With @arr
Length-driven indexes, sum/min/max, normalized %
<script src="https://cdn.jsdelivr.net/npm/fscss@1.2.5/runtime.min.js" async></script>
1. num() — compile-time math
Arithmetic in stylesheets
Corenum(expression) evaluates +, -, *, / when FSCSS runs. Append a unit immediately after the closing parenthesis.
Critical rule — grouping
Do not use parentheses (), braces {}, or brackets [] inside the expression (they conflict with CSS/FSCSS parsing).
Use angle brackets < > for grouping:
num(<100 - 1> / <4 + 50>)%
/* Basic */
selector {
max-height: num(40 * 4)px; /* → 160px */
}
/* Grouping with < > */
something {
$res: num(<100 - 1> / <4 + 50>)%; /* → ~1.85% */
}
/* With random */
textarea {
max-height: num(@random([40, 10, 5, 0]) + 50);
}
/* Nested / complex (v1.2.3+) */
.box {
width: num(<45 - 2> / 2 * <5 - 3>)px;
}
Unit after ):
num(10 * 2)px ✓
num(10 * 2 px) ✗
Grouping:
num(<a - b> / c) ✓
num((a - b) / c) ✗ inside expr
Variables:
num($n! * 2)rem
num(@arr.x[$i!] * 1.5)px
2. count() — number sequences
Build index lists
Corecount(limit) /* 1, 2, 3, …, limit */
count(limit, step) /* from step, by step, up to limit */
From v1.2.1+, the limit may be an array’s .length:
exec(_log, "count(5)") /* → 1, 2, 3, 4, 5 */
exec(_log, "count(10, 2)") /* → 2, 4, 6, 8, 10 */
@arr colors[#ef4444, #f59e0b, #10b981, #3b82f6]
@arr indexes[count(@arr.colors!.length)] /* → 1,2,3,4 */
/* or: count(@arr.colors!.length, 1) */
@arr idx[count(@arr.data!.length, 1)]
/* then loop */
.item:nth-child(@arr.idx[]) { … }
rpt(@arr.idx[], "div ") { … }
inline("empty-@arr.idx[] { … }")
3. Patterns with arrays
Index array for looping
Most common@arr colors[#ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6]
@arr indexes[count(@arr.colors!.length, 1)]
empty{ /* preserve */ }
.loading-dot:nth-child(@arr.indexes[]) {
$index: @arr.indexes[];
background: @arr.colors[$index!];
animation-delay: num(@arr.indexes[] * 0.15)s;
}
Derived values (normalize / CSS variables)
v1.2.3+@arr data[30, 55, 80, 45, 90]
@arr idx[count(@arr.data!.length, 1)]
empty-@arr.idx[] {
$i: @arr.idx[];
--st-p$i: num(100 - @arr.data[$i!])%;
}
Same idea as chart point inversion in st-core (100 - value → Y% for clip-path).
Math inside rpt() bodies
@arr sizes[8, 12, 16, 24]
@arr levels[count(@arr.sizes!.length)]
empty{ /* preserve */ }
rpt(@arr.levels[], ".item ") {
padding: num(@arr.sizes[@arr.levels[]] * 1.5)px;
font-size: num(@arr.sizes[@arr.levels[]] + 4)px;
}
Numeric array methods + num()
@arr nums[10, 20, 30, 40, 50]
.total { width: num(@arr.nums!.sum)px; }
.min-w { width: num(@arr.nums!.min)px; }
.max-w { width: num(@arr.nums!.max)px; }
Also: @arr.n!.length with count() for index generation.
Quick reference
| Feature | Syntax | Notes |
|---|---|---|
| Basic math | num(a * b)px |
Unit after ) |
| Grouping | num(<a - b> / <c + d>) |
Use <> only |
| Sequence | count(5) / count(10, 2) |
From 1 or from step |
| From length | count(@arr.name!.length) |
v1.2.1+ |
| Index array | @arr idx[count(@arr.data!.length, 1)] |
Loop helper |
| Array numerics | !.sum / .min / .max / .length |
With num() |