Custom functions
A custom function is a piece of Java that SCSS can call like any built-in. Register it under its SCSS signature and jsass marshals the arguments and the return value across the engine boundary for you.
SassCallback pow = ctx -> {
var args = ctx.getArgs();
var base = (SassNumber) args.get(0);
var exp = (SassNumber) args.get(1);
return SassNumber.of(Math.pow(base.getValue(), exp.getValue()), base.getUnit());
};
var options = StringOptions.builder()
.url(URI.create("virtual:root.scss"))
.functions(Map.of("pow($base, $exp)", SassFunction.builder().callback(pow).build()))
.build();
The map key is the full SCSS signature, argument names included — that is how dart-sass learns the
arity and the parameter names. The SassFunctionContext handed to the callback carries that same
signature plus the args in declaration order, as an unmodifiable list.
Values
All values crossing the boundary implement the sealed interface SassValue. There are seven
implementations and each has static factories; the constructors are not public.
| Type | Create it with | Read it with |
|---|---|---|
SassNumber |
SassNumber.of(3.5, "px"), SassNumber.unitless(42) |
getValue(), getUnit() |
SassString |
SassString.quoted("a"), SassString.unquoted("a") |
getValue(), isQuoted() |
SassColor |
SassColor.rgb(59, 130, 246, 1.0) and one factory per space |
getSpace(), getChannel1() … getAlpha() |
SassBoolean |
SassBoolean.of(true), SassBoolean.TRUE, SassBoolean.FALSE |
isValue() |
SassNull |
SassNull.NULL |
— |
SassList |
SassList.comma(items), .space(items), .slash(items) |
getContents(), getSeparator(), isBracketed() |
SassMap |
SassMap.of(map) |
getContents() |
Lists and maps defensive-copy their contents and are immutable afterwards; SassList.bracketed()
returns a bracketed copy rather than mutating.
Colors
SassColor carries a color-space tag, three channels and an alpha. Use the factory for the space
you are working in — the channel meaning follows from it:
| Space | Factory | Channels |
|---|---|---|
RGB, SRGB, SRGB_LINEAR, DISPLAY_P3, A98_RGB, PROPHOTO_RGB, REC2020 |
SassColor.rgb(r, g, b, a), .srgb(…), .srgbLinear(…), .displayP3(…), .a98Rgb(…), .prophotoRgb(…), .rec2020(…) |
red, green, blue |
HSL |
SassColor.hsl(h, s, l, a) |
hue, saturation, lightness |
HWB |
SassColor.hwb(h, w, b, a) |
hue, whiteness, blackness |
LAB / OKLAB |
SassColor.lab(…) / SassColor.oklab(…) |
lightness, a, b |
LCH / OKLCH |
SassColor.lch(…) / SassColor.oklch(…) |
lightness, chroma, hue |
XYZ_D50 / XYZ_D65 |
SassColor.xyzD50(…) / .xyzD65(…), .xyz(…) |
x, y, z |
rgb() validates its channels (0–255) and the alpha (0–1); the other spaces accept unbounded
channels — gamut mapping is deliberately out of scope.
RGB does not survive a round trip
dart-sass normalises the legacy rgb space to srgb on read-back. A color built with
SassColor.rgb(…) therefore comes back as ColorSpace.SRGB. Use SassColor.srgb(…) when
you compare a returned color against an expected one.
A lookup function
Anything the callback can reach is fair game — a configuration object, a design-token map, a database:
private static final Map<String, SassColor> THEME = Map.of(
"primary", SassColor.rgb(59, 130, 246, 1.0),
"secondary", SassColor.hsl(217, 91, 60, 1.0));
SassCallback themeColor = ctx -> {
var name = ((SassString) ctx.getArgs().get(0)).getValue();
var color = THEME.get(name);
if (null == color) {
throw new IllegalArgumentException("Unknown theme color: " + name);
}
return color;
};
When a function fails
Argument typing is the callback's job: cast to the SassValue subtype you expect. If the cast
fails, or the callback throws anything else, jsass wraps it as a
SassFunctionExecutionException and dart-sass reports it as a compilation error pointing at the
call site. The original exception stays available through getCause().
In other words: throwing from a custom function is a supported way to reject bad input. It produces a normal Sass error, not a crashed compiler.