sasso-rails
Rails integration for sasso — a pure-Rust, zero-dependency SCSS/Sass → CSS compiler (a byte-for-byte dart-sass alternative).
Compiles your stylesheets into app/assets/builds/ for the Rails asset
pipeline to serve. Propshaft-first (the Rails 8 default) and
Sprockets-compatible.
Why this gem over the alternatives:
- No Node.js. Unlike
cssbundling-rails, there is nonode/yarn/npxrequirement — the compiler ships as a precompiled Ruby native gem. - In-process, no subprocess. Unlike
dartsass-rails/tailwindcss-rails(which shell out to a standalone binary), sasso-rails callsSasso.compiledirectly through the native extension — no process spawn, no IPC. - Pure Rust core. No libsass (deprecated), no Dart VM.
Installation
# Gemfile
gem "sasso-rails"
bundle install
bin/rails generate sasso:install
The installer scaffolds app/assets/stylesheets/application.scss, creates
app/assets/builds/ (with a .keep), links the builds directory in a Sprockets
manifest if one exists, and adds a watch process to Procfile.dev.
Reference the compiled CSS in your layout:
<%= stylesheet_link_tag "application" %>
Usage
bin/rails sasso:build # compile once
bin/rails sasso:watch # recompile on change (or run ./bin/dev)
bin/rails sasso:clobber # remove generated CSS
sasso:build runs automatically before assets:precompile, so deploys
(rails assets:precompile) regenerate the CSS before the pipeline fingerprints
it. No extra deploy wiring needed.
Configuration
In config/application.rb (or an environment file):
# Map each entrypoint (under source_dir) to an output file (under build_dir).
config.sasso.builds = {
"application.scss" => "application.css",
"admin.scss" => "admin.css",
}
# :expanded | :compressed. Default: :compressed in production, :expanded else.
config.sasso.style = :compressed
# Extra @use/@import include dirs. The entrypoint's own directory is always
# searched first, so sibling partials need no configuration.
config.sasso.load_paths = [Rails.root.join("vendor/styles").to_s]
# Defaults shown:
config.sasso.source_dir = "app/assets/stylesheets"
config.sasso.build_dir = "app/assets/builds"
How it fits the pipeline
sasso:build writes plain CSS to app/assets/builds/. Both pipelines treat
that directory as a source of ready-to-serve assets:
- Propshaft (Rails 8 default) discovers
app/assets/buildson its load path, fingerprints the CSS, and rewritesurl(...)references — it performs no Sass compilation itself. - Sprockets serves the built CSS as a static asset (the installer adds
//= link_tree ../buildstoapp/assets/config/manifest.js).
Notes & limitations
- Source maps are not supported yet. The in-process compiler returns CSS
only; there is no
.css.mapoutput in any environment. buildsis file-to-file. Each entry maps one input file to one output file. There is no directory-glob form (no"." => "."); list each entrypoint explicitly.- Load paths are explicit. Only the entrypoint's own directory (searched
automatically) and
config.sasso.load_pathsare on the@use/@importsearch path. Gem-vendored orconfig.assets.pathsstylesheets are NOT added automatically — list their directories inconfig.sasso.load_paths. - No double-minification. On a Sprockets app that sets
config.assets.css_compressor, sasso-rails stays:expandedand lets the pipeline compress (unless you pinconfig.sasso.style). Propshaft does not compress, so production output is:compressedthere. - Watch is a 1s mtime poll (dependency-free, no native fs-events); it covers
the source dir and
config.sasso.load_paths.
Versioning
This gem versions independently of the sasso compiler gem and pins it with a
range (sasso >= 0.1.1, < 1). An app may pin a specific compiler version in its
own Gemfile.
License
MIT, matching the Sass ecosystem. (The core sasso compiler crate remains
dual-licensed MIT OR Apache-2.0.)