Sass (SCSS) compatibility
Jess can parse SCSS input and move supported Sass constructs into Jess's native model.
Native Jess uses $name for live/current references and $$name for
scoped/final lookup.
What SCSS adds beyond CSS
Sass extends CSS with (at least) the following features:
- Variables:
$var: value;(Sass variables) - Interpolation:
#{...}in selectors, property names, and values (Interpolation) - Mixins/includes/content blocks:
@mixin,@include,@content,using (...)(Mixins) - Control directives:
@if,@for,@each,@while,@error,@warn,@debug(Control directives) - Module system:
@use,@forward, configuration viawith (...)(use, forward) - Maps and lists: including built-in helpers like
map.get()(Maps, sass:map) - Extend + placeholders:
@extendand%placeholders(extend, placeholders) - @at-root: explicit control over nesting output (at-root)
- Nested declarations (“property nesting”) (declarations)
At-rule mapping: Sass → Jess
Jess keeps compiler-level at-rules prefixed with - to avoid collisions with the evolving CSS standard library.
@use (stylesheets) → @-compose
Sass:
@use './theme' as theme;
.box { color: theme.$primary-color; }
Jess equivalent:
@-compose './theme' as theme;
.box { color: $theme.primary-color; }
Jess stylesheet imports support namespaces, configuration, and export/protection flags.
@use (scripts) → @-use / @-from
In Jess, @-use is the namespace form for JS/TS/JSON modules. Like Sass, it can
infer the namespace from the module name when as is omitted. @-from is the
ESM-style form for named imports or import * as ns namespace imports.
When parsing SCSS, Jess will treat @use as a stylesheet import by default, but if the @use path clearly targets a script module (for example ./tokens.js, ./tokens.ts, ./tokens.json), it will parse to a JS import node instead.
@forward (SCSS) → @-export
Jess models Sass forwarding semantics with @-export (see At-rules).
Sass maps → Jess collections
Sass maps become Jess collections.
Sass:
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
Jess shape:
$font-weights: {
regular: 400;
medium: 500;
bold: 700;
}
Key normalization rules
A collection key is a value, matched by value equality — the same model Sass
maps use, where map.get($m, 1px) must match the number 1px and not the text
"1px".
- Quoted string keys with no interpolation are treated as plain keys.
"regular"becomes the bareregular, which is the string key"regular".
- Other Sass map keys (numbers, colors, lists) stay the values they are; see Namespaces, collections, and maps for the key model and for why a numeric subscript is a position rather than a key.
map-get() and map.get() → Reference lookups
Sass commonly accesses map keys via:
@use "sass:map";
.box {
font-weight: map.get($font-weights, "medium");
}
Jess does not treat this as a normal function call. Instead, the SCSS parser desugars:
map-get($map, $key[, ...])map.get($map, $key[, ...])
into a Reference lookup chain:
- Evaluate the map expression (often
$font-weights) - Look up the key(s) as nested property/index accesses
This is the same core mechanism Jess uses for $theme.colors.primary and array/map-like access.
Slash division and arithmetic
Sass treats / as either:
- division
- or a plain slash separator in a value
Jess does not try to preserve the whole ambiguity set when importing SCSS.
Jess supports the clear arithmetic forms:
math.div(...)- isolated parenthesized arithmetic like
(15px/30px)
Jess does not try to infer division from every legacy Sass slash form in general value position. If a slash form is ambiguous, Jess prefers to keep it as a slash-separated value rather than guess.
This aligns with Jess’s own language model:
- real math should be explicit
- imported SCSS arithmetic should only be recognized when the syntax is clearly arithmetic
- native Jess expressions use
$(...)
SCSS interpolation mapping
Jess distinguishes:
- a variable reference:
$var - an expression:
$(...) - an interpolation:
${ident}(identifier and string positions;${[ident]}to read a property rather than a variable) - a lookup:
$[key]/$m[key](value position)
#{$...} in selector/property names
When SCSS interpolation appears in selector or property-name positions:
#{$ident}(single variable reference) maps to${ident}- More complex interpolation expressions are rewritten through a generated private temp variable (
_tmp_<n>), then referenced as${_tmp_<n>}
This keeps selector/property interpolation identifier-only while still supporting complex expressions.
#{$...} in values/strings
In value/string contexts, SCSS interpolation may map to:
${ident}inside a string$(...)for expression interpolation in value position
For example, #{$color} can become $($color) when live-reference expression semantics are needed.
Interpolation in mixin names
SCSS does not allow interpolation in mixin names. Jess follows that behavior:
@include foo-#{$bar}()is rejected@mixin foo-#{$bar}is rejected
Placeholder selectors (%foo)
Sass placeholder selectors don’t emit CSS unless they’re extended.
Jess parses:
%fooas selector name\foo
This name conversion makes placeholders syntactically distinguishable from normal selectors while still being a selector-like key in the registry.
Placeholder visibility (current state)
Sass placeholders are “silent” until extended. In Jess today, placeholder parsing is supported, but full Sass-equivalent placeholder visibility behavior remains partial. Use this area with extra verification in migration-heavy code paths.
Cross-module behavior
Sass’s module system allows extending placeholders across module boundaries, with private placeholders (leading _ or -) restricted to their defining module (see Sass’s placeholder docs above). Jess aims for parity here, but behavior is still being finalized.
Variable flags: !global and !default
Sass supports !global and !default on variable assignments (variables).
Jess’s SCSS support intentionally does not implement Sass’s full !global shadowing model. It lowers !global to the scoped/final set-existing/no-shadow operation ($$name := value). SCSS !default corresponds to the scoped/final conditional operation ($$name?: value). Both $name: and $$name: create or update both bindings in native Jess.
!default exists in Sass primarily to support module configuration (@use ... with (...)). Jess will align with Sass’s @use configuration behavior by ensuring configurable variables are explicitly marked and handled accordingly.