Sass → CSS in pure Rust, at parity with dart-sass. No Node, no libsass.
1
stars
2,125
commits
Rust
primary language
Sep 13, 2026
updated
Sass infrastructure for Rust, at parity with dart-sass.
accent-sass compiles Sass to CSS in
pure Rust, with no Node and no libsass. It is a fork of
connorskees/grass that carries the
modern Dart Sass features upstream has not released, so that a Rust program
can build real-world stylesheets -- Bulma, Pico, Foundation, USWDS -- without
shelling out to another toolchain.
Parity with dart-sass is the goal, not an aspiration to approximate it: a
deviation from the reference implementation is a bug, except in error messages
and error spans.
It is built for and maintained alongside Accent CMS, the single-binary markdown CMS, which compiles theme Sass in-process through this crate. It is a general-purpose library, and does not depend on Accent.
Documentation: https://zoosky.github.io/accent-sass -- a usage guide, a
reference, and a demo that
compiles Bulma and USWDS from source in your browser. The site is built from
docs/ with Accent CMS, which compiles its stylesheet with
this compiler.
accent-sass = "0.16.0"
To track work that has not been released yet, pin a git revision instead:
accent-sass = { git = "https://github.com/zoosky/accent-sass.git", rev = "<commit>" }
As a library:
fn main() -> Result<(), Box<accent_sass::Error>> {
let css = accent_sass::from_string(
"a { b { color: &; } }".to_owned(),
&accent_sass::Options::default(),
)?;
assert_eq!(css, "a b {\n color: a b;\n}\n");
Ok(())
}
The API is deliberately small: from_string, from_path,
from_string_with_file_name, and an Options builder. Options::fs takes any
Fs implementation, so a host that already holds its stylesheets can compile
them without writing them to disk first -- MemoryFs is one such
implementation, and is what the browser build resolves imports through.
As a binary, intended as a drop-in for the sass executable:
accent-sass input.scss # compile to stdout
accent-sass --check app.scss app.css # verify app.css is up to date
--check compiles and writes nothing, exiting 3 when the output file is
stale or missing and 1 when the stylesheet does not compile, so a CI job can
tell "your CSS is out of date" from "your Sass is broken".
As a browser package, published to npm as
@zoosky/accent-sass:
npm install @zoosky/accent-sass
import init, { compileString } from "@zoosky/accent-sass";
await init();
const { css, loadedUrls } = compileString('@use "theme";', {
files: {
"theme/_colors.scss": "$brand: #bada55 !default;",
"theme/_index.scss": '@forward "colors";',
},
style: "expanded",
logger: (event) => console.warn(event.message),
});
Every file a compile might touch must be in files before you call it.
Reads are synchronous: the compiler asks for a file and gets bytes back, with
nothing to await, so an importer cannot fetch, cannot await, and cannot
reach the File System Access API. An editor loads the theme's stylesheets into
the map first, then compiles. Making imports async would mean an async
evaluator, which is a rewrite rather than a binding change.
Options are named after dart-sass's JavaScript API wherever the two have the
same knob: style, syntax, loadPaths, charset, alertAscii and url,
plus files, logger, and quiet for the one knob dart-sass has no name for.
A failed compile throws an Error carrying message, formatted, file,
line and column.
The package is the web target: an ES module with an init() that fetches
the .wasm, for a browser or a bundler. To build it yourself instead:
wasm-pack build crates/lib --release --target web --out-name index -- \
--no-default-features --features wasm-exports,random
wasm-exports is not a default feature. Without it wasm-bindgen exports
nothing and the module contains no compiler at all.
docs/demo is a page built on it, compiling Bulma and
USWDS from source in the browser.
14,147 of 14,266 sass-spec tests pass against the pinned spec revision
b39c32768, leaving 111 failures (measured on macOS 2026-09-13; CI's Linux
runner reports two fewer passing). CI compiles Bulma,
Pico, Foundation and USWDS with both engines on every commit and fails on any
colour-value difference; all four currently compile byte-identically to
dart-sass.
| Job | Gates? | What it checks |
|---|---|---|
tests, fmt, clippy | yes | the crate's own suite, on the 1.96.1 MSRV |
frameworks | yes | the four-framework corpus, gated on colour values |
bootstrap | advisory | Bootstrap 5.0.2; prints the delta |
sass-spec | advisory | publishes the spec tallies |
What each release changed is in CHANGELOG.md. What is left
is in specs/docs/features/, one document
per work item, ranked by the spec tests it unlocks.
accent-sass is not a drop-in replacement for libsass and does not intend
to be.
| Feature | Default | Effect |
|---|---|---|
commandline | yes | build the binary, using clap |
random | yes | the builtin random([$limit]) and unique-id() |
macro | no | the accent_sass::include! macro, compiling Sass at build time |
nightly | no | lets include! use proc_macro::tracked_path |
wasm-exports | no | the JavaScript API for a wasm32-unknown-unknown browser build |
wasi-exports | no | a C ABI for embedding the wasm32-wasip1 module in a host |
Running cargo test should be all you need. The crate keeps a suite distinct
from sass-spec, following the same philosophy as
rust-analyzer,
so tests run without ruby and can be more granular than the official spec.
To run the official suite (node >= v14.14.0; does not work on Windows):
git clone https://github.com/zoosky/accent-sass --recursive
cd accent-sass && cargo build --release
cd sass-spec && npm install
npm run sass-spec -- --impl=dart-sass --command '../target/release/accent-sass' \
--trim-errors --ignore-warning-diffs --ignore-error-diffs
The leniency flags score CSS output and error messages only. Without them a
test that differs solely in a missing deprecation warning or in error wording
counts as a failure; that gap is sized in
specs/docs/features/08-calculation-warnings-and-error-wording.md.
Semantic Versioning. While the major
version is 0, a breaking change bumps the minor version. Version numbers are
this fork's own and do not track upstream grass.
The crates are on the Rust 2024 edition. The minimum supported Rust version is
1.96.1, normalised across the Accent crates; CI gates on it. Raising the MSRV
is a minor version bump.
accent-sass targets dart-sass version 1.104.1.
Hacker News (1)
Rust
100.0%
Sass → CSS in pure Rust, at parity with dart-sass. No Node, no libsass.
1
stars
2,125
commits
Rust
primary language
Sep 13, 2026
updated
Sass infrastructure for Rust, at parity with dart-sass.
accent-sass compiles Sass to CSS in
pure Rust, with no Node and no libsass. It is a fork of
connorskees/grass that carries the
modern Dart Sass features upstream has not released, so that a Rust program
can build real-world stylesheets -- Bulma, Pico, Foundation, USWDS -- without
shelling out to another toolchain.
Parity with dart-sass is the goal, not an aspiration to approximate it: a
deviation from the reference implementation is a bug, except in error messages
and error spans.
It is built for and maintained alongside Accent CMS, the single-binary markdown CMS, which compiles theme Sass in-process through this crate. It is a general-purpose library, and does not depend on Accent.
Documentation: https://zoosky.github.io/accent-sass -- a usage guide, a
reference, and a demo that
compiles Bulma and USWDS from source in your browser. The site is built from
docs/ with Accent CMS, which compiles its stylesheet with
this compiler.
accent-sass = "0.16.0"
To track work that has not been released yet, pin a git revision instead:
accent-sass = { git = "https://github.com/zoosky/accent-sass.git", rev = "<commit>" }
As a library:
fn main() -> Result<(), Box<accent_sass::Error>> {
let css = accent_sass::from_string(
"a { b { color: &; } }".to_owned(),
&accent_sass::Options::default(),
)?;
assert_eq!(css, "a b {\n color: a b;\n}\n");
Ok(())
}
The API is deliberately small: from_string, from_path,
from_string_with_file_name, and an Options builder. Options::fs takes any
Fs implementation, so a host that already holds its stylesheets can compile
them without writing them to disk first -- MemoryFs is one such
implementation, and is what the browser build resolves imports through.
As a binary, intended as a drop-in for the sass executable:
accent-sass input.scss # compile to stdout
accent-sass --check app.scss app.css # verify app.css is up to date
--check compiles and writes nothing, exiting 3 when the output file is
stale or missing and 1 when the stylesheet does not compile, so a CI job can
tell "your CSS is out of date" from "your Sass is broken".
As a browser package, published to npm as
@zoosky/accent-sass:
npm install @zoosky/accent-sass
import init, { compileString } from "@zoosky/accent-sass";
await init();
const { css, loadedUrls } = compileString('@use "theme";', {
files: {
"theme/_colors.scss": "$brand: #bada55 !default;",
"theme/_index.scss": '@forward "colors";',
},
style: "expanded",
logger: (event) => console.warn(event.message),
});
Every file a compile might touch must be in files before you call it.
Reads are synchronous: the compiler asks for a file and gets bytes back, with
nothing to await, so an importer cannot fetch, cannot await, and cannot
reach the File System Access API. An editor loads the theme's stylesheets into
the map first, then compiles. Making imports async would mean an async
evaluator, which is a rewrite rather than a binding change.
Options are named after dart-sass's JavaScript API wherever the two have the
same knob: style, syntax, loadPaths, charset, alertAscii and url,
plus files, logger, and quiet for the one knob dart-sass has no name for.
A failed compile throws an Error carrying message, formatted, file,
line and column.
The package is the web target: an ES module with an init() that fetches
the .wasm, for a browser or a bundler. To build it yourself instead:
wasm-pack build crates/lib --release --target web --out-name index -- \
--no-default-features --features wasm-exports,random
wasm-exports is not a default feature. Without it wasm-bindgen exports
nothing and the module contains no compiler at all.
docs/demo is a page built on it, compiling Bulma and
USWDS from source in the browser.
14,147 of 14,266 sass-spec tests pass against the pinned spec revision
b39c32768, leaving 111 failures (measured on macOS 2026-09-13; CI's Linux
runner reports two fewer passing). CI compiles Bulma,
Pico, Foundation and USWDS with both engines on every commit and fails on any
colour-value difference; all four currently compile byte-identically to
dart-sass.
| Job | Gates? | What it checks |
|---|---|---|
tests, fmt, clippy | yes | the crate's own suite, on the 1.96.1 MSRV |
frameworks | yes | the four-framework corpus, gated on colour values |
bootstrap | advisory | Bootstrap 5.0.2; prints the delta |
sass-spec | advisory | publishes the spec tallies |
What each release changed is in CHANGELOG.md. What is left
is in specs/docs/features/, one document
per work item, ranked by the spec tests it unlocks.
accent-sass is not a drop-in replacement for libsass and does not intend
to be.
| Feature | Default | Effect |
|---|---|---|
commandline | yes | build the binary, using clap |
random | yes | the builtin random([$limit]) and unique-id() |
macro | no | the accent_sass::include! macro, compiling Sass at build time |
nightly | no | lets include! use proc_macro::tracked_path |
wasm-exports | no | the JavaScript API for a wasm32-unknown-unknown browser build |
wasi-exports | no | a C ABI for embedding the wasm32-wasip1 module in a host |
Running cargo test should be all you need. The crate keeps a suite distinct
from sass-spec, following the same philosophy as
rust-analyzer,
so tests run without ruby and can be more granular than the official spec.
To run the official suite (node >= v14.14.0; does not work on Windows):
git clone https://github.com/zoosky/accent-sass --recursive
cd accent-sass && cargo build --release
cd sass-spec && npm install
npm run sass-spec -- --impl=dart-sass --command '../target/release/accent-sass' \
--trim-errors --ignore-warning-diffs --ignore-error-diffs
The leniency flags score CSS output and error messages only. Without them a
test that differs solely in a missing deprecation warning or in error wording
counts as a failure; that gap is sized in
specs/docs/features/08-calculation-warnings-and-error-wording.md.
Semantic Versioning. While the major
version is 0, a breaking change bumps the minor version. Version numbers are
this fork's own and do not track upstream grass.
The crates are on the Rust 2024 edition. The minimum supported Rust version is
1.96.1, normalised across the Accent crates; CI gates on it. Raising the MSRV
is a minor version bump.
accent-sass targets dart-sass version 1.104.1.
Hacker News (1)
Rust
100.0%